Skip to content

port pvl_adrinverter #288

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 18 commits into from
Feb 14, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/sphinx/source/whatsnew/v0.4.4.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ v0.4.4 (January xx, 2017)

Enhancements
~~~~~~~~~~~~

* Added Anton Driesse Inverter database and made compatible with
pvsystem.retrieve_sam. (:issue:`169`)
* Ported Anton Driesse Inverter model from PV_LIB Toolbox. (:issue:`160`)

Documentation
~~~~~~~~~~~~~
Expand All @@ -17,3 +19,4 @@ Contributors
~~~~~~~~~~~~

* Will Holmgren
* Volker Beutner
1,762 changes: 1,762 additions & 0 deletions pvlib/data/adr-library.csv

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions pvlib/modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ def ac_model(self, model):
if model == 'snlinverter':
self._ac_model = self.snlinverter
elif model == 'adrinverter':
raise NotImplementedError
self._ac_model = self.adrinverter
elif model == 'pvwatts':
self._ac_model = self.pvwatts_inverter
else:
Expand All @@ -447,6 +447,8 @@ def infer_ac_model(self):
module_params = set(self.system.module_parameters.keys())
if set(['C0', 'C1', 'C2']) <= inverter_params:
return self.snlinverter
elif set(['ADRCoefficients']) <= inverter_params:
return self.adrinverter
elif set(['pdc0']) <= module_params:
return self.pvwatts_inverter
else:
Expand All @@ -458,7 +460,7 @@ def snlinverter(self):
return self

def adrinverter(self):
raise NotImplementedError
self.ac = self.system.adrinverter(self.dc['v_mp'], self.dc['p_mp'])
return self

def pvwatts_inverter(self):
Expand Down
110 changes: 110 additions & 0 deletions pvlib/pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,9 @@ def snlinverter(self, v_dc, p_dc):
"""
return snlinverter(v_dc, p_dc, self.inverter_parameters)

def adrinverter(self, v_dc, p_dc):
return adrinverter(v_dc, p_dc, self.inverter_parameters)

def scale_voltage_current_power(self, data):
"""
Scales the voltage, current, and power of the DataFrames
Expand Down Expand Up @@ -1056,6 +1059,7 @@ def retrieve_sam(name=None, path=None):
* CEC module database
* Sandia Module database
* CEC Inverter database
* Antone Driesse Inverter database
Copy link
Member

Choose a reason for hiding this comment

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

Anton

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(Sorry Anton)


and return it as a pandas DataFrame.

Expand All @@ -1070,6 +1074,7 @@ def retrieve_sam(name=None, path=None):
(CEC is only current inverter db available; tag kept for
backwards compatibility)
* 'SandiaMod' - returns the Sandia Module database
* 'ADRInverter' - returns the ADR Inverter database

path : None or string
Path to the SAM file. May also be a URL.
Expand Down Expand Up @@ -1122,6 +1127,8 @@ def retrieve_sam(name=None, path=None):
elif name == 'sandiamod':
csvdata = os.path.join(
data_path, 'sam-library-sandia-modules-2015-6-30.csv')
elif name == 'adrinverter':
csvdata = os.path.join(data_path, 'adr-library.csv')
Copy link
Member

Choose a reason for hiding this comment

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

The parameters in this particular file were generated using the CEC efficiency measurements available to me as of 2013-10. It might be good to add the date suffix. I'm looking into doing an update.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok I will add a date suffix

elif name in ['cecinverter', 'sandiainverter']:
# Allowing either, to provide for old code,
# while aligning with current expectations
Expand Down Expand Up @@ -1171,6 +1178,11 @@ def _parse_raw_sam_df(csvdata):

df.index = parsedindex
df = df.transpose()
if 'ADRCoefficients' in df.index:
ad_ce = 'ADRCoefficients'
# parses string into list containing floats:
Copy link
Member

Choose a reason for hiding this comment

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

# for each inverter, parses a string of coefficients like
# ***insert example***
# into a list containing floats

df.loc[ad_ce] = df.loc[ad_ce].map(lambda x: list(
map(float, x.strip(' []').split())))

return df

Expand Down Expand Up @@ -2037,6 +2049,104 @@ def snlinverter(v_dc, p_dc, inverter):
return ac_power


