Skip to content

Commit 411d94c

Browse files
committed
Add test files
1 parent cb9d693 commit 411d94c

File tree

4 files changed

+457
-68
lines changed

4 files changed

+457
-68
lines changed

twoaxistracking/tests/test_layout.py

Lines changed: 80 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from twoaxistracking import layout
1+
from twoaxistracking import layout, twoaxistrackerfield
22
from shapely import geometry
33
import numpy as np
44
import pytest
@@ -8,132 +8,144 @@
88
def rectangular_geometry():
99
collector_geometry = geometry.box(-2, -1, 2, 1)
1010
total_collector_area = collector_geometry.area
11-
l_min = layout._calculate_l_min(collector_geometry)
12-
return collector_geometry, total_collector_area, l_min
11+
min_tracker_spacing = layout._calculate_min_tracker_spacing(collector_geometry)
12+
return collector_geometry, total_collector_area, min_tracker_spacing
1313

1414

1515
@pytest.fixture
1616
def square_field_layout():
1717
# Corresponds to GCR 0.125 with the rectangular_geometry
18-
X = np.array([-8., 0., 8., -8., 8., -8., 0., 8.])
19-
Y = np.array([-8., -8., -8., 0., 0., 8., 8., 8.])
18+
X = np.array([-8, 0, 8, -8, 8, -8, 0, 8])
19+
Y = np.array([-8, -8, -8, 0, 0, 8, 8, 8])
2020
tracker_distance = (X**2 + Y**2)**0.5
21-
relative_azimuth = np.array([225., 180., 135., 270., 90., 315., 0., 45.])
21+
relative_azimuth = np.array([225, 180, 135, 270, 90, 315, 0, 45])
2222
Z = np.zeros(8)
2323
relative_slope = np.zeros(8)
2424
return X, Y, Z, tracker_distance, relative_azimuth, relative_slope
2525

2626

27-
def test_l_min_rectangle(rectangular_geometry):
28-
# Test calculation of L_min for a rectangular collector
29-
l_min = layout._calculate_l_min(rectangular_geometry[0])
30-
assert l_min == np.sqrt(4**2+2**2)
27+
def test_min_tracker_spacing_rectangle(rectangular_geometry):
28+
# Test calculation of min_tracker_spacing for a rectangular collector
29+
min_tracker_spacing = layout._calculate_min_tracker_spacing(rectangular_geometry[0])
30+
assert min_tracker_spacing == np.sqrt(4**2+2**2)
3131

3232

33-
def test_l_min_circle():
34-
# Test calculation of L_min for a circular collector with radius 1
33+
def test_min_tracker_spacing_circle():
34+
# Test calculation of min_tracker_spacing for a circular collector with radius 1
3535
collector_geometry = geometry.Point(0, 0).buffer(1)
36-
l_min = layout._calculate_l_min(collector_geometry)
37-
assert l_min == 2
36+
min_tracker_spacing = layout._calculate_min_tracker_spacing(collector_geometry)
37+
assert min_tracker_spacing == 2
3838

3939

40-
def test_l_min_circle_offcenter():
41-
# Test calculation of L_min for a circular collector with radius 1 rotating
40+
def test_min_tracker_spacing_circle_offcenter():
41+
# Test calculation of min_tracker_spacing for a circular collector with radius 1 rotating
4242
# off-center around the point (0, 1)
4343
collector_geometry = geometry.Point(0, 1).buffer(1)
44-
l_min = layout._calculate_l_min(collector_geometry)
45-
assert l_min == 4
44+
min_tracker_spacing = layout._calculate_min_tracker_spacing(collector_geometry)
45+
assert min_tracker_spacing == 4
4646

4747

