@@ -23,7 +23,7 @@ def test_solar_noon():
2323 gcr = 2.0 / 7.0 )
2424
2525 expect = pd .DataFrame ({'tracker_theta' : 0 , 'aoi' : 10 ,
26- 'surface_azimuth' : 90 , 'surface_tilt' : 0 },
26+ 'surface_azimuth' : 270 , 'surface_tilt' : 0 },
2727 index = index , dtype = np .float64 )
2828 expect = expect [SINGLEAXIS_COL_ORDER ]
2929
@@ -38,7 +38,7 @@ def test_scalars():
3838 max_angle = 90 , backtrack = True ,
3939 gcr = 2.0 / 7.0 )
4040 assert isinstance (tracker_data , dict )
41- expect = {'tracker_theta' : 0 , 'aoi' : 10 , 'surface_azimuth' : 90 ,
41+ expect = {'tracker_theta' : 0 , 'aoi' : 10 , 'surface_azimuth' : 270 ,
4242 'surface_tilt' : 0 }
4343 for k , v in expect .items ():
4444 assert np .isclose (tracker_data [k ], v )
@@ -52,7 +52,7 @@ def test_arrays():
5252 max_angle = 90 , backtrack = True ,
5353 gcr = 2.0 / 7.0 )
5454 assert isinstance (tracker_data , dict )
55- expect = {'tracker_theta' : 0 , 'aoi' : 10 , 'surface_azimuth' : 90 ,
55+ expect = {'tracker_theta' : 0 , 'aoi' : 10 , 'surface_azimuth' : 270 ,
5656 'surface_tilt' : 0 }
5757 for k , v in expect .items ():
5858 assert_allclose (tracker_data [k ], v , atol = 1e-7 )
@@ -68,7 +68,7 @@ def test_nans():
6868 gcr = 2.0 / 7.0 )
6969 expect = {'tracker_theta' : np .array ([0 , nan , nan ]),
7070 'aoi' : np .array ([10 , nan , nan ]),
71- 'surface_azimuth' : np .array ([90 , nan , nan ]),
71+ 'surface_azimuth' : np .array ([270 , nan , nan ]),
7272 'surface_tilt' : np .array ([0 , nan , nan ])}
7373 for k , v in expect .items ():
7474 assert_allclose (tracker_data [k ], v , atol = 1e-7 )
@@ -82,7 +82,7 @@ def test_nans():
8282 max_angle = 90 , backtrack = True ,
8383 gcr = 2.0 / 7.0 )
8484 expect = pd .DataFrame (np .array (
85- [[ 0. , 10. , 90 . , 0. ],
85+ [[ 0. , 10. , 270 . , 0. ],
8686 [nan , nan , nan , nan ],
8787 [nan , nan , nan , nan ]]),
8888 columns = ['tracker_theta' , 'aoi' , 'surface_azimuth' , 'surface_tilt' ])
@@ -195,6 +195,54 @@ def test_backtrack():
195195 assert_frame_equal (expect , tracker_data )
196196
197197
198+ def test__unit_normal ():
199+ # with scalar input
200+ unorm = tracking ._unit_normal (180. , 45. , 45. )
201+ assert_allclose (unorm , np .array ([[- np .sqrt (2 )/ 2 , - 0.5 , 0.5 ]]))
202+ # with vector input
203+ az = np .array ([0. , 90. , 180. , 270. ,
204+ 0. , 90. , 180. , 270. ,
205+ 180. , 180. , 180 , 180. ,
206+ 180. , 180. , 180. , 180 ,
207+ 0. , 90. , 180. , 270. ,
208+ ])
209+ tilt = np .array ([30. , 30. , 30. , 30. ,
210+ 0. , 0. , 0. , 0. ,
211+ - 30. , - 90. , 90. , 180. ,
212+ 0. , 0. , 0. , 0. ,
213+ 30. , 30. , 30. , 30 ,
214+ ])
215+ theta = np .array ([0. , 0. , 0. , 0. ,
216+ 0. , 0. , 0. , 0. ,
217+ 0. , 0. , 0. , 0. ,
218+ - 30. , 30. , - 90. , 90. ,
219+ 30. , 30. , 30. , 30. ,
220+ ])
221+ expected = np .array (
222+ [[ 0. , 0.5 , 0.8660254 ],
223+ [ 0.5 , 0. , 0.8660254 ],
224+ [ 0. , - 0.5 , 0.8660254 ],
225+ [- 0.5 , - 0. , 0.8660254 ],
226+ [ 0. , 0. , 1. ],
227+ [ 0. , 0. , 1. ],
228+ [ 0. , - 0. , 1. ],
229+ [- 0. , 0. , 1. ],
230+ [- 0. , 0.5 , 0.8660254 ],
231+ [- 0. , 1. , 0. ],
232+ [ 0. , - 1. , 0. ],
233+ [ 0. , - 0. , - 1. ],
234+ [ 0.5 , 0. , 0.8660254 ],
235+ [- 0.5 , - 0. , 0.8660254 ],
236+ [ 1. , 0. , 0. ],
237+ [- 1. , - 0. , 0. ],
238+ [ 0.5 , 0.4330127 , 0.75 ],
239+ [ 0.4330127 , - 0.5 , 0.75 ],
240+ [- 0.5 , - 0.4330127 , 0.75 ],
241+ [- 0.4330127 , 0.5 , 0.75 ]])
242+ unorms = tracking ._unit_normal (az , tilt , theta )
243+ assert np .allclose (unorms , expected )
244+
245+
198246def test_axis_tilt ():
199247 apparent_zenith = pd .Series ([30 ])
200248 apparent_azimuth = pd .Series ([135 ])
@@ -226,6 +274,7 @@ def test_axis_tilt():
226274
227275
228276def test_axis_azimuth ():
277+ # sun to the east, horizontal east-oriented tracker
229278 apparent_zenith = pd .Series ([30 ])
230279 apparent_azimuth = pd .Series ([90 ])
231280
@@ -234,13 +283,30 @@ def test_axis_azimuth():
234283 max_angle = 90 , backtrack = True ,
235284 gcr = 2.0 / 7.0 )
236285
237- expect = pd .DataFrame ({'aoi' : 30 , 'surface_azimuth' : 180 ,
286+ expect = pd .DataFrame ({'aoi' : 30 , 'surface_azimuth' : 0 ,
238287 'surface_tilt' : 0 , 'tracker_theta' : 0 },
239288 index = [0 ], dtype = np .float64 )
240289 expect = expect [SINGLEAXIS_COL_ORDER ]
241290
242291 assert_frame_equal (expect , tracker_data )
243292
293+ # sun to the east, horizontal south-oriented tracker
294+ apparent_zenith = pd .Series ([30 ])
295+ apparent_azimuth = pd .Series ([90 ])
296+
297+ tracker_data = tracking .singleaxis (apparent_zenith , apparent_azimuth ,
298+ axis_tilt = 0 , axis_azimuth = 180 ,
299+ max_angle = 90 , backtrack = True ,
300+ gcr = 2.0 / 7.0 )
301+
302+ expect = pd .DataFrame ({'aoi' : 0 , 'surface_azimuth' : 90 ,
303+ 'surface_tilt' : 30 , 'tracker_theta' : - 30 },
304+ index = [0 ], dtype = np .float64 )
305+ expect = expect [SINGLEAXIS_COL_ORDER ]
306+
307+ assert_frame_equal (expect , tracker_data )
308+
309+ # sun to the south, horizontal east-oriented tracker
244310 apparent_zenith = pd .Series ([30 ])
245311 apparent_azimuth = pd .Series ([180 ])
246312
@@ -269,7 +335,7 @@ def test_horizon_flat():
269335 axis_azimuth = 180 , backtrack = False , max_angle = 180 )
270336 expected = pd .DataFrame (np .array (
271337 [[ nan , nan , nan , nan ],
272- [ 0. , 45. , 270 . , 0. ],
338+ [ 0. , 45. , 90 . , 0. ],
273339 [ nan , nan , nan , nan ]]),
274340 columns = ['tracker_theta' , 'aoi' , 'surface_azimuth' , 'surface_tilt' ])
275341 assert_frame_equal (out , expected )
@@ -389,6 +455,23 @@ def test_slope_aware_backtracking():
389455 check_less_precise = True )
390456
391457
458+ def test_singleaxis_neg_axis_tilt ():
459+ ''' Check equivalence of (negative tilt, axis azimuth) and
460+ (positive tilt, axis azimuth + 180)
461+ '''
462+ params = dict (apparent_zenith = 45 , solar_azimuth = 270 )
463+
464+ tr_pos = pvlib .tracking .singleaxis (axis_tilt = 10 , axis_azimuth = 0 ,
465+ ** params )
466+ tr_neg = pvlib .tracking .singleaxis (axis_tilt = - 10 , axis_azimuth = 180 ,
467+ ** params )
468+
469+ tr_neg ['tracker_theta' ] *= - 1 # expect tracker_theta to be negated
470+
471+ for key in tr_pos :
472+ assert_allclose (tr_pos [key ], tr_neg [key ])
473+
474+
392475def test_singleaxis_aoi_gh1221 ():
393476 # vertical tracker
394477 loc = pvlib .location .Location (40.1134 , - 88.3695 )
@@ -408,7 +491,8 @@ def test_calc_surface_orientation_types():
408491 # numpy arrays
409492 rotations = np .array ([- 10 , 0 , 10 ])
410493 expected_tilts = np .array ([10 , 0 , 10 ], dtype = float )
411- expected_azimuths = np .array ([270 , 90 , 90 ], dtype = float )
494+ expected_azimuths = np .array ([270 , 270 , 90 ], dtype = float )
495+ # defaults to axis_azimuth=0
412496 out = tracking .calc_surface_orientation (tracker_theta = rotations )
413497 np .testing .assert_allclose (expected_tilts , out ['surface_tilt' ])
414498 np .testing .assert_allclose (expected_azimuths , out ['surface_azimuth' ])
@@ -445,7 +529,8 @@ def test_calc_surface_orientation_special():
445529 # special cases for rotations
446530 rotations = np .array ([- 180 , - 90 , - 0 , 0 , 90 , 180 ])
447531 expected_tilts = np .array ([180 , 90 , 0 , 0 , 90 , 180 ], dtype = float )
448- expected_azimuths = [270 , 270 , 90 , 90 , 90 , 90 ]
532+ expected_azimuths = [270 , 270 , 270 , 270 , 90 , 90 ]
533+ # defaults to axis_azimuth=0
449534 out = tracking .calc_surface_orientation (rotations )
450535 np .testing .assert_allclose (out ['surface_tilt' ], expected_tilts )
451536 np .testing .assert_allclose (out ['surface_azimuth' ], expected_azimuths )
@@ -454,14 +539,15 @@ def test_calc_surface_orientation_special():
454539 rotations = np .array ([- 10 , 0 , 10 ])
455540 expected_tilts = np .array ([90 , 90 , 90 ], dtype = float )
456541 expected_azimuths = np .array ([350 , 0 , 10 ], dtype = float )
542+ # defaults to axis_azimuth=0
457543 out = tracking .calc_surface_orientation (rotations , axis_tilt = 90 )
458544 np .testing .assert_allclose (out ['surface_tilt' ], expected_tilts )
459545 np .testing .assert_allclose (out ['surface_azimuth' ], expected_azimuths )
460546
461547 # special cases for axis_azimuth
462548 rotations = np .array ([- 10 , 0 , 10 ])
463549 expected_tilts = np .array ([10 , 0 , 10 ], dtype = float )
464- expected_azimuth_offsets = np .array ([- 90 , 90 , 90 ], dtype = float )
550+ expected_azimuth_offsets = np .array ([- 90 , - 90 , 90 ], dtype = float )
465551 for axis_azimuth in [0 , 90 , 180 , 270 , 360 ]:
466552 expected_azimuths = (axis_azimuth + expected_azimuth_offsets ) % 360
467553 out = tracking .calc_surface_orientation (rotations ,
0 commit comments