Skip to content

PVsyst inverter #1002

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

Open
mikofski opened this issue Jul 14, 2020 · 24 comments
Open

PVsyst inverter #1002

mikofski opened this issue Jul 14, 2020 · 24 comments

Comments

@mikofski
Copy link
Member

mimic pvsyst
I would like to mimic pvsyst so as to be bankable

a pvsyst inverter
Inverter model: efficiency says:

Set of 3 efficiency profiles: this is a more accurate definition: we define an efficiency profile as above for 3 different input voltages. At each step, the simulation will perform a quadratic interpolation between these 3 curves, as function of the real input voltage.

Sandia grid inverter model
Any idea how the Sandia inverter model compares?

Other pvsyst inverters
There are 4 options single curve, Euro per weighted efficiency, etc..

See also grid inverter-efficiency curve

@cwhanse
Copy link
Member

cwhanse commented Jul 14, 2020

I agree, pvlib.inverter.pvsyst would be a good addition. In the case of PVsyst option 3, input consisting of three efficiency curves at 3 input voltages, these data are typical for an inverter datasheet. Perhaps we can start there, since I don't know how PVsyst builds the curves from CEC efficiency and threshold power (option 4).

The Sandia inverter model assumes that the efficiency curves evolve linearly as the input DC voltage changes; as @Peque has pointed out, a given device might not follow that linear behavior. An interpolation would be more general.

@Peque
Copy link
Contributor

Peque commented Jul 14, 2020

Would it be interesting to support reading .ond files directly from pvlib? For what I know, it is a non-standard and not documented format used by PVSyst to import/export module data, so probably no... 😂 but maybe I am wrong (?).

@btaute
Copy link
Contributor

btaute commented Jul 14, 2020

The ability to read in .OND files (inverters) and .PAN files (modules) would be a big help for people trying to transition to pvlib from PVSyst.

@cwhanse
Copy link
Member

cwhanse commented Jul 14, 2020

I think @frivollier has shared some tools to read OND and PAN files. I would support adding capabilities to pvlib, either directly or by importing from @frivollier 's project if there's intention to maintain that project.

@mikofski
Copy link
Member Author

CanadianSolar CASSYS is another source of code to parse and model inverters:

@frivollier
Copy link

frivollier commented Jul 14, 2020 via email

@kandersolar
Copy link
Member

In #1199 (comment) @cwhanse said:

That would be a nice addition to pvlib. But, to my knowledge the inverter model in Pvsyst is not fully documented. The Pvsyst license prohibits reverse engineering the software. Without reference to complete and open documentation, I don't think we can add inverter.pvsyst and claim that it's the model in Pvsyst.

Just curious, is that still the case? Do we expect to ever be able to address this feature request?

@cwhanse
Copy link
Member

cwhanse commented Apr 5, 2022

Just curious, is that still the case?

AFAIK, yes.

@mikofski
Copy link
Member Author

mikofski commented Apr 5, 2022

Should we just close this issue? AFAIK pvsyst just interpolates between the curves to get the efficiency. I don't know how it compares to Sandia grid inverter model, but I think it can't be bad to have a function that just does that. I also think the ability to read PAN and OND files would be very helpful. And measurements from the CEC solar equipment lists too while we're at it.

