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 981 forks source link

Adding Loss Model Categories #988

Closed btaute closed 4 years ago

btaute commented 4 years ago

Following this conversation on the Google Group, it would be helpful to create loss models divided into different categories. I think this could be done effectively with 4 categories:

  1. Effective Irradiance A. All losses already accounted for in the transposition models B. Angle of Incidence losses C. Soiling D. Snow Coverage E. Shading -> this one gets a bit more nuanced if you take into account cell stringing and blends with an electrical loss F. Bifaciality -> Also a bit more nuanced as it incorporates module-specific parameters
  2. Module Performance A. Degradation, including LID, LeTID, and annual degradation B. Module Quality, or the actual performance of a module installed compared to its rated performance C. Module Mismatch? (This one might fit better in the DC losses bucket, but technically current mismatch within a string and voltage mismatch between parallel strings impact performance at a module level)
  3. DC Electrical Losses (after dc_model) A. Voltage drop due to cabling resistance (DC Ohmic Loss in PVSyst) B. Clipping when DC output at MPPT is higher than the inverter rating (although I think this is already well accounted for in pvlib)
  4. AC Losses (after ac_model) A. Medium Voltage Transformer losses B. AC Cabling resistance losses C. High Voltage Transformer losses D. Transmission line losses E. Clipping when a plant is capable of producing more power than it is allowed to inject into its point of interconnection F. Any additional auxiliary losses should be included (like parasitic trackers or a control house)

Often the Module Performance and DC Electrical Loss categories are combined, but I think being more explicit can offer value, as those losses would be applied before the single diode model instead of after.

Are there any thoughts on this implementation?

btaute commented 4 years ago

Following up on this thread, I'd be happy to submit a pull request for one of the models (and eventually all of them). I believe the DC electrical losses would be the simplest to implement, so I would intend to start with that one. I'd like to get some feedback on the implementation prior to doing this, though. Please let me know how I can best assist this project.

Thanks!

kandersolar commented 4 years ago

Hi @btaute, it sounds like you're planning on adding these models as methods on the ModelChain class, is that true? One general piece of feedback is that pvlib is organized into two layers: (1) a backbone of low-level procedural functions to implement models and (2) high-level classes (PVSystem and ModelChain) that provide a more organized interface but still delegate the model execution to the low-level functions. So keeping with that structure, there are two general approaches you should consider:

So for example, in your example code in the google group thread you suggested methods like ModelChain.dc_losses_model and ModelChain.effective_irradiance_model -- we would probably want to see the underlying models implemented as low-level functions before creating the corresponding ModelChain steps, assuming the models you want to include don't already exist in pvlib.

Anyway, your categorizations seem mostly reasonable to me, though I might quibble a bit over the small details. A good reference is the PVPMC modeling website. I would tend to kick the can down the road on what the API changes to ModelChain end up being and focus more on the models that need to be added. What might help make it more concrete would be to include the names/references of the models you're interested in implementing because many of the loss categories you listed don't currently have any models in pvlib.

btaute commented 4 years ago

Thank you for the feedback. I think it would help to focus on one loss category. If we start with an irradiance_loss_model that served the purpose of reducing the effective_irradiance that goes into the dc_model, I see value in two potential loss models (at the moment), which could be called by a ModelChain.effective_irradiance_loss_model.

def pvsyst_irradiance_loss_model(effective_irradiance, monthly_soiling_factors, timeseries_shading_factors):
    """
    Reduce the effective irradiance on the solar plant by accounting for soiling and linear shading effects (electrical effects due to 
    shading are accounted for in the dc_losses_model)

    Parameters
    ------------
    effective_irradiance: Series.
        The effective irradiance in the plane of the array of the solar racking

    monthly_soiling_factors: float or Series with length 12.
        The percent of effective irradiance on a module reduced by soiling.  A constant loss for each month

    timeseries_shading_factors: Series.
        The percent of module surface area shaded

    Returns
    --------
    new_effective_irradiance: Series.
        Effective irradiance accounting for soiling and linear shading losses [W/m2]
    """

    timeseries_soiling_factors = convert_monthly_to_timeseries(monthly_soiling_factors)
    new_effective_irradiance = effective_irradiance * (1 - timeseries_soiling_factors) * (1 - timeseries_shading_factors)

    return new_effective_irradiance

