pvlib / pvlib-python

A set of documented functions for simulating the performance of photovoltaic energy systems.
https://pvlib-python.readthedocs.io
BSD 3-Clause "New" or "Revised" License
1.16k stars 979 forks source link

Hayes Time-Dependent Module Temperature Model should be added to temperature API. #1080

Open stephenjkaplan opened 3 years ago

stephenjkaplan commented 3 years ago

Description A paper in JPV written by William Hayes in 2014 proposed a time-dependent module temperature model for fixed tilt utility-scale systems. (If anyone wants a copy of the paper, let me know). It's main strengths are that it can model temperature at sub-hourly intervals because if factors in the time dependency of module temperature on its heat capacity. The paper claims higher accuracy than other models.

Proposed Solution

Potential Obstacles

Additional Notes I have started to prototype the API function for this and have discussed this feature with @wholmgren

mikofski commented 3 years ago

I would like to see Matt Prillman's weighted moving average temperature model implemented as well. Stephen, can you comment on differences between the models? Please see https://github.com/NREL/ssc/issues/261

cwhanse commented 3 years ago

I think there's room and use for both models in pvlib

cwhanse commented 3 years ago

About the initial temperature - if in doubt, make it a user input. I'll look through the paper and look forward to the PR.

mikofski commented 3 years ago

As far as I can tell, there isn't anything in the paper that describes what initial condition to use

I believe if running a simulation that starts at midnight, then you can assume the panels are at steady-state in equilibrium with the environment, and therefore use the ambient dry-bulb temperature as the initial condition, otherwise, if starting the simulation when the sun is already up, then I agree with @cwhanse and let the user set the initial temperature.

stephenjkaplan commented 3 years ago

I would like to see Matt Prillman's weighted moving average temperature model implemented as well. Stephen, can you comment on differences between the models? Please see NREL/ssc#261

I haven't dug too deep into that paper, but from what I can tell, both of these models share very similar motivations, but involve different computations. Looks interesting, and sounds like both could be implemented without conflict.

stephenjkaplan commented 3 years ago

@mikofski @cwhanse thank you for the input on initial condition.

any recommendations on the best way to validate my implementation (with limited resources)?

cwhanse commented 3 years ago

pvlib relies on the published sources for validation of the algorithm. If you are asking about functional testing for pvlib, we test for correct calculation, and its OK to create a test input and corresponding output by hand. Or with a separate implementation in e.g. Matlab.

stephenjkaplan commented 3 years ago

