-
Notifications
You must be signed in to change notification settings - Fork 6
Add tests #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Add tests #16
Changes from 39 commits
Commits
Show all changes
50 commits
Select commit
Hold shift + click to select a range
7916426
Create twoaxistrackerfield.py
AdamRJensen 396e297
Add TwoAxisTrackerField to __init__.py and documentation.rst
AdamRJensen 97bf622
Add _calculate_l_min to layout
AdamRJensen 1561434
Fix linter error
AdamRJensen 5e4cbf6
Update twoaxistrackerfield.py
AdamRJensen b63e676
Reorder layout ValueError warnings
AdamRJensen 9ee9bd9
Fix typo
AdamRJensen de7b7d7
Remove layout_type from generate_field_layout function
AdamRJensen 31f45a2
Add layout_type code to TwoAxisTrackerField
AdamRJensen 156d98f
L_min -> min_tracker_spacing
AdamRJensen 90893d1
Add whatsnew entry
AdamRJensen f5db9d1
Fix linting errors
AdamRJensen 6fcbc4c
Update twoaxistrackerfield.py
AdamRJensen 167078f
Correct horizon shading
AdamRJensen dd40bcd
Update twoaxistracking/twoaxistrackerfield.py
AdamRJensen d898205
Update twoaxistracking/twoaxistrackerfield.py
AdamRJensen bc93293
Update twoaxistracking/twoaxistrackerfield.py
AdamRJensen 6fd5717
Return scalar when scalar is passed
AdamRJensen 5177ff9
Correct use of np.isscalar
AdamRJensen 4e36d1d
Update documentation
AdamRJensen 77a5abc
Update whatsnew
AdamRJensen 9210ac3
Rework intro_tutorial.ipynb
AdamRJensen 82116a7
Update README.md
AdamRJensen 86cf947
Create test_layout.py
AdamRJensen 8f5098e
Update test_layout.py
AdamRJensen 2f0ef34
Use test folder in package folder
AdamRJensen 49e3ac7
Delete test_placeholder.py
AdamRJensen 18c6549
Add test files
AdamRJensen 057e3aa
Fix linting errors
AdamRJensen 901111a
Update scalar test
AdamRJensen 3e49947
Add pandas to test in setup.cfg
AdamRJensen bcf5908
Revert "package metadata and doc build updates (#17)"
AdamRJensen 147ba2a
Undo commits
AdamRJensen a87ee3e
Merge branch 'tests' of https://github.com/pvlib/twoaxistracking into…
AdamRJensen e9cc008
Revert "Undo commits"
AdamRJensen e4d2d82
Add test files
AdamRJensen 0cdfffc
Switch to using np.testing.assert_allclose
AdamRJensen 13c7bd5
Add test for sloped field layout
AdamRJensen 4a75240
Move test_twoaxistracker to twoaxistracker PR
AdamRJensen a9f368a
Add conftest.py to avoid duplicate fixtures
AdamRJensen 2a44791
Merge branch 'main' into tests
AdamRJensen c517f4e
Remove imports of fixtures from conftest
AdamRJensen 37ee22f
Remove unnecessary imports
AdamRJensen eda911a
Increase test tolerance for test_field_slope
AdamRJensen 6d6a391
Add multi-polygon active area test
AdamRJensen e4ea335
Add empty __init__.py
AdamRJensen 4c84e5d
Use "from .conftest" as Kevin said..
AdamRJensen b3d2496
Base square_field_layout_slope fixture off square_field_layout
AdamRJensen 7352085
Fix linter error
AdamRJensen 39c94e6
Remove backslash in conftest.py
AdamRJensen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,7 @@ install_requires = | |
| test = | ||
| pytest | ||
| pytest-cov | ||
| pandas | ||
| doc = | ||
| sphinx==4.4.0 | ||
| myst-nb==0.13.2 | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,181 @@ | ||
| from twoaxistracking import layout | ||
| from shapely import geometry | ||
| import numpy as np | ||
| import pytest | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def rectangular_geometry(): | ||
| collector_geometry = geometry.box(-2, -1, 2, 1) | ||
| total_collector_area = collector_geometry.area | ||
| min_tracker_spacing = layout._calculate_min_tracker_spacing(collector_geometry) | ||
| return collector_geometry, total_collector_area, min_tracker_spacing | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def square_field_layout(): | ||
| # Corresponds to GCR 0.125 with the rectangular_geometry | ||
| X = np.array([-8, 0, 8, -8, 8, -8, 0, 8]) | ||
| Y = np.array([-8, -8, -8, 0, 0, 8, 8, 8]) | ||
| tracker_distance = (X**2 + Y**2)**0.5 | ||
| relative_azimuth = np.array([225, 180, 135, 270, 90, 315, 0, 45]) | ||
| Z = np.zeros(8) | ||
| relative_slope = np.zeros(8) | ||
| return X, Y, Z, tracker_distance, relative_azimuth, relative_slope | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def square_field_layout_sloped(): | ||
| # Corresponds to GCR 0.125 with the rectangular_geometry | ||
| X = np.array([-8, 0, 8, -8, 8, -8, 0, 8]) | ||
| Y = np.array([-8, -8, -8, 0, 0, 8, 8, 8]) | ||
| tracker_distance = (X**2 + Y**2)**0.5 | ||
| relative_azimuth = np.array([225, 180, 135, 270, 90, 315, 0, 45]) | ||
| Z = np.array([0.12372765, 0.06186383, 0, 0.06186383, | ||
| -0.06186383, 0, -0.06186383, -0.12372765]) | ||
| relative_slope = np.array([5, 3.53553391, 0, 3.53553391, | ||
| -3.53553391, 0, -3.53553391, -5]) | ||
| return X, Y, Z, tracker_distance, relative_azimuth, relative_slope | ||
|
|
||
|
|
||
| def test_min_tracker_spacing_rectangle(rectangular_geometry): | ||
| # Test calculation of min_tracker_spacing for a rectangular collector | ||
| min_tracker_spacing = layout._calculate_min_tracker_spacing(rectangular_geometry[0]) | ||
| np.testing.assert_allclose(min_tracker_spacing, np.sqrt(4**2+2**2)) | ||
|
|
||
|
|
||
| def test_min_tracker_spacing_circle(): | ||
| # Test calculation of min_tracker_spacing for a circular collector with radius 1 | ||
| collector_geometry = geometry.Point(0, 0).buffer(1) | ||
| min_tracker_spacing = layout._calculate_min_tracker_spacing(collector_geometry) | ||
| assert min_tracker_spacing == 2 | ||
|
|
||
|
|
||
| def test_min_tracker_spacing_circle_offcenter(): | ||
| # Test calculation of min_tracker_spacing for a circular collector with radius 1 rotating | ||
| # off-center around the point (0, 1) | ||
| collector_geometry = geometry.Point(0, 1).buffer(1) | ||
| min_tracker_spacing = layout._calculate_min_tracker_spacing(collector_geometry) | ||
| assert min_tracker_spacing == 4 | ||
|
|
||
|
|
||
| def test_min_tracker_spacingpolygon(): | ||
| # Test calculation of min_tracker_spacing for a polygon | ||
| collector_geometry = geometry.Polygon([(-1, -1), (3, 2), (4, 4), (1, 2), (-1, -1)]) | ||
| min_tracker_spacing = layout._calculate_min_tracker_spacing(collector_geometry) | ||
| np.testing.assert_allclose(min_tracker_spacing, 2 * np.sqrt(4**2 + 4**2)) | ||
|
|
||
|
|
||
| def test_square_layout_generation(rectangular_geometry, square_field_layout): | ||
| # Test that a square field layout is returned correctly | ||
| collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry | ||
| X_exp, Y_exp, Z_exp, tracker_distance_exp, relative_azimuth_exp, relative_slope_exp = \ | ||
| square_field_layout | ||
|
|
||
| X, Y, Z, tracker_distance, relative_azimuth, relative_slope = \ | ||
| layout.generate_field_layout( | ||
| gcr=0.125, | ||
| total_collector_area=total_collector_area, | ||
| min_tracker_spacing=min_tracker_spacing, | ||
| neighbor_order=1, | ||
| aspect_ratio=1, | ||
| offset=0, | ||
| rotation=0) | ||
| np.testing.assert_allclose(X, X_exp) | ||
| np.testing.assert_allclose(Y, Y_exp) | ||
| np.testing.assert_allclose(Z, Z_exp) | ||
| np.testing.assert_allclose(tracker_distance_exp, tracker_distance_exp) | ||
| np.testing.assert_allclose(relative_azimuth, relative_azimuth_exp) | ||
| np.testing.assert_allclose(relative_slope, relative_slope_exp) | ||
|
|
||
|
|
||
| def test_field_slope(rectangular_geometry, square_field_layout_sloped): | ||
| # Test that a square field layout on tilted surface is returned correctly | ||
| collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry | ||
| X_exp, Y_exp, Z_exp, tracker_distance_exp, relative_azimuth_exp, relative_slope_exp = \ | ||
| square_field_layout_sloped | ||
| X, Y, Z, tracker_distance, relative_azimuth, relative_slope = \ | ||
| layout.generate_field_layout( | ||
| gcr=0.125, | ||
| total_collector_area=total_collector_area, | ||
| min_tracker_spacing=min_tracker_spacing, | ||
| neighbor_order=1, | ||
| aspect_ratio=1, | ||
| offset=0, | ||
| rotation=0, | ||
| slope_azimuth=45, | ||
| slope_tilt=5) | ||
| np.testing.assert_allclose(X, X_exp) | ||
| np.testing.assert_allclose(Y, Y_exp) | ||
| np.testing.assert_allclose(Z, Z_exp) | ||
| np.testing.assert_allclose(tracker_distance_exp, tracker_distance_exp) | ||
| np.testing.assert_allclose(relative_azimuth, relative_azimuth_exp) | ||
| np.testing.assert_allclose(relative_slope, relative_slope_exp, atol=10**-9) | ||
|
|
||
|
|
||
| def test_layout_generation_value_error(rectangular_geometry): | ||
| # Test if value errors are correctly raised | ||
| collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry | ||
|
|
||
| # Test if ValueError is raised if offset is out of range | ||
| with pytest.raises(ValueError, match="offset is outside the valid range"): | ||
| _ = layout.generate_field_layout( | ||
| gcr=0.25, total_collector_area=total_collector_area, | ||
| min_tracker_spacing=min_tracker_spacing, neighbor_order=1, | ||
| aspect_ratio=1, offset=1.1, rotation=0) | ||
|
|
||
| # Test if ValueError is raised if aspect ratio is too low | ||
| with pytest.raises(ValueError, match="Aspect ratio is too low"): | ||
| _ = layout.generate_field_layout( | ||
| gcr=0.25, total_collector_area=total_collector_area, | ||
| min_tracker_spacing=min_tracker_spacing, neighbor_order=1, | ||
| aspect_ratio=0.6, offset=0, rotation=0) | ||
|
|
||
| # Test if ValueError is raised if aspect ratio is too high | ||
| with pytest.raises(ValueError, match="Aspect ratio is too high"): | ||
| _ = layout.generate_field_layout( | ||
| gcr=0.25, total_collector_area=total_collector_area, | ||
| min_tracker_spacing=min_tracker_spacing, neighbor_order=1, | ||
| aspect_ratio=5, offset=0, rotation=0) | ||
|
|
||
| # Test if ValueError is raised if rotation is greater than 180 degrees | ||
| with pytest.raises(ValueError, match="rotation is outside the valid range"): | ||
| _ = layout.generate_field_layout( | ||
| gcr=0.25, total_collector_area=total_collector_area, | ||
| min_tracker_spacing=min_tracker_spacing, neighbor_order=1, | ||
| aspect_ratio=1.2, offset=0, rotation=190) | ||
|
|
||
| # Test if ValueError is raised if rotation is less than 0 | ||
| with pytest.raises(ValueError, match="rotation is outside the valid range"): | ||
| _ = layout.generate_field_layout( | ||
| gcr=0.5, total_collector_area=total_collector_area, | ||
| min_tracker_spacing=min_tracker_spacing, neighbor_order=1, | ||
| aspect_ratio=1, offset=0, rotation=-1) | ||
|
|
||
| # Test if ValueError is raised if min_tracker_spacing is outside valid range | ||
| with pytest.raises(ValueError, match="Lmin is not physically possible"): | ||
| _ = layout.generate_field_layout( | ||
| gcr=0.25, total_collector_area=total_collector_area, | ||
| min_tracker_spacing=1, neighbor_order=1, aspect_ratio=1.2, | ||
| offset=0, rotation=90) | ||
|
|
||
| # Test if ValueError is raised if maximum ground cover ratio is exceeded | ||
| with pytest.raises(ValueError, match="Maximum ground cover ratio exceded"): | ||
| _ = layout.generate_field_layout( | ||
| gcr=0.5, total_collector_area=total_collector_area, | ||
| min_tracker_spacing=min_tracker_spacing, neighbor_order=1, | ||
| aspect_ratio=1, offset=0, rotation=0) | ||
|
|
||
|
|
||
| def test_neighbor_order(rectangular_geometry): | ||
| collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry | ||
| X, Y, Z, tracker_distance, relative_azimuth, relative_slope = \ | ||
| layout.generate_field_layout( | ||
| gcr=0.125, | ||
| total_collector_area=total_collector_area, | ||
| min_tracker_spacing=min_tracker_spacing, | ||
| neighbor_order=3, | ||
| aspect_ratio=1, | ||
| offset=0, | ||
| rotation=0) | ||
| assert len(X) == (7*7-1) | ||
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import matplotlib.pyplot as plt | ||
| from twoaxistracking import plotting, layout, twoaxistrackerfield | ||
| from shapely import geometry | ||
| import numpy as np | ||
| import pytest | ||
|
|
||
|
|
||
| def assert_isinstance(obj, klass): | ||
| assert isinstance(obj, klass), f'got {type(obj)}, expected {klass}' | ||
|
|
||
|
|
||
| def test_field_layout_plot(): | ||
| X = np.array([-8, 0, 8, -8, 8, -8, 0, 8]) | ||
| Y = np.array([-8, -8, -8, 0, 0, 8, 8, 8]) | ||
| Z = np.array([0.1237, 0.0619, 0, 0.0619, -0.0619, 0, -0.0619, -0.1237]) | ||
| L_min = 4.4721 | ||
| result = plotting._plot_field_layout(X, Y, Z, L_min) | ||
| assert_isinstance(result, plt.Figure) | ||
| plt.close('all') | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def rectangular_geometry(): | ||
| collector_geometry = geometry.box(-2, -1, 2, 1) | ||
| total_collector_area = collector_geometry.area | ||
| min_tracker_spacing = layout._calculate_min_tracker_spacing(collector_geometry) | ||
| return collector_geometry, total_collector_area, min_tracker_spacing | ||
|
|
||
|
|
||
| def test_shading_plot(rectangular_geometry): | ||
| collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry | ||
| result = plotting._plot_shading(collector_geometry, collector_geometry, | ||
| collector_geometry, min_tracker_spacing) | ||
| assert_isinstance(result, plt.Figure) | ||
| plt.close('all') | ||
|
|
||
|
|
||
| def test_plotting_of_field_layout(rectangular_geometry): | ||
| # Test if plot_field_layout returns a figure object | ||
| collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry | ||
| field = twoaxistrackerfield.TwoAxisTrackerField( | ||
| total_collector_geometry=collector_geometry, | ||
| active_collector_geometry=collector_geometry, | ||
| neighbor_order=1, | ||
| gcr=0.25, | ||
| aspect_ratio=1, | ||
| offset=0.45, | ||
| rotation=30, | ||
| ) | ||
| result = field.plot_field_layout() | ||
| assert_isinstance(result, plt.Figure) | ||
| plt.close('all') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| from twoaxistracking import shading, layout | ||
| from shapely import geometry | ||
| import numpy as np | ||
| import pytest | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def rectangular_geometry(): | ||
kandersolar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| collector_geometry = geometry.box(-2, -1, 2, 1) | ||
| total_collector_area = collector_geometry.area | ||
| min_tracker_spacing = layout._calculate_min_tracker_spacing(collector_geometry) | ||
| return collector_geometry, total_collector_area, min_tracker_spacing | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def square_field_layout(): | ||
| # Corresponds to GCR 0.125 with the rectangular_geometry | ||
| X = np.array([-8, 0, 8, -8, 8, -8, 0, 8]) | ||
| Y = np.array([-8, -8, -8, 0, 0, 8, 8, 8]) | ||
| tracker_distance = (X**2 + Y**2)**0.5 | ||
| relative_azimuth = np.array([225, 180, 135, 270, 90, 315, 0, 45]) | ||
| Z = np.zeros(8) | ||
| relative_slope = np.zeros(8) | ||
| return X, Y, Z, tracker_distance, relative_azimuth, relative_slope | ||
|
|
||
|
|
||
| def test_shading(rectangular_geometry, square_field_layout): | ||
| # Test shading calculation | ||
| # Also plots the geometry (ensures no errors are raised) | ||
| collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry | ||
| X, Y, Z, tracker_distance, relative_azimuth, relative_slope = \ | ||
| square_field_layout | ||
| shaded_fraction = shading.shaded_fraction( | ||
| solar_elevation=3, | ||
| solar_azimuth=120, | ||
| total_collector_geometry=collector_geometry, | ||
| active_collector_geometry=collector_geometry, | ||
| min_tracker_spacing=min_tracker_spacing, | ||
| tracker_distance=tracker_distance, | ||
| relative_azimuth=relative_azimuth, | ||
| relative_slope=relative_slope, | ||
| slope_azimuth=0, | ||
| slope_tilt=0, | ||
| plot=True) | ||
| np.testing.assert_allclose(shaded_fraction, 0.191324034) | ||
|
|
||
|
|
||
| def test_shading_zero_solar_elevation(rectangular_geometry, square_field_layout): | ||
| # Test shading when geometries completely overlap | ||
| collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry | ||
| X, Y, Z, tracker_distance, relative_azimuth, relative_slope = \ | ||
| square_field_layout | ||
| shaded_fraction = shading.shaded_fraction( | ||
| solar_elevation=0, | ||
| solar_azimuth=180, | ||
| total_collector_geometry=collector_geometry, | ||
| active_collector_geometry=collector_geometry, | ||
| min_tracker_spacing=min_tracker_spacing, | ||
| tracker_distance=tracker_distance, | ||
| relative_azimuth=relative_azimuth, | ||
| relative_slope=relative_slope, | ||
| slope_azimuth=0, | ||
| slope_tilt=0, | ||
| plot=False) | ||
| assert shaded_fraction == 1 | ||
|
|
||
|
|
||
| def test_no_shading(rectangular_geometry, square_field_layout): | ||
| # Test shading calculation when there is no shading (high solar elevation) | ||
| collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry | ||
| X, Y, Z, tracker_distance, relative_azimuth, relative_slope = \ | ||
| square_field_layout | ||
| shaded_fraction = shading.shaded_fraction( | ||
| solar_elevation=45, | ||
| solar_azimuth=180, | ||
| total_collector_geometry=collector_geometry, | ||
| active_collector_geometry=collector_geometry, | ||
| min_tracker_spacing=min_tracker_spacing, | ||
| tracker_distance=tracker_distance, | ||
| relative_azimuth=relative_azimuth, | ||
| relative_slope=relative_slope, | ||
| slope_azimuth=0, | ||
| slope_tilt=0, | ||
| plot=False) | ||
| assert shaded_fraction == 0 | ||
|
|
||
|
|
||
| def test_shading_below_horizon(rectangular_geometry, square_field_layout): | ||
| # Test shading calculation when sun is below the horizon (elevation<0) | ||
| collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry | ||
| X, Y, Z, tracker_distance, relative_azimuth, relative_slope = \ | ||
| square_field_layout | ||
| shaded_fraction = shading.shaded_fraction( | ||
| solar_elevation=-5.1, | ||
| solar_azimuth=180, | ||
| total_collector_geometry=collector_geometry, | ||
| active_collector_geometry=collector_geometry, | ||
| min_tracker_spacing=min_tracker_spacing, | ||
| tracker_distance=tracker_distance, | ||
| relative_azimuth=relative_azimuth, | ||
| relative_slope=relative_slope, | ||
| slope_azimuth=0, | ||
| slope_tilt=0, | ||
| plot=False) | ||
| assert np.isnan(shaded_fraction) | ||
|
|
||
|
|
||
| def test_shading_below_hill_horizon(rectangular_geometry, square_field_layout): | ||
| # Test shading when sun is below horizon line caused by sloped surface | ||
| collector_geometry, total_collector_area, min_tracker_spacing = rectangular_geometry | ||
| X, Y, Z, tracker_distance, relative_azimuth, relative_slope = \ | ||
| square_field_layout | ||
| shaded_fraction = shading.shaded_fraction( | ||
| solar_elevation=9, | ||
| solar_azimuth=180, | ||
| total_collector_geometry=collector_geometry, | ||
| active_collector_geometry=collector_geometry, | ||
| min_tracker_spacing=min_tracker_spacing, | ||
| tracker_distance=tracker_distance, | ||
| relative_azimuth=relative_azimuth, | ||
| relative_slope=relative_slope, | ||
| slope_azimuth=0, | ||
| slope_tilt=10, | ||
| plot=False) | ||
| assert shaded_fraction == 1 | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.