and

def timeseries_irradiance_loss_model(effective_irradiance, timeseries_soiling_factors, timeseries_shading_factors,
                                                            timeseries_snow_coverage_factors):
    """
    Reduce the effective irradiance on the solar plant by accounting for soiling, snow coverage, and linear shading effects 
    (electrical effects due to shading and snow coverage are accounted for in the dc_losses_model)

    Parameters
    ------------
    effective_irradiance: Series.
        The effective irradiance in the plane of the array of the solar racking

    timeseries_soiling_factors: Series.
        The percent of effective irradiance on a module reduced by soiling

    timeseries_shading_factors: Series.
        The percent of module surface area shaded

    timeseries_snow_coverage_factors: Series.
        The percent of module surface area covered by snow

    Returns
    --------
    new_effective_irradiance: Series.
        Effective irradiance accounting for soiling and linear shading losses [W/m2]
    """

    new_effective_irradiance = effective_irradiance * (1 - timeseries_soiling_factors) * (1 - timeseries_shading_factors) * (1 - 
                                                timeseries_snow_coverage_factors)

    return new_effective_irradiance

I believe pvlib has models in place for calculating the snow coverage and soiling timeseries factors. Monthly soiling factors will likely remain inputs. I think a shading model is still missing. I'm imagining the effective_irradiance would already be incorporating bifaciality (if applicable) before reaching this model.