@cwhanse yea, that's what I meant. I'm trying to talk to some people at First Solar (I've since left) to try and get my hands on the original implementation, but no luck so far.

cwhanse commented 3 years ago

If you can't get some code from FS, I could implement in Matlab. If our two calculations agree, that's enough evidence for me that the calculation is correct.

stephenjkaplan commented 3 years ago

one of the inputs to the model is effective POA (accounting for optical losses). is it conventional in pvlib to take care of those kinds of calculations within a function, or for the user to calculate effective POA separately with another API function and then use that as input?

wholmgren commented 3 years ago

Convention is for users to do that kind of prep. Keeps functions simple and maximizes user flexibility. If it were intimately tied to the model then we'd include it in the function, but I doubt that's the case here.

stephenjkaplan commented 3 years ago

another question: I have to perform the calculation for long wave radiation using view factors 3 separate times. this seems suited for a utility function. If I want to break out that functionality into a separate function, should I put it within temperature.py as a "private" underscored function? what is conventional?

wholmgren commented 3 years ago

should I put it within temperature.py as a "private" underscored function?

I suggest starting this way and we'll reevaluate once we see the pull request.

wholmgren commented 3 years ago

On that note, be sure to check out the contributing guidelines and perhaps review the discussion on some open and recently closed PRs. Lots of approaches to PRs and PR review, but in short everything is up for debate so don't worry about getting it perfect on the first try.

cwhanse commented 3 years ago

should I put it within temperature.py as a "private" underscored function?

I suggest starting this way and we'll reevaluate once we see the pull request.

If we can get any of the bifacial irradiance models ported to pvlib, the sky view factor being calculated could have other use. I've about given up on #863, too many differences between the Matlab code in PVLib for Matlab and the paper.

stephenjkaplan commented 3 years ago

should I put it within temperature.py as a "private" underscored function?

I suggest starting this way and we'll reevaluate once we see the pull request.

If we can get any of the bifacial irradiance models ported to pvlib, the sky view factor being calculated could have other use. I've about given up on #863, too many differences between the Matlab code in PVLib for Matlab and the paper.

okay I guess I'll see if I can figure out that calculation, and if you all think it should be refactored into a different file for more general use you can let me know after the PR

mikofski commented 3 years ago

I am going to finish #717 I promise!

stephenjkaplan commented 3 years ago

a bit stuck on the convective heat transfer term if anyone has a tip. q12 has to be calculated for:

These values are summed to get the total convective heat transfer. I'm a bit rusty on view factors. I need to determine the view factors for each case. My impression is that the module to module convective term can be assumed to be zero because the emissivity and temperatures are approximately equal (and therefore the terms inside of the q12 calculation cancel out.

I've attached a screenshot of the section of the paper, as well as the entire paper.

Screen Shot 2020-10-15 at 3 51 48 PM JPV Accepted_A Time Dependent Model for CdTe PV Module Temperature in Utility Scale Systems_Grayscale.pdf

cwhanse commented 3 years ago

I thought that might come up when you mentioned view factors. Here's where this effort intersects with the bifacial model effort.

I'm guessing this model assumes that the sky dome is isotropic? If not, that's another hurdle.

To calculate the module-to-sky and module-to-ground view factors one needs to assume array geometry. If the array is many, regularly spaced, infinitely long rows of modules mounted such that the lower edge is close to the ground, the view factors for an interior module can be expressed analytically - see this paper. Quite a few equations to wade through and I haven't been in this literature for a bit.

For arrays that are elevated enough to care about visibility from the module to the shaded areas, the calculations I've seen involve integrating over the module's slant height. That's what #914 and #717 are hoping to do.

Rather than integrating, here it may be OK to use pvlib.shading.masking_angle_passias to get the average angle theta below which the module can't see the sky. Then the module-to-sky view factor could be approximated as

VF = (1 + cos(180 - tilt - theta)) / 2 (the angle is the fraction of a great circle of sky visible from the module) (basically this is Eq. 8 in the Applebaum paper)

For modules at the ends of rows, the view factors involve an integral without a nice closed-form solution that I know of (see here), but these cases are likely not of great interest.

stephenjkaplan commented 3 years ago

@cwhanse thanks. I believe the paper makes the assumptions you mention, as described in the screenshot I took. still a bit unclear on the module-to-module VF.

cwhanse commented 3 years ago

The module-to-module view factor can be computed using the "crossed-string" approach, see Eq. 4 in the Appelbaum paper. Although I share your view that the front and rear surface temperatures are likely close enough (in K) that radiative transfer between modules could be neglected.

mikofski commented 3 years ago

There are module-sky and module-ground view factor functions in #717

These are the links to the POA calls because they average the shaded and no-shade regions separately. EG:

where f_x is the fraction of the module shaded and f_sky_pv_xxx & f_gnd_pv_xxx are the VF's of the sky & ground from the shaded and unshaded fractions of the module.

The functions for f_x aka shade_line, f_sky_diffuse_pv & f_ground_pv are all provided. They return both the shaded & no-shade view factors.

NOTE: b/c you're only interested in the radiant heat xfer between module, sky, & ground, you can ignore the shade line (f_x) and just use the no-shade VF for sky & full shade VF for ground.

Therefore the only relevant angles you need are the tangent of the angle from the bottom of the module to the top of the next row, aka sky_angle_0_tangent and the angle from the top of the module to the bottom of the next row aka ground_angle_1_tangent where "zero" refers to f_x=0 or the bottom of the module and "one" refers to f_x=1 or the top of the module. Then when calling the VF functions, set both angles to these values. Then the only other parameters you need are GCR and tilt:

def sky_angle(gcr, tilt):
    # tangent of angle to top of next row
    f_x = 0.0  # shade line is at bottom, ie no shade
    f_y = 1.0 - f_x  # could just remove these lines
    return f_y * np.sin(tilt) / (1/gcr - f_y * np.cos(tilt))

def sky_vf(tilt, tan_psi_top):
    # view factors of sky from module
    psi_top = np.arctan(tan_psi_top)
    # psi_top_0 = np.arctan(tan_psi_top_0)
    # psi_top_0 = psi_top  # we only want to VF for entire module
    # f_sky_pv_shade = (
    #     (1 + (np.cos(psi_top + tilt)
    #           + np.cos(psi_top_0 + tilt)) / 2) / (1 + np.cos(tilt)))
    # if psi_top == psi_top_0 then there is no shade, and this VF is zero!
    # NOTE: The view factor from the top of the rack is one because it's view is not
    # obstructed.
    f_sky_pv_noshade = (1 + (
        1 + np.cos(psi_top + tilt)) / (1 + np.cos(tilt))) / 2
    return f_sky_pv_noshade  # QED

etc.

stephenjkaplan commented 3 years ago

@mikofski excellent thanks, i'll take a look at these. as far as logistics, this is code in an open pull request, correct? if so, what would be the best way for me to integrate your view factor functions: wait for the PR to be merged and put placeholders in the mean time? copy the code over to my PR? something else?

mikofski commented 3 years ago

I'm not sure? One option would be to refactor the existing pull request to have only the needed functions (VF to sky & ground) and then open a new PR that completes the rest of the functions for infinite sheds. This part of the existing PR is already tested, but not reviewed. However, it is published in PVSC-2019. Would that work? It might actually help to break up #717 and move things along a little faster. What do others think?

cwhanse commented 3 years ago

IMO a separate PR for the view factor functions makes sense. I'd put the functions in pvlib.bifacial

stephenjkaplan commented 3 years ago

I can create the issue/PR for that.

stephenjkaplan commented 3 years ago

@mikofski can you please tell me if I'm on the right track here with the additions I made to this file: https://github.com/stephenjkaplan/pvlib-python/blob/view_factors_1082/pvlib/bifacial.py

If so, I can write some tests

also, I assumed I should only write the functions to perform the minimum scope necessary for my task. not sure if I should have fully implemented them as you have them.

mikofski commented 3 years ago

Hmm, I was thinking the reverse of this. Like repurpose the existing pr to provide the view factors that are needed for #1080, and then make a new pr for the remaining ground-sky view factor, which is the only holdup in the existing pr.

I'm not a huge fan of calling a new module bifacial.py because these viewfactors should also be applied to mono-facial as well. My preference is to use the existing infinite_sheds.py module and just move the unfinished work to a new PR

I assumed I should only write the functions to perform the minimum scope necessary for my task. not sure if I should have fully implemented them as you have them.

The infinite sheds module is already the minimum scope possible, you could perhaps take it down a tiny notch by not spliting the module into two parts, but we're talking a single line, I don't think it's worth it, and it means that we would have double sets of nearly identical functions.

the equations for the module-sky and module-ground VFs are in the pvsc paper linked above. They are very very basic modifications of the standalone VFs that are already listed at the PVPMC website:

For example basic standalone module-ground VF is given in the ground diffuse section image

and the basic standalone module-sky VF is given in the isotropic diffuse sky POA section image

To modify these VFs for a module that is inside an array instead of standalone, you get these "adjustment" factors from the pvsc paper: image

where in the paper I replaced the module tilt angle, θT, with β to be consistent with NREL. Hopefully you can see that the view factor adjustment cancels out the cos(0) = 1 angle of the ground with the angle to the next row which I call ψ in both equations. So they're very minor adjustments mentally, but you do need to know the angle ψ which requires knowledge of GCR and module tilt angle, either θT or β

Can we please not create a duplicate PR? Is it okay if I provide these view factors using the existing infinite sheds PR, stripping out the other unfinished work for a future as yet unopened PR?

BTW: there are a few other papers presented at pvsc-2019 that have these exact same equations, so I'm confident that they're okay.

mikofski commented 3 years ago

That also suggests a simplified approach you could use, if you want to really accelerate this, just use the standalone VFs.

it's not as accurate, but it's fast and done, no muss no fuss

stephenjkaplan commented 3 years ago

Thank you for all of this commentary.

I see. I totally misunderstood what you meant with the pull requests. I definitely won't generate a new pull request. As I was working on it, I was thinking about how complicated a future patch would be if I had done it that way, so that's fine with me.

If it's not too much trouble, that would be really helpful if you reduced the scope of the PR to get it merged in.

I think I might start with the simplified approach and see how well it performs for now.

cwhanse commented 3 years ago

I'm -1 on pvlib.infinite_sheds, the module name doesn't tell me (clearly) what it does. Why not add to the existing pvlib.bifacial?

cwhanse commented 3 years ago

I think I might start with the simplified approach and see how well it performs for now.

Hayes' paper isn't specific whether horizon shading etc. is considered, so I don't see that using the simple VF expressions is a contradiction. OK for v1 of this to use those, IMO, and later, we can wire in more involved VFs which would also expand the parameter list e.g. to include ground coverage ratio.

mikofski commented 3 years ago

Why not add to the existing pvlib.bifacial?

Because the view factors are not unique to bifacial, they should also be used for mono-facial (or front side) as well

wholmgren commented 3 years ago

Could we compromise on irradiance.py?

On Fri, Oct 16, 2020 at 3:07 PM Mark Mikofski notifications@github.com wrote:

Why not add to the existing pvlib.bifacial?

Because the view factors are not unique to bifacial, they should also be used for mono-facial (or front side) as well

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/pvlib/pvlib-python/issues/1080#issuecomment-710674758, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABBOERY2I56BJ7O4EAKJIYLSLC73PANCNFSM4SRE3GJA .

cwhanse commented 3 years ago

Why not add to the existing pvlib.bifacial?

Because the view factors are not unique to bifacial, they should also be used for mono-facial (or front side) as well

I should have been more clear - I'm -1 on have a module named infinite_sheds, I think the infinite sheds approach to calculating front and rear irradiance should be added to bifacial.py.

I'm not opposed to putting utility view factor calculations in irradiance.py, but note that nothing currently in there (or planned) would use them, unless we changed get_ground_diffuse to use the module-to-ground factor. shading.masking_angle is in the same family of utility functions as the view factor functions (and may duplicated by something in #717), so shading.py might be a place to put them, but I think they are less discoverable there than they would be in bifacial.py.