def interp_inverter_eff(eff_curves, pdc_timeseries, vdc_timeseries, voltages, method="quadratic"):
    """
    interpolate inverter efficiency given 3 voltages

    Parameters
    ----------
    eff_curves : numpy.ndarray or pandas.DataFrame
        table of efficiencies with indices or first column as the DC power monotonically
        increasing and each consecutive column at a different monotonically increasing
        voltage corresponding to the ``voltages`` parameter
    pdc_timeseries : numpy.ndarray, pandas.Series, or pandas.DataFrame
    vdc_timeseries : numpy.ndarray, pandas.Series, or pandas.DataFrame 
    voltages : numeric
        a sequence or scalar of the corresponding nominal voltages, must be
        monotonically increasing and length must be same as columns in ``eff_curves``
    method : str
        how to interpolate the efficiency if given 3 voltages

    returns
    -------
    efficiency : numpy.ndarray or pandas.Series
    """
    eff_curves = pd.DataFrame(eff_curves, index_col=0)
    f = scipy.interpolate.interp1d(x=eff_curves.index, y=eff_curves)
    eff = f(pdc_timeseries)
    try:
        num_volts = len(voltages)
    except TypeError: 
        num_volts = 1
    else:
        if num_volts > 1:
            g = scipy.interpolate.interp1d(x=voltages, y=eff.T, kind=method)
            eff = g(vdc_timeseries)

    return eff

@cwhanse
Copy link
Member

cwhanse commented Apr 5, 2022

OK with me to close, or to keep the issue open. It would be nice to have the Pvsyst inverter model in pvlib, the real blocker is the lack of documentation.

@mikofski
Copy link
Member Author

mikofski commented Apr 5, 2022

I'm not sure if the above function works, as you say, there's no docs - which according to the pvlib ethos means it doesn't go in - soo... how about a gallery example or we start a contrib folder?

@cwhanse
Copy link
Member

cwhanse commented Apr 5, 2022

The interpolation is simple enough that I'm ok adding it with the docstring being the reference, if you are persuaded that there's demand for it. It would fill a gap between the Sandia/adriesse models (which require fitting) and the PVWatts model (a single parameter), in that this interpolation could work from the data in the CEC listing or from digitized datasheet curves.

@adriesse
Copy link
Member

adriesse commented Apr 9, 2022

Does anyone else see the irony here? The opening line is:

I would like to mimic pvsyst so as to be bankable

And:

It would be nice to have the Pvsyst inverter model in pvlib, the real blocker is the lack of documentation.

Please let the banks know!

@adriesse
Copy link
Member

adriesse commented Apr 9, 2022

If you have some data and are tempted to use interpolation, I suggest you also give the fitting routine for the ADR inverter model a try. It is located here.

This model is well-documented, and being based on the summation of physically explained losses it is pretty much constrained to produce physically correct efficiency curves. If there is enough demand I can move it to pvlib.

@adriesse
Copy link
Member

adriesse commented Apr 9, 2022

Maybe we could join forces and publish a paper with a title like "Bankability assessment of inverter simulation models" in a publication that bankers read.

@frivollier
Copy link

frivollier commented Apr 9, 2022 via email

@cwhanse
Copy link
Member

cwhanse commented Apr 10, 2022

We could implement the interpolation model described in CASSYS documentation, call it the CASSYS model, and point to the comparison with Pvsyst which shows the two models' results are close.

@mikofski
Copy link
Member Author

mikofski commented Apr 11, 2022

Good idea, starts here.

@adriesse
Copy link
Member

There are some graphs in these publications illustrating what can go wrong with generic numeric interpolation:

https://www.researchgate.net/project/PV-System-Simulation-Software-Validation

@kurt-rhee
Copy link
Contributor

kurt-rhee commented Mar 3, 2023

I found this thread while trying to mimic PVSyst's inverter efficiency model. I wrote a small script to try to emulate:

Notes:

  • I wrote the script to calculate the final efficiency in a looping fashion so that I could step through individual values.
  • I am not sure if PVSyst is linearly interpolating the efficiency vs power curves at each respective voltage using all available points in the curve or if it is generating a piecewise linear interpolation. I have written the code below to linearly interpolate using all available points, and therefore the resulting interpolation is not piecewise. This was done to match the Cassys implementation.
  • The quadratic interpolation gives an upside down curve if a point is not added for efficiency=0 at voltage=0 [Figure 1], a curve with those points added to the interpolation is in [Figure 2] though this makes the curve much too square. I noticed in the Cassys source code [Figure 3] that the efficiency curve at a given power is divided by the input power, which is not done in the python code below. I wasn't sure why the efficiency curved was divided by input power.
  • I have hardcoded some values from an SMA Sunny Central 4400 Inverter [Figure 3].

