Skip to content

Commit

Permalink
Merge pull request #97 from UBC-Solar/development
Browse files Browse the repository at this point in the history
Added assertions and unit tests for Cloud Cover
  • Loading branch information
chrischang5 authored Feb 8, 2022
2 parents 5a8a0b3 + 0be8709 commit a7c5aae
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 93 deletions.
44 changes: 30 additions & 14 deletions simulation/environment/SolarCalculations.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def calculate_hour_angle(self, time_zone_utc, day_of_year, local_time, longitude
"""

lst = helpers.local_time_to_apparent_solar_time(time_zone_utc / 3600, day_of_year,
local_time, longitude)
local_time, longitude)

hour_angle = 15 * (lst - 12)

Expand Down Expand Up @@ -134,11 +134,11 @@ def calculate_azimuth_angle(self, latitude, longitude, time_zone_utc, day_of_yea
local_time, longitude)

term_1 = np.sin(np.radians(declination_angle)) * \
np.sin(np.radians(latitude))
np.sin(np.radians(latitude))

term_2 = np.cos(np.radians(declination_angle)) * \
np.sin(np.radians(latitude)) * \
np.cos(np.radians(hour_angle))
np.sin(np.radians(latitude)) * \
np.cos(np.radians(hour_angle))

elevation_angle = self.calculate_elevation_angle(latitude, longitude,
time_zone_utc, day_of_year, local_time)
Expand Down Expand Up @@ -189,7 +189,7 @@ def calculate_DNI(self, latitude, longitude, time_zone_utc, day_of_year,
air_mass = np.float_(1) / np.float_(np.cos(np.radians(zenith_angle)))
with np.errstate(over="ignore"):
DNI = self.S_0 * ((1 - a * elevation * 0.001) * np.power(np.power(0.7, air_mass),
0.678) + a * elevation * 0.001)
0.678) + a * elevation * 0.001)
return np.where(zenith_angle > 90, 0, DNI)

def calculate_DHI(self, latitude, longitude, time_zone_utc, day_of_year,
Expand Down Expand Up @@ -227,9 +227,6 @@ def calculate_GHI(self, latitude, longitude, time_zone_utc, day_of_year,
on the Earth
https://www.pveducation.org/pvcdrom/properties-of-sunlight/calculation-of-solar-insolation
Cloud cover adjustment follows the equation laid out here:
http://www.shodor.org/os411/courses/_master/tools/calculators/solarrad/
latitude: The latitude of a location on Earth
longitude: The longitude of a location on Earth
time_zone_utc: The UTC time zone of your area in hours of UTC offset, without
Expand All @@ -239,11 +236,12 @@ def calculate_GHI(self, latitude, longitude, time_zone_utc, day_of_year,
being the first day of the year.
local_time: The local time in hours from midnight.
elevation: The local elevation of a location in metres
cloud_cover: A NumPy array representing cloud cover as a percentage from 0 to 100
note: If local time and time_zone_utc are both unadjusted for Daylight Savings, the
calculation will end up just the same
Returns: The Global Horizontal Irradiance in W/m2
Returns: The Global Horizontal Irradiance in W/m^2
"""

DHI = self.calculate_DHI(latitude, longitude, time_zone_utc, day_of_year,
Expand All @@ -255,15 +253,33 @@ def calculate_GHI(self, latitude, longitude, time_zone_utc, day_of_year,
zenith_angle = self.calculate_zenith_angle(latitude, longitude,
time_zone_utc, day_of_year, local_time)

cloud_cover = cloud_cover / 100
GHI = DNI * np.cos(np.radians(zenith_angle)) + DHI

# Convert cloud cover from percentage out of 100 to number between 0-1
cloud_cover /= 100
GHI = GHI * (1 - (0.75 * np.power(cloud_cover, 3.4)))
return self.apply_cloud_cover(GHI=GHI, cloud_cover=cloud_cover)

@staticmethod
def apply_cloud_cover(GHI, cloud_cover):
"""
Applies a cloud cover model to the GHI data.
Cloud cover adjustment follows the equation laid out here:
http://www.shodor.org/os411/courses/_master/tools/calculators/solarrad/
Args:
GHI: Global Horizontal Index in W/m^2
cloud_cover: A NumPy array representing cloud cover as a percentage from 0 to 100
Returns: GHI after considering cloud cover data
"""

assert np.logical_and(cloud_cover >= 0, cloud_cover <= 100).all()

scaled_cloud_cover = cloud_cover / 100

assert np.logical_and(scaled_cloud_cover >= 0, scaled_cloud_cover <= 1).all()

return GHI
return GHI * (1 - (0.75 * np.power(scaled_cloud_cover, 3.4)))

# ----- Calculation of modes of solar irradiance, but returning numpy arrays -----
@helpers.timeit
Expand Down
19 changes: 1 addition & 18 deletions simulation/environment/WeatherForecasts.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,21 +394,4 @@ def get_weather_advisory(weather_id):


if __name__ == "__main__":
google_api_key = ""

simulation_duration = 60 * 60 * 9

origin_coord = np.array([39.0918, -94.4172])

waypoints = np.array([[39.0379, -95.6764], [40.8838, -98.3734],
[41.8392, -103.7115], [42.8663, -106.3372], [42.8408, -108.7452],
[42.3224, -111.2973], [42.5840, -114.4703]])

dest_coord = np.array([43.6142, -116.2080])

gis = simulation.GIS(google_api_key, origin_coord, dest_coord, waypoints)
route_coords = gis.get_path()

weather_api_key = ""

weather = simulation.WeatherForecasts(weather_api_key, route_coords, simulation_duration)
pass
25 changes: 2 additions & 23 deletions tests/test_GIS.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,6 @@ def gis():

return location_system

#TODO, testing the route_visualization function with the additional arguments
# def test_route_visualization(gis):
# waypoints = np.array([[38.9253374, -95.678453], [38.921052, -95.674689],
# [38.9206115, -95.6784807], [38.9211163, -95.6777508],
# [38.9233953, -95.6783869]])
# #The waypoints can be changed to visualize another path
# helpers.route_visualization(waypoints, visible=False)


def test_calculate_closest_gis_indices(gis):
test_cumulative_distances = np.array([0, 9, 18, 19, 27, 35, 38, 47, 48, 56, 63])
Expand All @@ -58,10 +50,10 @@ def test_get_time_zones(gis):

test_coord_cumulative_distances = np.cumsum(test_coord)

test_coord_closest_gis_indices = gis.calculate_closest_gis_indices(cumulative_distances=test_coord_cumulative_distances)
test_coord_closest_gis_indices = gis.calculate_closest_gis_indices(
cumulative_distances=test_coord_cumulative_distances)
result = gis.get_time_zones(test_coord_closest_gis_indices)


assert len(test_coord) == len(expected_time_zone)
assert np.all(result == expected_time_zone)

Expand Down Expand Up @@ -144,19 +136,6 @@ def test_calculate_path_gradients2(gis):
result = helpers.calculate_path_gradients(test_elevations, test_distances)
assert np.all(result == expected_gradients)

# def test_calculate_path_elevations_FSGP(gis):
# start_coord = np.array([[38.9266274, -95.6781231]])
# end_coord = np.array([[38.9219577, -95.6776967]])
# waypoints = np.array([
# [38.9253374, -95.678453], [38.921052, -95.674689],
# [38.9206115, -95.6784807], [38.9211163, -95.6777508],
# [38.9233953, -95.6783869]])
# path = np.concatenate([start_coord, waypoints, end_coord])
# print(gis.calculate_path_elevations(path))
#
# # Expected results of elevations in meters calculated from: https://www.freemaptools.com/elevation-finder.htm
# expected_results = np.array(326 , 326, 322 , 326, 323, 328, 326)
# print(expected_results)

if __name__ == "__main__":
pass
38 changes: 0 additions & 38 deletions tests/test_basic.py

This file was deleted.

30 changes: 30 additions & 0 deletions tests/test_solar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import simulation
import numpy as np
import pytest


@pytest.fixture
def solar():
solar_calculations = simulation.environment.SolarCalculations()

return solar_calculations


def test_apply_cloud_cover1(solar):
test_cloud_cover = np.array([0, 0, 0])
test_GHI = np.array([1, 2, 3])

result_GHI = solar.apply_cloud_cover(GHI=test_GHI, cloud_cover=test_cloud_cover)

expected_GHI = np.array([1, 2, 3])
assert np.all(result_GHI == expected_GHI)


def test_apply_cloud_cover2(solar):
test_cloud_cover = np.array([100, 100, 100])
test_GHI = np.array([1, 2, 1])

result_GHI = solar.apply_cloud_cover(GHI=test_GHI, cloud_cover=test_cloud_cover)

expected_GHI = np.array([0.25, 0.5, 0.25])
assert np.all(result_GHI == expected_GHI)

0 comments on commit a7c5aae

Please sign in to comment.