48-
def test_l_min_polygon():
49-
# Test calculation of L_min for a polygon
48+
def test_min_tracker_spacingpolygon():
49+
# Test calculation of min_tracker_spacing for a polygon
5050
collector_geometry = geometry.Polygon([(-1, -1), (3, 2), (4, 4), (1, 2), (-1, -1)])
51-
l_min = layout._calculate_l_min(collector_geometry)
52-
assert l_min == 2 * np.sqrt(4**2 + 4**2)
51+
min_tracker_spacing = layout._calculate_min_tracker_spacing(collector_geometry)
52+
assert min_tracker_spacing == 2 * np.sqrt(4**2 + 4**2)
5353

5454

55-
def test_square_layout_generation(rectangular_geometry):
56-
# Test square field layout defined using tje built-in layout types
57-
collector_geometry, total_collector_area, L_min = rectangular_geometry
55+
def test_square_layout_generation(rectangular_geometry, square_field_layout):
56+
# Test that a square field layout is returned correctly
57+
collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry
58+
X_exp, Y_exp, Z_exp, tracker_distance_exp, relative_azimuth_exp, relative_slope_exp = \
59+
square_field_layout
5860

5961
X, Y, Z, tracker_distance, relative_azimuth, relative_slope = \
6062
layout.generate_field_layout(
61-
gcr=0.25, total_collector_area=total_collector_area, L_min=L_min,
62-
neighbor_order=1, layout_type='square',
63-
slope_azimuth=0, slope_tilt=0, plot=False)
64-
assert np.isclose(X, np.array([-5.65685425, 0, 5.65685425, -5.65685425,
65-
5.65685425, -5.65685425, 0, 5.65685425])
66-
).all()
63+
gcr=0.125,
64+
total_collector_area=total_collector_area,
65+
min_tracker_spacing=min_tracker_spacing,
66+
neighbor_order=1,
67+
aspect_ratio=1,
68+
offset=0,
69+
rotation=0)
70+
assert (X == X_exp).all()
71+
assert (Y == Y_exp).all()
72+
assert (Z == Z_exp).all()
73+
assert (tracker_distance_exp == tracker_distance_exp).all()
74+
assert (relative_azimuth == relative_azimuth_exp).all()
75+
assert (relative_slope == relative_slope_exp).all()
6776

6877

6978
def test_layout_generation_value_error(rectangular_geometry):
7079
# Test if value errors are correctly raised
71-
collector_geometry, total_collector_area, L_min = rectangular_geometry
72-
73-
# Test if ValueError is raised when an incorrect layout_type is specified
74-
with pytest.raises(ValueError, match="layout type specified was not recognized"):
75-
_ = layout.generate_field_layout(
76-
gcr=0.25, total_collector_area=total_collector_area, L_min=L_min,
77-
neighbor_order=1, layout_type='this_is_not_a_layout_type')
78-
79-
# Test if ValueError is raised if too few layout parameters are specified
80-
with pytest.raises(ValueError, match="no layout type has not been selected"):
81-
_ = layout.generate_field_layout(
82-
gcr=0.25, total_collector_area=total_collector_area, L_min=L_min,
83-
neighbor_order=1, rotation=0)
80+
collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry
8481

8582
# Test if ValueError is raised if offset is out of range
8683
with pytest.raises(ValueError, match="offset is outside the valid range"):
8784
_ = layout.generate_field_layout(
88-
gcr=0.25, total_collector_area=total_collector_area, L_min=L_min,
89-
neighbor_order=1, rotation=0, offset=1.1, aspect_ratio=1)
85+
gcr=0.25, total_collector_area=total_collector_area,
86+
min_tracker_spacing=min_tracker_spacing, neighbor_order=1,
87+
aspect_ratio=1, offset=1.1, rotation=0)
9088

9189
# Test if ValueError is raised if aspect ratio is too low
9290
with pytest.raises(ValueError, match="Aspect ratio is too low"):
9391
_ = layout.generate_field_layout(
94-
gcr=0.25, total_collector_area=total_collector_area, L_min=L_min,
95-
neighbor_order=1, rotation=0, offset=0, aspect_ratio=0.6)
92+
gcr=0.25, total_collector_area=total_collector_area,
93+
min_tracker_spacing=min_tracker_spacing, neighbor_order=1,
94+
aspect_ratio=0.6, offset=0, rotation=0)
9695