image
Figure 1: Inverter Curve Shape from quadratic interpolation

image
Figure 2: Inverter Curve Shape from quadratic interpolation when 0, 0 point is added

image
Figure 3: Cassys Source Code
I am not the best C sharp programmer, but my understanding is that:

itsLowEff[0] = DC Power (point from inverter defined curve)
itsLowEff[1] = Efficiency at DC Power
DCPwrIn = DC Power from data (interpolant)

itsPresentEfficiencies[0] = Array of Low Voltage, Med Voltage and High Voltage from inverter defined curve
itsPresentEfficiencies[1] = Array of interpolated efficiencies from linear interpolation

image
Figure 4: PVSyst parameters for inverter efficiency

Emulation Script

import numpy as np
import pandas as pd
import plotly.graph_objects as go


# --- Efficiency Curves from OND ---
LowVoltage = 962
LowVoltageEffCurve = pd.DataFrame({
    'Power': [225, 447, 891, 1336, 2227, 3345, 4468],
    'Efficiency': [97.68, 98.47, 98.79, 98.83, 98.79, 98.66, 98.47]
})

MidVoltage = 1144
MidVoltageEffCurve = pd.DataFrame({
    'Power': [227, 449, 893, 1338, 2230, 3350, 4477],
    'Efficiency': [96.77, 98.01, 98.53, 98.63, 98.64, 98.52, 98.27]
})

HighVoltage = 1193
HighVoltageEffCurve = pd.DataFrame({
    'Power': [228, 449, 893, 1339, 2231, 3351, 4479],
    'Efficiency': [96.55, 97.95, 98.49, 98.57, 98.59, 98.47, 98.23]
})

VoltageLevels = [0, LowVoltage, MidVoltage, HighVoltage]


# --- Read in performance data ---
df = pd.read_csv('inverter.csv')
df['DC Power at MPP (kW)'] = df['DC Power at MPP (W)'] / 1000

# --- Do Interpolations ---
xs = []
ys = []
for i, row in df.iterrows():
    x = row['DC Power at MPP (kW)']
    v = row['DC Voltage at MPP (V)']

    # --- given power, predict efficiency ---
    LowVoltageCoeff = np.polynomial.polynomial.polyfit(
        LowVoltageEffCurve['Power'], LowVoltageEffCurve['Efficiency'], deg=1
    )
    MedVoltageCoeff = np.polynomial.polynomial.polyfit(
        MidVoltageEffCurve['Power'], MidVoltageEffCurve['Efficiency'], deg=1
    )
    HighVoltageCoeff = np.polynomial.polynomial.polyfit(
        HighVoltageEffCurve['Power'], HighVoltageEffCurve['Efficiency'], deg=1
    )

    LowVoltageEfficiency = (LowVoltageCoeff[1] * x) + LowVoltageCoeff[0]
    MidVoltageEfficiency = (MedVoltageCoeff[1] * x) + MedVoltageCoeff[0]
    HighVoltageEfficiency = (HighVoltageCoeff[1] * x) + HighVoltageCoeff[0]
    EfficiencyLevels = [0, LowVoltageEfficiency, MidVoltageEfficiency, HighVoltageEfficiency]

    QuadFitCoeff = np.polynomial.polynomial.polyfit(
        VoltageLevels,
        EfficiencyLevels,
        deg=2
    )

    y = (
        (QuadFitCoeff[2] * (v ** 2)) +
        (QuadFitCoeff[1] * v) +
        (QuadFitCoeff[0])
    )

    xs.append(x)
    ys.append(y)

fig = go.Figure()
fig.add_scatter(
    x=xs,
    y=ys,
    mode='markers'
)
fig.show(renderer='browser')