def adrinverter(v_dc, p_dc, inverter):
r'''
PVL_ADRINVERTER Converts DC power and voltage to AC power
Copy link
Member

Choose a reason for hiding this comment

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

no PVL_ADRINVERTER

using Anton Driesse's Grid-Connected PV Inverter efficiency model

Parameters
----------
v_dc : numeric
A scalar or pandas series of DC voltages, in volts, which are provided
as input to the inverter. If Vdc and Pdc are vectors, they must be
of the same size. v_dc must be >= 0. (V)

p_dc : numeric
A scalar or pandas series of DC powers, in watts, which are provided
as input to the inverter. If Vdc and Pdc are vectors, they must be
of the same size. p_dc must be >= 0. (W)

inverter : dict-like
A struct defining the inverter to be used, giving the
Copy link
Member

Choose a reason for hiding this comment

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

dict-like

inverter performance parameters according to the model
developed by Anton Driesse[1].
Copy link
Member

Choose a reason for hiding this comment

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

space before [1]

A set of inverter performance parameters are provided with
pvlib, or may be generated from the library using retrievesam.
Copy link
Member

Choose a reason for hiding this comment

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

maybe something like "loaded from the supplied data table using retrieve_sam" would be more accurate.

See Notes for required keys.

Returns
-------
ac_power : numeric
A numpy array of modeled AC power output given the input
Copy link
Member

Choose a reason for hiding this comment

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

"A numpy array" is not quite right. It returns a Series if the input data is also a Series.

DC voltage, v_dc, and input DC power, p_dc. When ac_power would be
greater than pac_max, it is set to p_max to represent inverter
"clipping". When ac_power would be less than -p_nt (energy consumed rather
than produced) then ac_power is set to -p_nt to represent nightly
power losses. ac_power is not adjusted for maximum power point
tracking (MPPT) voltage windows or maximum current limits of the
inverter.

Notes
-----

Required inverter keys are:

====== ============================================================
Column Description
====== ============================================================
p_nom The nominal power value used to normalize all power values,
typically the DC power needed to produce maximum AC power output, (W).

v_nom The nominal DC voltage value used to normalize DC voltage values,
typically the level at which the highest efficiency is achieved, (V).

pac_max The maximum AC output power value, used to clip the output if needed,(W).

ce_list This is a list of 9 coefficients that capture the influence
of input voltage and power on inverter losses, and thereby efficiency.

p_nt ac-power consumed by inverter at night (night tare) to maintain
circuitry required to sense PV array voltage, (W).
====== ============================================================

References
----------
[1] Beyond the Curves: Modeling the Electrical Efficiency
of Photovoltaic Inverters, PVSC 2008, Anton Driesse et. al.

See also
--------
sapm
singlediode
'''

p_nom = inverter['Pnom']
v_nom = inverter['Vnom']
pac_max = inverter['Pacmax']
p_nt = inverter['Pnt']
ce_list = inverter['ADRCoefficients']

pdc = p_dc/p_nom
vdc = v_dc/v_nom
poly = np.array([pdc**0, pdc, pdc**2, vdc-1, pdc*(vdc-1),
pdc**2*(vdc-1), 1/vdc-1, pdc*(1./vdc-1),
pdc**2*(1./vdc-1)])

p_loss = np.dot(np.array(ce_list), poly)
ac_power = p_nom * (pdc-p_loss)
Copy link
Member

Choose a reason for hiding this comment

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

the name "ac_power" seems to be breaking the naming pattern...


p_nt = -1*np.absolute(p_nt)
Copy link
Member

Choose a reason for hiding this comment

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

Earlier versions of the cec/sam/sandia database were not consistent: most inverters had positive p_nt but many were negative. The latest sam database I looked at had only positve values.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

so p_nt = -1*np.absolute(p_nt) should just be -1*p_nt ?

Copy link
Member

Choose a reason for hiding this comment

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

That was just background info. The absolute() shouldn't be needed, but makes it more robust.


ac_power = np.where((ac_power < p_nt) | (vdc == 0), p_nt, ac_power)

ac_power = np.where(ac_power > pac_max, pac_max, ac_power)

if isinstance(p_dc, pd.Series):
ac_power = pd.Series(ac_power, index=pdc.index)

return ac_power


def scale_voltage_current_power(data, voltage=1, current=1):
"""
Scales the voltage, current, and power of the DataFrames
Expand Down
9 changes: 4 additions & 5 deletions pvlib/test/test_modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ def cec_dc_snl_ac_system(sam_data):
module_parameters['b'] = 0.05
module_parameters['EgRef'] = 1.121
module_parameters['dEgdT'] = -0.0002677
inverters = sam_data['cecinverter']
inverter = inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'].copy()
inverters = sam_data['adrinverter']
Copy link
Member

Choose a reason for hiding this comment

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

maybe I'm forgetting something, but don't we want a new test here instead of changing the existing test?

Copy link
Member

Choose a reason for hiding this comment

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

on a second read, maybe this is ok because all of the methods are still being tested in some way. Is that right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea they are, I think the coverage is the same- this was something I was debating as well.

Copy link
Member

Choose a reason for hiding this comment

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

ok with me to leave it alone then

Copy link
Member

Choose a reason for hiding this comment

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

I'm only now realizing that this change to the cec_dc_snl_ac_system fixture makes the name no longer correct. I do think it would be worthwhile to make a second test called cec_dc_adr_ac_system or something like it. Then you can revert the change to the test_dc_models fixture, while also updating the test_ac_models ac_systems dictionary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch

inverter = inverters['Zigor__Sunzet_3_TL_US_240V__CEC_2011_'].copy()
system = PVSystem(module_parameters=module_parameters,
inverter_parameters=inverter)
return system
Expand Down Expand Up @@ -173,7 +173,7 @@ def poadc(mc):
@requires_scipy
@pytest.mark.parametrize('dc_model, expected', [
('sapm', [181.604438144, -2.00000000e-02]),
('singlediode', [181.044109596, -2.00000000e-02]),
('singlediode', [154.61940956, -25.00000000e-02]),
('pvwatts', [190.028186986, 0]),
(poadc, [189.183065667, 0]) # user supplied function
])
Expand Down Expand Up @@ -201,8 +201,7 @@ def acdc(mc):
@requires_scipy
@pytest.mark.parametrize('ac_model, expected', [
('snlinverter', [181.604438144, -2.00000000e-02]),
pytest.mark.xfail(raises=NotImplementedError)
(('adrinverter', [179.7178188, -2.00000000e-02])),
('adrinverter', [154.61940956, -25.00000000e-02]),
('pvwatts', [190.028186986, 0]),
(acdc, [199.845296258, 0]) # user supplied function
])
Expand Down
11 changes: 11 additions & 0 deletions pvlib/test/test_pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ def sam_data():
data['cecmod'] = pvsystem.retrieve_sam('cecmod')
data['sandiamod'] = pvsystem.retrieve_sam('sandiamod')
data['cecinverter'] = pvsystem.retrieve_sam('cecinverter')
data['adrinverter'] = pvsystem.retrieve_sam('adrinverter')
return data


Expand Down Expand Up @@ -577,6 +578,16 @@ def test_snlinverter(sam_data):
pacs = pvsystem.snlinverter(vdcs, pdcs, inverters[testinv])
assert_series_equal(pacs, pd.Series([-0.020000, 132.004308, 250.000000]))

def test_adrinverter(sam_data):
inverters = sam_data['adrinverter']
testinv = 'Zigor__Sunzet_3_TL_US_240V__CEC_2011_'
vdcs = pd.Series(np.linspace(0,50,3))
idcs = pd.Series(np.linspace(0,11,3))
pdcs = idcs * vdcs

pacs = pvsystem.adrinverter(vdcs, pdcs, inverters[testinv])
assert_series_equal(pacs, pd.Series([-0.25000, 105.635043, 503.769061]))

Copy link
Member

Choose a reason for hiding this comment

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

This test may catch some types of future bugs, but it's not a good PV example. The current, voltage and power ranges are way too small for this Zigor model inverter.

Another idea might be to use some voltage and current combinations found in the pdf file on the CEC/gosolar website and look for the results of the model to be approxiamtely equal.


def test_PVSystem_snlinverter(sam_data):
inverters = sam_data['cecinverter']
Expand Down