9796
# Test if ValueError is raised if aspect ratio is too high
9897
with pytest.raises(ValueError, match="Aspect ratio is too high"):
9998
_ = layout.generate_field_layout(
100-
gcr=0.25, total_collector_area=total_collector_area, L_min=L_min,
101-
neighbor_order=1, rotation=0, offset=0, aspect_ratio=5)
99+
gcr=0.25, total_collector_area=total_collector_area,
100+
min_tracker_spacing=min_tracker_spacing, neighbor_order=1,
101+
aspect_ratio=5, offset=0, rotation=0)
102102

103-
# Test if ValueError is raised if rotation is outside valid range
103+
# Test if ValueError is raised if rotation is greater than 180 degrees
104104
with pytest.raises(ValueError, match="rotation is outside the valid range"):
105105
_ = layout.generate_field_layout(
106-
gcr=0.25, total_collector_area=total_collector_area, L_min=L_min,
107-
neighbor_order=1, rotation=190, offset=0, aspect_ratio=1.2)
106+
gcr=0.25, total_collector_area=total_collector_area,
107+
min_tracker_spacing=min_tracker_spacing, neighbor_order=1,
108+
aspect_ratio=1.2, offset=0, rotation=190)
108109

109-
# Test if ValueError is raised if L_min is outside valid range
110+
# Test if ValueError is raised if rotation is less than 0
111+
with pytest.raises(ValueError, match="rotation is outside the valid range"):
112+
_ = layout.generate_field_layout(
113+
gcr=0.5, total_collector_area=total_collector_area,
114+
min_tracker_spacing=min_tracker_spacing, neighbor_order=1,
115+
aspect_ratio=1, offset=0, rotation=-1)
116+
117+
# Test if ValueError is raised if min_tracker_spacing is outside valid range
110118
with pytest.raises(ValueError, match="Lmin is not physically possible"):
111119
_ = layout.generate_field_layout(
112-
gcr=0.25, total_collector_area=total_collector_area, L_min=1,
113-
neighbor_order=1, rotation=90, offset=0, aspect_ratio=1.2)
120+
gcr=0.25, total_collector_area=total_collector_area,
121+
min_tracker_spacing=1, neighbor_order=1, aspect_ratio=1.2,
122+
offset=0, rotation=90)
114123

115-
# Test if ValueError is raised if rotation is outside valid range
124+
# Test if ValueError is raised if maximum ground cover ratio is exceeded
116125
with pytest.raises(ValueError, match="Maximum ground cover ratio exceded"):
117126
_ = layout.generate_field_layout(
118-
gcr=0.5, total_collector_area=total_collector_area, L_min=L_min,
119-
neighbor_order=1, rotation=0, offset=0, aspect_ratio=1)
127+
gcr=0.5, total_collector_area=total_collector_area,
128+
min_tracker_spacing=min_tracker_spacing, neighbor_order=1,
129+
aspect_ratio=1, offset=0, rotation=0)
120130

121131

122-
def test_square_field_layout(rectangular_geometry, square_field_layout):
123-
# Test that a square field layout is returned correctly
124-
collector_geometry, total_collector_area, L_min = rectangular_geometry
125-
X_exp, Y_exp, Z_exp, tracker_distance_exp, relative_azimuth_exp, relative_slope_exp = \
126-
square_field_layout
132+
def test_field_slope():
133+
assert True
127134

135+
136+
def test_neighbor_order(rectangular_geometry):
137+
collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry
128138
X, Y, Z, tracker_distance, relative_azimuth, relative_slope = \
129139
layout.generate_field_layout(
130140
gcr=0.125,
131141
total_collector_area=total_collector_area,
132-
L_min=L_min,
133-
neighbor_order=1,
134-
layout_type='square')
142+
min_tracker_spacing=min_tracker_spacing,
143+
neighbor_order=3,
144+
aspect_ratio=1,
145+
offset=0,
146+
rotation=0)
147+
assert len(X) == (7*7-1)
135148