@kurt-rhee
Copy link
Contributor

I also tried modifying @mikofski 's code to go line by line, it gives the correct curve orientation, but I had to add some new points at 0 for the scipy functions to complete the interpolation. This may give the curve a more linear shape [Figure 1]

image
Figure 1

import numpy as np
import pandas as pd
import scipy
import plotly.graph_objects as go

eff_curves = {
    'power': [0, 227, 449, 893, 1338, 2230, 3350, 4477],
    'v1': [0, 97.68, 98.47, 98.79, 98.83, 98.79, 98.66, 98.47],
    'v2': [0, 96.77, 98.01, 98.53, 98.63, 98.64, 98.52, 98.27],
    'v3': [0, 96.55, 97.95, 98.49, 98.57, 98.59, 98.47, 98.23]
}
eff_curves = pd.DataFrame(eff_curves)
eff_curves = eff_curves.set_index('power')

voltages = [0, 962, 1144, 1193]

df = pd.read_csv('inverter.csv')
df['DC Power at MPP (kW)'] = df['DC Power at MPP (W)'] / 1000

f1 = scipy.interpolate.interp1d(x=eff_curves.index, y=eff_curves['v1'])
f2 = scipy.interpolate.interp1d(x=eff_curves.index, y=eff_curves['v2'])
f3 = scipy.interpolate.interp1d(x=eff_curves.index, y=eff_curves['v3'])

xs = []
ys = []
for i, row in df.iterrows():
    x = row['DC Power at MPP (kW)']
    v = row['DC Voltage at MPP (V)']
    eff1 = f1(x)
    eff2 = f2(x)
    eff3 = f3(x)
    try:
        num_volts = len(voltages)
    except TypeError:
        num_volts = 1
    else:
        if num_volts > 1:
            g = scipy.interpolate.interp1d(x=voltages, y=[0, eff1, eff2, eff3], kind='quadratic')
            y = g(v)
    xs.append(x)
    ys.append(y)

fig = go.Figure()
fig.add_scatter(
    x=xs,
    y=ys,
    mode='markers'
)
fig.show(renderer='browser')

@kurt-rhee
Copy link
Contributor

kurt-rhee commented Mar 3, 2023

Playing around with this a little bit more, here are pictures of different types of linear interpolations and their resulting quadratic interpolations:

None give the expected output, so there must be something else that I am missing.

  1. Linear Interpolation, not including 0,0
    image
    image

  2. Linear Interpolation, including 0,0
    image
    image

  3. Piecewise Linear Interpolation, not including 0, 0
    image
    image

  4. Piecewise Linear Interpolation, including 0, 0
    image
    image

@kandersolar
Copy link
Member

I have written the code below to linearly interpolate using all available points, and therefore the resulting interpolation is not piecewise. This was done to match the Cassys implementation.

I interpret the PVsyst description to be "piecewise interpolation". I don't think a linear least-squares fit to all points in each curve is what is meant; it wouldn't be my first interpretation for the word "interpolation", and it doesn't make sense to fit straight lines to the full efficiency curves anyway. The CASSYS implementation is also "piecewise" in the linear Pdc interpolation:

https://github.com/CanadianSolar/CASSYS/blob/b5487bb4e9e77174c805d64e3c960c46d357b7e2/CASSYS%20Engine/Interpolate.cs#L38

I haven't run any code myself but @mikofski's code in #1002 (comment) looks right to me: linear interpolation (not least-squares fit) to get low/medium/high efficiencies for the given Pdc, then quadratic interpolation between those three efficiencies for the given Vdc. Maybe adding (0,0) is needed if the curves in the OND files don't include it already.

I wasn't sure why the efficiency curved was divided by input power.

I can't make sense of that either. Strange.

@kurt-rhee
Copy link
Contributor

I agree, least squares would be throwing away good data points.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants