Skip to content

Commit 3d42536

Browse files
VolkrBwholmgren
authored andcommitted
port pvl_adrinverter (#288)
* added adr csv * ADRinv implemented/tests * Added meta data to csv, moved parsing * Cleaned up parsing line, started docs * Updated docs/whatsnew * flake8 docs, fixed typos, other mistakes * p_dc * added date to adr-inv data csv * updated pvsystem with new adr-inv csv filename * updated adr-inverter test case * Updated based on suggested changes * float test, removed idcs * updated docs with vtol * added vtol=0.20 test * changed test params in test_mc to nan * new adrinv test fixture/formatting * typo in test
1 parent c6e870c commit 3d42536

File tree

6 files changed

+1951
-6
lines changed

6 files changed

+1951
-6
lines changed

docs/sphinx/source/whatsnew/v0.4.4.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ v0.4.4 (January xx, 2017)
66
Enhancements
77
~~~~~~~~~~~~
88

9+
* Added Anton Driesse Inverter database and made compatible with
10+
pvsystem.retrieve_sam. (:issue:`169`)
11+
* Ported Anton Driesse Inverter model from PV_LIB Toolbox. (:issue:`160`)
912
* Added Kasten pyrheliometric formula to calculate Linke turbidity factors with
1013
improvements by Ineichen and Perez to extend range of air mass (:issue:`278`)
1114

@@ -39,4 +42,5 @@ Contributors
3942
~~~~~~~~~~~~
4043

4144
* Will Holmgren
45+
* Volker Beutner
4246
* Mark Mikofski

pvlib/data/adr-library-2013-10-01.csv

Lines changed: 1762 additions & 0 deletions
Large diffs are not rendered by default.

pvlib/modelchain.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ def ac_model(self, model):
434434
if model == 'snlinverter':
435435
self._ac_model = self.snlinverter
436436
elif model == 'adrinverter':
437-
raise NotImplementedError
437+
self._ac_model = self.adrinverter
438438
elif model == 'pvwatts':
439439
self._ac_model = self.pvwatts_inverter
440440
else:
@@ -447,6 +447,8 @@ def infer_ac_model(self):
447447
module_params = set(self.system.module_parameters.keys())
448448
if set(['C0', 'C1', 'C2']) <= inverter_params:
449449
return self.snlinverter
450+
elif set(['ADRCoefficients']) <= inverter_params:
451+
return self.adrinverter
450452
elif set(['pdc0']) <= module_params:
451453
return self.pvwatts_inverter
452454
else:
@@ -458,7 +460,7 @@ def snlinverter(self):
458460
return self
459461

460462
def adrinverter(self):
461-
raise NotImplementedError
463+
self.ac = self.system.adrinverter(self.dc['v_mp'], self.dc['p_mp'])
462464
return self
463465

464466
def pvwatts_inverter(self):

pvlib/pvsystem.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,9 @@ def snlinverter(self, v_dc, p_dc):
468468
"""
469469
return snlinverter(v_dc, p_dc, self.inverter_parameters)
470470

471+
def adrinverter(self, v_dc, p_dc):
472+
return adrinverter(v_dc, p_dc, self.inverter_parameters)
473+
471474
def scale_voltage_current_power(self, data):
472475
"""
473476
Scales the voltage, current, and power of the DataFrames
@@ -1062,6 +1065,7 @@ def retrieve_sam(name=None, path=None):
10621065
* CEC module database
10631066
* Sandia Module database
10641067
* CEC Inverter database
1068+
* Anton Driesse Inverter database
10651069
10661070
and return it as a pandas DataFrame.
10671071
@@ -1076,6 +1080,7 @@ def retrieve_sam(name=None, path=None):
10761080
(CEC is only current inverter db available; tag kept for
10771081
backwards compatibility)
10781082
* 'SandiaMod' - returns the Sandia Module database
1083+
* 'ADRInverter' - returns the ADR Inverter database
10791084
10801085
path : None or string
10811086
Path to the SAM file. May also be a URL.
@@ -1128,6 +1133,8 @@ def retrieve_sam(name=None, path=None):
11281133
elif name == 'sandiamod':
11291134
csvdata = os.path.join(
11301135
data_path, 'sam-library-sandia-modules-2015-6-30.csv')
1136+
elif name == 'adrinverter':
1137+
csvdata = os.path.join(data_path, 'adr-library-2013-10-01.csv')
11311138
elif name in ['cecinverter', 'sandiainverter']:
11321139
# Allowing either, to provide for old code,
11331140
# while aligning with current expectations
@@ -1177,6 +1184,13 @@ def _parse_raw_sam_df(csvdata):
11771184

11781185
df.index = parsedindex
11791186
df = df.transpose()
1187+
if 'ADRCoefficients' in df.index:
1188+
ad_ce = 'ADRCoefficients'
1189+
# for each inverter, parses a string of coefficients like
1190+
# ' 1.33, 2.11, 3.12' into a list containing floats:
1191+
# [1.33, 2.11, 3.12]
1192+
df.loc[ad_ce] = df.loc[ad_ce].map(lambda x: list(
1193+
map(float, x.strip(' []').split())))
11801194

11811195
return df
11821196

@@ -2043,6 +2057,120 @@ def snlinverter(v_dc, p_dc, inverter):
20432057
return ac_power
20442058

20452059

2060+
def adrinverter(v_dc, p_dc, inverter, vtol=0.10):
2061+
r'''
2062+
Converts DC power and voltage to AC power using Anton Driesse's
2063+
Grid-Connected PV Inverter efficiency model
2064+
2065+
Parameters
2066+
----------
2067+
v_dc : numeric
2068+
A scalar or pandas series of DC voltages, in volts, which are provided
2069+
as input to the inverter. If Vdc and Pdc are vectors, they must be
2070+
of the same size. v_dc must be >= 0. (V)
2071+
2072+
p_dc : numeric
2073+
A scalar or pandas series of DC powers, in watts, which are provided
2074+
as input to the inverter. If Vdc and Pdc are vectors, they must be
2075+
of the same size. p_dc must be >= 0. (W)
2076+
2077+
inverter : dict-like
2078+
A dict-like object defining the inverter to be used, giving the
2079+
inverter performance parameters according to the model
2080+
developed by Anton Driesse [1].
2081+
A set of inverter performance parameters may be loaded from the
2082+
supplied data table using retrievesam.
2083+
See Notes for required keys.
2084+
2085+
vtol : numeric
2086+
A unit-less fraction that determines how far the efficiency model is
2087+
allowed to extrapolate beyond the inverter's normal input voltage
2088+
operating range. 0.0 <= vtol <= 1.0
2089+
2090+
Returns
2091+
-------
2092+
ac_power : numeric
2093+
A numpy array or pandas series of modeled AC power output given the
2094+
input DC voltage, v_dc, and input DC power, p_dc. When ac_power would
2095+
be greater than pac_max, it is set to p_max to represent inverter
2096+
"clipping". When ac_power would be less than -p_nt (energy consumed
2097+
rather than produced) then ac_power is set to -p_nt to represent
2098+
nightly power losses. ac_power is not adjusted for maximum power point
2099+
tracking (MPPT) voltage windows or maximum current limits of the
2100+
inverter.
2101+
2102+
Notes
2103+
-----
2104+
2105+
Required inverter keys are:
2106+
2107+
======= ============================================================
2108+
Column Description
2109+
======= ============================================================
2110+
p_nom The nominal power value used to normalize all power values,
2111+
typically the DC power needed to produce maximum AC power
2112+
output, (W).
2113+
2114+
v_nom The nominal DC voltage value used to normalize DC voltage
2115+
values, typically the level at which the highest efficiency
2116+
is achieved, (V).
2117+
2118+
pac_max The maximum AC output power value, used to clip the output
2119+
if needed, (W).
2120+
2121+
ce_list This is a list of 9 coefficients that capture the influence
2122+
of input voltage and power on inverter losses, and thereby
2123+
efficiency.
2124+
2125+
p_nt ac-power consumed by inverter at night (night tare) to
2126+
maintain circuitry required to sense PV array voltage, (W).
2127+
======= ============================================================
2128+
2129+
References
2130+
----------
2131+
[1] Beyond the Curves: Modeling the Electrical Efficiency
2132+
of Photovoltaic Inverters, PVSC 2008, Anton Driesse et. al.
2133+
2134+
See also
2135+
--------
2136+
sapm
2137+
singlediode
2138+
'''
2139+
2140+
p_nom = inverter['Pnom']
2141+
v_nom = inverter['Vnom']
2142+
pac_max = inverter['Pacmax']
2143+
p_nt = inverter['Pnt']
2144+
ce_list = inverter['ADRCoefficients']
2145+
v_max = inverter['Vmax']
2146+
v_min = inverter['Vmin']
2147+
vdc_max = inverter['Vdcmax']
2148+
mppt_hi = inverter['MPPTHi']
2149+
mppt_low = inverter['MPPTLow']
2150+
2151+
v_lim_upper = np.nanmax([v_max, vdc_max, mppt_hi])*(1+vtol)
2152+
v_lim_lower = np.nanmax([v_min, mppt_low])*(1-vtol)
2153+
2154+
pdc = p_dc/p_nom
2155+
vdc = v_dc/v_nom
2156+
poly = np.array([pdc**0, pdc, pdc**2, vdc-1, pdc*(vdc-1),
2157+
pdc**2*(vdc-1), 1/vdc-1, pdc*(1./vdc-1),
2158+
pdc**2*(1./vdc-1)])
2159+
p_loss = np.dot(np.array(ce_list), poly)
2160+
ac_power = p_nom * (pdc-p_loss)
2161+
p_nt = -1*np.absolute(p_nt)
2162+
2163+
ac_power = np.where((v_lim_upper < v_dc) | (v_dc < v_lim_lower),
2164+
np.nan, ac_power)
2165+
ac_power = np.where((ac_power < p_nt) | (vdc == 0), p_nt, ac_power)
2166+
ac_power = np.where(ac_power > pac_max, pac_max, ac_power)
2167+
2168+
if isinstance(p_dc, pd.Series):
2169+
ac_power = pd.Series(ac_power, index=pdc.index)
2170+
2171+
return ac_power
2172+
2173+
20462174
def scale_voltage_current_power(data, voltage=1, current=1):
20472175
"""
20482176
Scales the voltage, current, and power of the DataFrames

pvlib/test/test_modelchain.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,20 @@ def cec_dc_snl_ac_system(sam_data):
4040
return system
4141

4242

43+
@pytest.fixture
44+
def cec_dc_adr_ac_system(sam_data):
45+
modules = sam_data['cecmod']
46+
module_parameters = modules['Canadian_Solar_CS5P_220M'].copy()
47+
module_parameters['b'] = 0.05
48+
module_parameters['EgRef'] = 1.121
49+
module_parameters['dEgdT'] = -0.0002677
50+
inverters = sam_data['adrinverter']
51+
inverter = inverters['Zigor__Sunzet_3_TL_US_240V__CEC_2011_'].copy()
52+
system = PVSystem(module_parameters=module_parameters,
53+
inverter_parameters=inverter)
54+
return system
55+
56+
4357
@pytest.fixture
4458
def pvwatts_dc_snl_ac_system(sam_data):
4559
module_parameters = {'pdc0': 220, 'gamma_pdc': -0.003}
@@ -201,15 +215,14 @@ def acdc(mc):
201215
@requires_scipy
202216
@pytest.mark.parametrize('ac_model, expected', [
203217
('snlinverter', [181.604438144, -2.00000000e-02]),
204-
pytest.mark.xfail(raises=NotImplementedError)
205-
(('adrinverter', [179.7178188, -2.00000000e-02])),
218+
('adrinverter', [np.nan, -25.00000000e-02]),
206219
('pvwatts', [190.028186986, 0]),
207220
(acdc, [199.845296258, 0]) # user supplied function
208221
])
209-
def test_ac_models(system, cec_dc_snl_ac_system, pvwatts_dc_pvwatts_ac_system,
222+
def test_ac_models(system, cec_dc_adr_ac_system, pvwatts_dc_pvwatts_ac_system,
210223
location, ac_model, expected):
211224

212-
ac_systems = {'snlinverter': system, 'adrinverter': cec_dc_snl_ac_system,
225+
ac_systems = {'snlinverter': system, 'adrinverter': cec_dc_adr_ac_system,
213226
'pvwatts': pvwatts_dc_pvwatts_ac_system,
214227
acdc: pvwatts_dc_pvwatts_ac_system}
215228

pvlib/test/test_pvsystem.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ def sam_data():
139139
data['cecmod'] = pvsystem.retrieve_sam('cecmod')
140140
data['sandiamod'] = pvsystem.retrieve_sam('sandiamod')
141141
data['cecinverter'] = pvsystem.retrieve_sam('cecinverter')
142+
data['adrinverter'] = pvsystem.retrieve_sam('adrinverter')
142143
return data
143144

144145

@@ -567,6 +568,41 @@ def test_PVSystem_sapm_celltemp():
567568
assert_frame_equal(expected, pvtemps)
568569

569570

571+
def test_adrinverter(sam_data):
572+
inverters = sam_data['adrinverter']
573+
testinv = 'Ablerex_Electronics_Co___Ltd___' + \
574+
'ES_2200_US_240__240_Vac__240V__CEC_2011_'
575+
vdcs = pd.Series([135, 154, 390, 420, 551])
576+
pdcs = pd.Series([135, 1232, 1170, 420, 551])
577+
578+
pacs = pvsystem.adrinverter(vdcs, pdcs, inverters[testinv])
579+
assert_series_equal(pacs, pd.Series([np.nan, 1161.5745, 1116.4459,
580+
382.6679, np.nan]))
581+
582+
583+
def test_adrinverter_vtol(sam_data):
584+
inverters = sam_data['adrinverter']
585+
testinv = 'Ablerex_Electronics_Co___Ltd___' + \
586+
'ES_2200_US_240__240_Vac__240V__CEC_2011_'
587+
vdcs = pd.Series([135, 154, 390, 420, 551])
588+
pdcs = pd.Series([135, 1232, 1170, 420, 551])
589+
590+
pacs = pvsystem.adrinverter(vdcs, pdcs, inverters[testinv], vtol=0.20)
591+
assert_series_equal(pacs, pd.Series([104.8223, 1161.5745, 1116.4459,
592+
382.6679, 513.3385]))
593+
594+
595+
def test_adrinverter_float(sam_data):
596+
inverters = sam_data['adrinverter']
597+
testinv = 'Ablerex_Electronics_Co___Ltd___' + \
598+
'ES_2200_US_240__240_Vac__240V__CEC_2011_'
599+
vdcs = 154.
600+
pdcs = 1232.
601+
602+
pacs = pvsystem.adrinverter(vdcs, pdcs, inverters[testinv])
603+
assert_allclose(pacs, 1161.5745)
604+
605+
570606
def test_snlinverter(sam_data):
571607
inverters = sam_data['cecinverter']
572608
testinv = 'ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'

0 commit comments

Comments
 (0)