136-
# Test custom layout
137149
# Test slope
138150
# Test neighbor order
139151

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import matplotlib.pyplot as plt
2+
from twoaxistracking import plotting, layout, twoaxistrackerfield
3+
from shapely import geometry
4+
import numpy as np
5+
import pytest
6+
7+
8+
def assert_isinstance(obj, klass):
9+
assert isinstance(obj, klass), f'got {type(obj)}, expected {klass}'
10+
11+
12+
def test_field_layout_plot():
13+
X = np.array([-8, 0, 8, -8, 8, -8, 0, 8])
14+
Y = np.array([-8, -8, -8, 0, 0, 8, 8, 8])
15+
Z = np.array([0.1237, 0.0619, 0, 0.0619, -0.0619, 0, -0.0619, -0.1237])
16+
L_min = 4.4721
17+
result = plotting._plot_field_layout(X, Y, Z, L_min)
18+
assert_isinstance(result, plt.Figure)
19+
plt.close('all')
20+
21+
22+
@pytest.fixture
23+
def rectangular_geometry():
24+
collector_geometry = geometry.box(-2, -1, 2, 1)
25+
total_collector_area = collector_geometry.area
26+
min_tracker_spacing = layout._calculate_min_tracker_spacing(collector_geometry)
27+
return collector_geometry, total_collector_area, min_tracker_spacing
28+
29+
30+
def test_shading_plot(rectangular_geometry):
31+
collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry
32+
result = plotting._plot_shading(collector_geometry, collector_geometry,
33+
collector_geometry, min_tracker_spacing)
34+
assert_isinstance(result, plt.Figure)
35+
plt.close('all')
36+
37+
38+
def test_plotting_of_field_layout(rectangular_geometry):
39+
# Test if plot_field_layout returns a figure object
40+
collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry
41+
field = twoaxistrackerfield.TwoAxisTrackerField(
42+
total_collector_geometry=collector_geometry,
43+
active_collector_geometry=collector_geometry,
44+
neighbor_order=1,
45+
gcr=0.25,
46+
aspect_ratio=1,
47+
offset=0.45,
48+
rotation=30,
49+
)
50+
result = field.plot_field_layout()
51+
assert_isinstance(result, plt.Figure)
52+
plt.close('all')
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
from twoaxistracking import shading, layout
2+
from shapely import geometry
3+
import numpy as np
4+
import pytest
5+
6+
7+
@pytest.fixture
8+
def rectangular_geometry():
9+
collector_geometry = geometry.box(-2, -1, 2, 1)
10+
total_collector_area = collector_geometry.area
11+
min_tracker_spacing = layout._calculate_min_tracker_spacing(collector_geometry)
12+
return collector_geometry, total_collector_area, min_tracker_spacing
13+
14+
15+
@pytest.fixture
16+
def square_field_layout():
17+
# Corresponds to GCR 0.125 with the rectangular_geometry
18+
X = np.array([-8, 0, 8, -8, 8, -8, 0, 8])
19+
Y = np.array([-8, -8, -8, 0, 0, 8, 8, 8])
20+
tracker_distance = (X**2 + Y**2)**0.5
21+
relative_azimuth = np.array([225, 180, 135, 270, 90, 315, 0, 45])
22+
Z = np.zeros(8)
23+
relative_slope = np.zeros(8)
24+
return X, Y, Z, tracker_distance, relative_azimuth, relative_slope
25+
26+
27+
def test_shading(rectangular_geometry, square_field_layout):
28+
# Test shading when geometries completly overlap
29+
# Also plots the geometry (ensures no errors occurs)
30+
collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry
31+
X, Y, Z, tracker_distance, relative_azimuth, relative_slope = \
32+
square_field_layout
33+
shaded_fraction = shading.shaded_fraction(
34+
solar_elevation=3,
35+
solar_azimuth=120,
36+
total_collector_geometry=collector_geometry,
37+
active_collector_geometry=collector_geometry,
38+
min_tracker_spacing=min_tracker_spacing,
39+
tracker_distance=tracker_distance,
40+
relative_azimuth=relative_azimuth,
41+
relative_slope=relative_slope,
42+
slope_azimuth=0,
43+
slope_tilt=0,
44+
plot=True)
45+
assert np.isclose(shaded_fraction, 0.191324)
46+
47+
48+
def test_shading_zero_solar_elevation(rectangular_geometry, square_field_layout):
49+
collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry
50+
X, Y, Z, tracker_distance, relative_azimuth, relative_slope = \
51+
square_field_layout
52+
# Test shading when geometries completly overlap
53+
shaded_fraction = shading.shaded_fraction(
54+
solar_elevation=0,
55+
solar_azimuth=180,
56+
total_collector_geometry=collector_geometry,
57+
active_collector_geometry=collector_geometry,
58+
min_tracker_spacing=min_tracker_spacing,
59+
tracker_distance=tracker_distance,
60+
relative_azimuth=relative_azimuth,
61+
relative_slope=relative_slope,
62+
slope_azimuth=0,
63+
slope_tilt=0,
64+
plot=False)
65+
assert shaded_fraction == 1
66+
67+
68+
def test_no_shading(rectangular_geometry, square_field_layout):
69+
# Test shading calculation when there is no shading (high solar elevation)
70+
collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry
71+
X, Y, Z, tracker_distance, relative_azimuth, relative_slope = \
72+
square_field_layout
73+
shaded_fraction = shading.shaded_fraction(
74+
solar_elevation=45,
75+
solar_azimuth=180,
76+
total_collector_geometry=collector_geometry,
77+
active_collector_geometry=collector_geometry,
78+
min_tracker_spacing=min_tracker_spacing,
79+
tracker_distance=tracker_distance,
80+
relative_azimuth=relative_azimuth,
81+
relative_slope=relative_slope,
82+
slope_azimuth=0,
83+
slope_tilt=0,
84+
plot=False)
85+
assert shaded_fraction == 0
86+
87+
88+
def test_shading_below_horizon(rectangular_geometry, square_field_layout):
89+
# Test shading calculation when sun is below the horizon (elevation<0)
90+
collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry
91+
X, Y, Z, tracker_distance, relative_azimuth, relative_slope = \
92+
square_field_layout
93+
shaded_fraction = shading.shaded_fraction(
94+
solar_elevation=-5.1,
95+
solar_azimuth=180,
96+
total_collector_geometry=collector_geometry,
97+
active_collector_geometry=collector_geometry,
98+
min_tracker_spacing=min_tracker_spacing,
99+
tracker_distance=tracker_distance,
100+
relative_azimuth=relative_azimuth,
101+
relative_slope=relative_slope,
102+
slope_azimuth=0,
103+
slope_tilt=0,
104+
plot=False)
105+
assert np.isnan(shaded_fraction)
106+
107+
108+
def test_shading_below_hill_horizon(rectangular_geometry, square_field_layout):
109+
# Test shading calculation when there is no shading (high solar elevation)
110+
collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry
111+
X, Y, Z, tracker_distance, relative_azimuth, relative_slope = \
112+
square_field_layout
113+
shaded_fraction = shading.shaded_fraction(
114+
solar_elevation=9,
115+
solar_azimuth=180,
116+
total_collector_geometry=collector_geometry,
117+
active_collector_geometry=collector_geometry,
118+
min_tracker_spacing=min_tracker_spacing,
119+
tracker_distance=tracker_distance,
120+
relative_azimuth=relative_azimuth,
121+
relative_slope=relative_slope,
122+
slope_azimuth=0,
123+
slope_tilt=10,
124+
plot=False)
125+
assert shaded_fraction == 1

0 commit comments

Comments
 (0)