(The code is meant to be indicative. I'm certainly not married to the name "new_effective_irradiance.")

kandersolar commented 4 years ago

Ok, thanks for the examples. And sorry for the continued delayed responses -- I think this is just a busy time of year, it's not a lack of interest. These functions are simple enough, basically just combining different losses together rather than what I was imagining (modeling the component losses themselves). I think hearing from @cwhanse would be useful because he has some planned improvements to ModelChain along these lines as well.

btaute commented 4 years ago

Thanks @kanderso-nrel, and certainly no worries on the response timing.

I thought that would be the appropriate level of abstraction as it would allow different methods of implementing the component loss models together. I see tremendous value in getting the modelchain object in such a form that you can run it to replicate PVSyst results and even more value when the flexibility exists to improve upon that model.

I envision my initial pull requests to implement models at the same level of abstraction as the examples above to apply losses to effective_irradiance, singlediode parameters, dc output (pdc), and ac output (pac), which leave room for more detailed component models to then be implemented. Subsequent pull requests would be to alter the modelchain workflow to infer and utilize these loss models. A PVSyst model replication would leave most of these component model losses as constant W or % decreases, but better component level models would be able to improve upon that.

Another aspect of this I'd be happy to work on, which is both part of this discussion and a discussion all in its own, is incorporating bifaciality. To date, I've done this by applying rear shading and mismatch losses to the backside irradiance, then multiplying it by the module's bifaciality factor and adding to the front side irradiance prior to entering the single diode equation. We can save that implementation until the end if we believe it will muddy the waters of this discussion.

Again, I welcome feedback from all members of this team on the implementation before I put together the first pull request. I'm happy to start coding instead of chatting, but this is obviously something that needs to be incorporated with the style that you all have in mind. Thanks!

btaute commented 4 years ago

I’d like to gently bump this issue. To further clarify my intentions, I’d like to submit a pull request that implements the timeseries_irradiance_loss_model above (with a better name) as a function in the pvystem.py module and as a method in the PVSystem class (within the same module). Please let me know if that Pull Request would be an appropriate first step or if the maintaining team would like to see this implemented in a different manner. Thank you.

cwhanse commented 4 years ago

@btaute I apologize for the delay in responding. Time of year, and other work obligations, etc.

Thank you for this initiative. I think we will do well to take implementing loss functions in manageable steps. The outline you provide is excellent; let's use that as a guide.

As we implement loss models we should do so with an eye towards accomodating different loss models that may be applied at different points in the model chain. As you point out, shading loss can be applied (in a simple way) to reduce effective irradiance, or to reduce module DC current with a more complex shading effects model. Something to keep in mind as we develop naming conventions and code modules.

I agree with your assessment that it is valuable to add loss models in the PVsyst style. Lacking a better idea, let's add the system-level functions to pvsystem.py.

The two functions proposed above appear to have the same objective (implement losses in the PVsyst style) so I think these two are actually a single function irradiance_loss_pvsyst. The loss inputs (soiling, shading, snow) should be Series, and get resampled to the time index of the effective_irradiance input, and a dedicated monthly_to_xxx function shouldn't be necessary.

btaute commented 4 years ago

While the pull request for the irradiance losses is being worked out, I'm thinking about the proper way to incorporate the "Module Performance" losses. I think the dc capacity included in the single diode equation should be adjusted by degradation and a "module quality" factor. The more I think about it, I believe voltage and current mismatch factors should be applied as dc_losses (which may be better named as array_losses). However, if we wanted to include a "pvsyst_model", it would technically include all of these losses at the dc_losses point (post the single diode equation). I believe that's a nuance and the calculations would come out the same, but I figured it's worth addressing, if for no other reason than naming convention. Are there any preferences from the team on the proper implementation of a module_performance_loss model?

For clarity, my suggestion would be something like: adjusted_dc_capacity = pdc (1 - (1 - degradation_loss) (1 - module_quality_loss)) where degradation_loss would be a Series (the calculation of which would likely take into account LID, LeTID, and annual degradation but would be done by another model) and module_quality_loss would be a float.

steve-ransome commented 4 years ago

If you use the Loss Factors Model with IV curves it apportions losses to Isc, Rsc, Imp, Vmp, Roc and Voc components, http://www.steveransome.com/PUBS/1909_5CV4_35_PVSEC36_Marseille_Ransome_PPT.pdf

btaute commented 4 years ago

Thanks for forcing further clarification. I imagine different “module_quality_model” implementations would choose to adjust the single-diode equation parameters differently. The research you shared is very interesting and would serve as a great basis for a potential model. We’ll want to make sure the implementation maintains flexibility for all of these. Keeping in the vein of a model that approximates PVSyst, I’m proposing one implementation that adjusts the cumulative dc capacity and not the parameters of the single diode model itself. For a PVWatts DC Model, that means adjusting the pdc0 parameter. For the other dc models (which actually use the single diode equation), it would be a change in the parameters that go into scale_voltage_current_power() after the single diode model is called. Having to spell this out now, I realize it would be better to implement these losses as part of the array dc loss model and not as a separate module quality model. That said, a different, (non-PVSyst based) model implementation, like the one you shared, would fit into the module_quality_model slot well.

With that in mind, I’m going to focus my next PR on dc losses and circle back to a module performance adjustment later.

btaute commented 4 years ago

My understanding of PVSyst’s array losses calculated during simulation are that they are applied to the Pmpp only. Since the inverter function depends on Pmpp and Vmpp, I think that means PVSyst is effectively applying the DC losses to current instead of voltage. I can’t find that spelled out explicitly, but it seems to line up with observations and the documentation I can find. I’d love to hear others’ interpretations. With that in mind, I propose a dc_losses_pvsyst model that calculates:

Pmpp_losses = Pmpp * (1 - (1 – dc_cabling) * (1 – module_quality) * (1 – mismatch) * (1 – degradation) * (1 – shading_electrical_loss) * (1 – snow_coverage_electrical_loss) * (1 – soiling_electrical_loss))

All of the inputs would be pd.Series, similar to the irradiance_loss_pvsyst parameters. Module_quality, mismatch, and degradation are all constants in PVSyst, but the other parameters would need models of their own to calculate. Something worth adding later.

It’s worth noting that PVSyst will also calculate these losses by adjusting its single diode equation parameters (like increasing Rseries to include the dc_cabling resistance). I don’t believe this is done during simulation; only when a user is evaluating specific scenarios, like creating a Losses Graph. Per their own statement, the results of the two methods are "very close." I think it would be great to include both options in pvlib eventually.