Skip to content

Implement enhanced Faiman temperature model #1595

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 16 commits into from
Dec 12, 2022
Merged

Implement enhanced Faiman temperature model #1595

merged 16 commits into from
Dec 12, 2022

Conversation

adriesse
Copy link
Member

@adriesse adriesse commented Nov 17, 2022

@adriesse
Copy link
Member Author

adriesse commented Nov 17, 2022

Early comments welcome, as always! @jsstein @mtheristis

@adriesse
Copy link
Member Author

Ready for review. I'll do the sphinx stuff when it is clear which release this goes into. (The upcoming one I hope.)

Copy link
Member

@cwhanse cwhanse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have a test that checks the output of this function matches the output of faiman, for default parameters.

:math:`\left[ \frac{\text{W}/\text{m}^2}{\text{C}\ \left( \text{m/s} \right)} \right]`

sky_view : numeric, default 1.0
Effective view factor limiting the radiative exchange between the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is "effective" adding something here? sky_view looks to be the fraction of the sky dome "seen" by the module, so that's the usual view factor. I wonder if the formula could be considered part of the model and the input would be surface_tilt, but I'll defer to the model author.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I snuck in the "effective" because the infra-red sky is very non-isotropic and that needs to be taken into account. The formula is really just a starting point for future work, not adequately explored and validated.

Co-authored-by: Cliff Hansen <[email protected]>
@adriesse
Copy link
Member Author

I think we should have a test that checks the output of this function matches the output of faiman, for default parameters.

You mean, the test should call faiman to determine the expected value?

@cwhanse
Copy link
Member

cwhanse commented Nov 22, 2022

I think we should have a test that checks the output of this function matches the output of faiman, for default parameters.

You mean, the test should call faiman to determine the expected value?

Yes

@adriesse
Copy link
Member Author

Additional reviewer(s) invited!

@kandersolar kandersolar added this to the 0.9.4 milestone Dec 1, 2022
Copy link
Member

@kandersolar kandersolar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adriesse I've tagged this for 0.9.4 if you're ready to add a what'snew entry for this PR

@adriesse
Copy link
Member Author

adriesse commented Dec 1, 2022

Last chance to comment! @jsstein @mtheristis

@mtheristis
Copy link

@adriesse, I am sorry for the delay.

Out of curiosity, is there a reason we are only focusing on Faiman here? Or will the others (SAPM, NOCT, PVSyst) come later?

@adriesse
Copy link
Member Author

adriesse commented Dec 2, 2022

@adriesse, I am sorry for the delay.

Out of curiosity, is there a reason we are only focusing on Faiman here? Or will the others (SAPM, NOCT, PVSyst) come later?

Good questions. There were multiple considerations in my head. I decided to do one in order to keep the PR to a reasonable size and work through integration issues on one function rather than four at a time. I picked Faiman because it is the simplest but also because it is used in IEC 61853. When this feature is released I will point it out to interested parties in the working group for that standard.

If there is user demand I see no problem to implement the other three and maybe enhance the generic linear model too.

@mtheristis
Copy link

@adriesse, I am sorry for the delay.
Out of curiosity, is there a reason we are only focusing on Faiman here? Or will the others (SAPM, NOCT, PVSyst) come later?

Good questions. There were multiple considerations in my head. I decided to do one in order to keep the PR to a reasonable size and work through integration issues on one function rather than four at a time. I picked Faiman because it is the simplest but also because it is used in IEC 61853. When this feature is released I will point it out to interested parties in the working group for that standard.

If there is user demand I see no problem to implement the other three and maybe enhance the generic linear model too.

Understood.

I am just thinking that we mind end up with X versions of a same base model, and the users might be confused with their differences. Is there a way to have an option when running faiman to say "include_radiative_losses = True/False"? Or is this bad practice?

@adriesse
Copy link
Member Author

adriesse commented Dec 2, 2022

I am just thinking that we mind end up with X versions of a same base model, and the users might be confused with their differences.

Well, it has taken a lot of years for the first enhancement to arrive and I'm not planning another one! Presenting this as an enhancement of something that people feel familiar and comfortable with is a strategy to gain acceptance more readily. It could
easily have been packaged as a single new model.

Is there a way to have an option when running faiman to say "include_radiative_losses = True/False"? Or is this bad practice?

