-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Update irradiance.aoi to use more reliable formula #1191
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
Conversation
|
I don't think option 4 is feasible. There will always be some combination of inputs that produce a round off error. My only thought about using An alternative is to truncate In any case, I think the risk is small with the simple Or, use |
pvlib/irradiance.py
Outdated
| Input all angles in degrees. | ||
| .. versionchanged:: 0.9.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't know about this directive. Maybe we could be using it more often.
pvlib/irradiance.py
Outdated
| aoi = 2 * np.arctan(c / np.sqrt((1+(1+c))*((1-c)+1))) | ||
| aoi_value = np.rad2deg(aoi) | ||
|
|
||
| for arg in [surface_tilt, surface_azimuth, solar_zenith, solar_azimuth]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's OK to assume that these Series have a common index, and stop the loop when one index if found.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does the new implementation preserve Series in --> Series out without this code?
I'm fine with dropping the name assignment and letting users deal with that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does the new implementation preserve Series in --> Series out without this code?
Unfortunately it does not; the _spherical_to_cartesian function turns Series into 2-D numpy arrays. I don't immediately see a way around it that's cleaner than the "fix types at the end" idiom.
pvlib/irradiance.py
Outdated
| Input all angles in degrees. | ||
| .. warning:: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not be opposed to retiring the aoi_projection function. aoi could return cos(aoi) also, if that's of interest.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 on retiring aoi_projection, but let the few users that need cos(aoi) call it themselves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not a big deal, but several of the sky diffuse transposition models use aoi_projection(), so that deprecation will need some other minor changes too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-1 on retiring aoi_projection because I use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks @adriesse. I suggest we keep it and clip the output. Perhaps we should document the formula used by each function.
pvlib/irradiance.py
Outdated
| aoi = 2 * np.arctan(c / np.sqrt((1+(1+c))*((1-c)+1))) | ||
| aoi_value = np.rad2deg(aoi) | ||
|
|
||
| for arg in [surface_tilt, surface_azimuth, solar_zenith, solar_azimuth]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does the new implementation preserve Series in --> Series out without this code?
I'm fine with dropping the name assignment and letting users deal with that.
pvlib/irradiance.py
Outdated
| Input all angles in degrees. | ||
| .. warning:: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 on retiring aoi_projection, but let the few users that need cos(aoi) call it themselves.
|
Further to my request to keep the function, I searched my code base and I make relatively few uses of it, mainly in code where I did not want a dependency on my private toolbox. As to my usage patterns:
|
|
Should we merge this as is? Should we also add clip to the aoi projection function? Should we forget about the clever math and just use clip? I'm ok with any of these options. |
pvlib/irradiance.py
Outdated
| .. versionchanged:: 0.9.0 | ||
| Updated to a slower but more reliable formula that correctly handles | ||
| cases where the surface normal and solar position vectors are parallel. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method treats aforementioned vectors as sides of a triangle, a and b, each length one. The value c in the code below is the length of the third side.
pvlib/irradiance.py
Outdated
| aoi_value.name = 'aoi' | ||
| except AttributeError: | ||
| pass | ||
| aoi = 2 * np.arctan(c / np.sqrt((1+(1+c))*((1-c)+1))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think since a and b both equal 1, the sequencing of operations is not necessary, and I would suggest:
np.arctan2(c, np.sqrt((2.0 + c) * (2.0 - c)))
|
After finally following the links and looking at the reference, I must say I found these thoroughly interesting! However, I am not convinced that a triangle formed with two unit vectors runs as many risks of numerical issues as and arbitrary triangle with sides a, b and c, and forming the vectors in the first place requires substantially more imprecise trig function calls. Furthermore, this method produces the aoi directly, so for most purposes a cos() call must follow. I vote for a few well-placed clips to keep values sane. |
This PR started out with a single clip near the end of the |
|
No objection. |
|
No objection. It was nice to learn about this other formula. |
pvlib/tests/test_irradiance.py
Outdated
| zenith = 89.26778228223463 | ||
| azimuth = 60.932028605997004 | ||
| projection = irradiance.aoi_projection(zenith, azimuth, zenith, azimuth) | ||
| assert projection == 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know enough to intelligently comment on this, but I noticed that we're clipping to the int values -1 and 1. Is projection here also an int? I believe that if tested with realistic array input we'd always see floats returned. Might be worth testing that the clip behavior remains as expected in that case. Perhaps the important thing is that projection is always <= 1 and that np.isclose(projection, 1) returns True.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good suggestions. I also added a dtype check to make sure that clipping with int limits doesn't do something funky.
|
Lets merge |
Updates entries todocs/sphinx/source/api.rstfor API changes.docs/sphinx/source/whatsnewfor all changes. Includes link to the GitHub Issue with:issue:`num`or this Pull Request with:pull:`num`. Includes contributor name and/or GitHub username (link with:ghuser:`user`).New code is fully documented. Includes numpydoc compliant docstrings, examples, and comments where necessary.I decided the proposed option 2 in #1185 made the most sense, but happy to go another way if a better fix exists.