Perhaps in the system class or model chain something like this is possible. I'm not sure.

Copy link
Member

@AdamRJensen AdamRJensen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool stuff and nice investigations :)

Copy link
Member Author

@adriesse adriesse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reviewing, Adam. I'm glad you found the work interesting!
I responded with some comments and will hold off on code changes to see what other comments might appear...

PS. Sorry, I thought these would appear as replies to your comments. :(
PPS. Oh they do show up as replies. They're above as well as below! :)

Comment on lines 563 to 571
u0 = np.asanyarray(u0)
u1 = np.asanyarray(u1)
emissivity = np.asanyarray(emissivity)

abs_zero = np.array(-273.15)
sigma = np.array(scipy.constants.Stefan_Boltzmann)

if ir_down is None:
qrad_sky = np.array(0.0)
Copy link
Member

@AdamRJensen AdamRJensen Dec 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
u0 = np.asanyarray(u0)
u1 = np.asanyarray(u1)
emissivity = np.asanyarray(emissivity)
abs_zero = np.array(-273.15)
sigma = np.array(scipy.constants.Stefan_Boltzmann)
if ir_down is None:
qrad_sky = np.array(0.0)
poa_global = np.asanyarray(poa_global)
temp_air = np.asanyarray(temp_air)
wind_speed = np.asanyarray(wind_speed)
ir_down = np.asanyarray(ir_down)
u0 = np.asanyarray(u0)
u1 = np.asanyarray(u1)
sky_view = np.asanyarray(sky_view)
emissivity = np.asanyarray(emissivity)
abs_zero = -273.15
sigma = scipy.constants.Stefan_Boltzmann
if ir_down is None:
qrad_sky = 0.0

I see that the usage of np.asanyarray is very carefully chosen such that all inputs can be passed as lists and directly or indirectly be converted to arrays. The indirect conversion to arrays (by multiplying a list something array-like) seems undesirable and unintuitive to the user. Why not instead just convert all the inputs to array-like in the beginning and be straightforward about it?

It's not a blocker for me, but this is the point that I was trying to raise earlier.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the whole I believe this function behaves like numpy would. The reason for the indirect and partial approach is to allow pandas and xarray data structures to be propagated from the first four arguments. Try it, you might like it!

Copy link
Member

@wholmgren wholmgren Dec 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about this function requires any of the np.asanyarray calls? np.asanyarray is not robust.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, now I see the hidden conversation about allowing list input. That is not needed and comes at a cost of disallowing arbitrary array-like input that doesn't fully support the asanyarray machinery (e.g. dask, or whatever the pydata library of the month is).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate on what doesn't work here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall I'm getting more push-back than expected on something which I though was a very simple technique to make the function very flexible. If there is a concrete down-side I'd like to learn about it, especially the risks associated with asanyarray (which could be replaced with asarray in this particular function). I've use this technique before and am likely to use it again unless there really is a problem with it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function is already very flexible because you did a good job writing it :). It doesn't need asanyarray! Here's a concrete downside for asanyarray:

In [1]: import numpy as np

In [2]: import dask.array as da

In [3]: da.from_array([0, 1])
Out[3]: dask.array<array, shape=(2,), dtype=int32, chunksize=(2,), chunktype=numpy.ndarray>

In [4]: np.asanyarray(da.from_array([0, 1]))
Out[4]: array([0, 1])

In [5]: np.asarray(da.from_array([0, 1]))
Out[5]: array([0, 1])

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

np.asanyarray is not robust.

I am curious to learn more about this...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Anton, here's a quote from NEP16

Sometimes people suggest using np.asanyarray for this purpose, but unfortunately its semantics are exactly backwards: it guarantees that the object it returns uses the same memory layout as an ndarray, but tells you nothing at all about its semantics, which makes it essentially impossible to use safely in practice.

The scientific python brain trust has discussed solutions in a variety of venues and I find it very difficult to track, but here's a very incomplete list of links:

I think we have another issue or two related to this, so probably best to continue discussion there rather than a closed PR :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! That was educational!

@AdamRJensen AdamRJensen self-requested a review December 7, 2022 12:20
@kandersolar kandersolar mentioned this pull request Dec 9, 2022
9 tasks
@adriesse
Copy link
Member Author

Thank for all the reviews!!

@adriesse adriesse deleted the rad-loss branch December 12, 2022 18:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants