def test_brentq_spr_e20_327(): """test pvsystem.singlediode with Brent method on SPR-E20-327""" spr_e20_327 = CECMOD.SunPower_SPR_E20_327 x = pvsystem.calcparams_desoto( effective_irradiance=POA, temp_cell=TCELL, alpha_sc=spr_e20_327.alpha_sc, a_ref=spr_e20_327.a_ref, I_L_ref=spr_e20_327.I_L_ref, I_o_ref=spr_e20_327.I_o_ref, R_sh_ref=spr_e20_327.R_sh_ref, R_s=spr_e20_327.R_s, EgRef=1.121, dEgdT=-0.0002677) il, io, rs, rsh, nnsvt = x pvs = pvsystem.singlediode(*x, method='lambertw') out = pvsystem.singlediode(*x, method='brentq') isc, voc, imp, vmp, pmp, ix, ixx = out.values() assert np.isclose(pvs['i_sc'], isc) assert np.isclose(pvs['v_oc'], voc) # the singlediode method doesn't actually get the MPP correct pvs_imp = pvsystem.i_from_v(rsh, rs, nnsvt, vmp, io, il, method='lambertw') pvs_vmp = pvsystem.v_from_i(rsh, rs, nnsvt, imp, io, il, method='lambertw') assert np.isclose(pvs_imp, imp) assert np.isclose(pvs_vmp, vmp) assert np.isclose(pvs['p_mp'], pmp) assert np.isclose(pvs['i_x'], ix) pvs_ixx = pvsystem.i_from_v(rsh, rs, nnsvt, (voc + vmp)/2, io, il, method='lambertw') assert np.isclose(pvs_ixx, ixx) return isc, voc, imp, vmp, pmp, pvs
def test_newton_fs_495(): """test pvsystem.singlediode with Newton method on FS495""" fs_495 = CECMOD.First_Solar_FS_495 x = pvsystem.calcparams_desoto( effective_irradiance=POA, temp_cell=TCELL, alpha_sc=fs_495.alpha_sc, a_ref=fs_495.a_ref, I_L_ref=fs_495.I_L_ref, I_o_ref=fs_495.I_o_ref, R_sh_ref=fs_495.R_sh_ref, R_s=fs_495.R_s, EgRef=1.475, dEgdT=-0.0003) il, io, rs, rsh, nnsvt = x x += (101, ) pvs = pvsystem.singlediode(*x, method='lambertw') out = pvsystem.singlediode(*x, method='newton') isc, voc, imp, vmp, pmp, ix, ixx, i, v = out.values() assert np.isclose(pvs['i_sc'], isc) assert np.isclose(pvs['v_oc'], voc) # the singlediode method doesn't actually get the MPP correct pvs_imp = pvsystem.i_from_v(rsh, rs, nnsvt, vmp, io, il, method='lambertw') pvs_vmp = pvsystem.v_from_i(rsh, rs, nnsvt, imp, io, il, method='lambertw') assert np.isclose(pvs_imp, imp) assert np.isclose(pvs_vmp, vmp) assert np.isclose(pvs['p_mp'], pmp) assert np.isclose(pvs['i_x'], ix) pvs_ixx = pvsystem.i_from_v(rsh, rs, nnsvt, (voc + vmp)/2, io, il, method='lambertw') assert np.isclose(pvs_ixx, ixx) return isc, voc, imp, vmp, pmp, i, v, pvs
def test_newton_fs_495(): """test pvsystem.singlediode with Newton method on FS495""" fs_495 = CECMOD.First_Solar_FS_495 x = pvsystem.calcparams_desoto(effective_irradiance=POA, temp_cell=TCELL, alpha_sc=fs_495.alpha_sc, a_ref=fs_495.a_ref, I_L_ref=fs_495.I_L_ref, I_o_ref=fs_495.I_o_ref, R_sh_ref=fs_495.R_sh_ref, R_s=fs_495.R_s, EgRef=1.475, dEgdT=-0.0003) il, io, rs, rsh, nnsvt = x x += (101, ) pvs = pvsystem.singlediode(*x, method='lambertw') out = pvsystem.singlediode(*x, method='newton') isc, voc, imp, vmp, pmp, ix, ixx, i, v = out.values() assert np.isclose(pvs['i_sc'], isc) assert np.isclose(pvs['v_oc'], voc) # the singlediode method doesn't actually get the MPP correct pvs_imp = pvsystem.i_from_v(rsh, rs, nnsvt, vmp, io, il, method='lambertw') pvs_vmp = pvsystem.v_from_i(rsh, rs, nnsvt, imp, io, il, method='lambertw') assert np.isclose(pvs_imp, imp) assert np.isclose(pvs_vmp, vmp) assert np.isclose(pvs['p_mp'], pmp) assert np.isclose(pvs['i_x'], ix) pvs_ixx = pvsystem.i_from_v(rsh, rs, nnsvt, (voc + vmp) / 2, io, il, method='lambertw') assert np.isclose(pvs_ixx, ixx) return isc, voc, imp, vmp, pmp, i, v, pvs
def test_brentq_spr_e20_327(): """test pvsystem.singlediode with Brent method on SPR-E20-327""" spr_e20_327 = CECMOD.SunPower_SPR_E20_327 x = pvsystem.calcparams_desoto(effective_irradiance=POA, temp_cell=TCELL, alpha_sc=spr_e20_327.alpha_sc, a_ref=spr_e20_327.a_ref, I_L_ref=spr_e20_327.I_L_ref, I_o_ref=spr_e20_327.I_o_ref, R_sh_ref=spr_e20_327.R_sh_ref, R_s=spr_e20_327.R_s, EgRef=1.121, dEgdT=-0.0002677) il, io, rs, rsh, nnsvt = x pvs = pvsystem.singlediode(*x, method='lambertw') out = pvsystem.singlediode(*x, method='brentq') isc, voc, imp, vmp, pmp, ix, ixx = out.values() assert np.isclose(pvs['i_sc'], isc) assert np.isclose(pvs['v_oc'], voc) # the singlediode method doesn't actually get the MPP correct pvs_imp = pvsystem.i_from_v(rsh, rs, nnsvt, vmp, io, il, method='lambertw') pvs_vmp = pvsystem.v_from_i(rsh, rs, nnsvt, imp, io, il, method='lambertw') assert np.isclose(pvs_imp, imp) assert np.isclose(pvs_vmp, vmp) assert np.isclose(pvs['p_mp'], pmp) assert np.isclose(pvs['i_x'], ix) pvs_ixx = pvsystem.i_from_v(rsh, rs, nnsvt, (voc + vmp) / 2, io, il, method='lambertw') assert np.isclose(pvs_ixx, ixx) return isc, voc, imp, vmp, pmp, pvs
def test_method_spr_e20_327(method, cec_module_spr_e20_327): """test pvsystem.singlediode with different methods on SPR-E20-327""" spr_e20_327 = cec_module_spr_e20_327 x = pvsystem.calcparams_desoto( effective_irradiance=POA, temp_cell=TCELL, alpha_sc=spr_e20_327['alpha_sc'], a_ref=spr_e20_327['a_ref'], I_L_ref=spr_e20_327['I_L_ref'], I_o_ref=spr_e20_327['I_o_ref'], R_sh_ref=spr_e20_327['R_sh_ref'], R_s=spr_e20_327['R_s'], EgRef=1.121, dEgdT=-0.0002677) il, io, rs, rsh, nnsvt = x pvs = pvsystem.singlediode(*x, method='lambertw') out = pvsystem.singlediode(*x, method=method) isc, voc, imp, vmp, pmp, ix, ixx = out.values() assert np.isclose(pvs['i_sc'], isc) assert np.isclose(pvs['v_oc'], voc) # the singlediode method doesn't actually get the MPP correct pvs_imp = pvsystem.i_from_v(rsh, rs, nnsvt, vmp, io, il, method='lambertw') pvs_vmp = pvsystem.v_from_i(rsh, rs, nnsvt, imp, io, il, method='lambertw') assert np.isclose(pvs_imp, imp) assert np.isclose(pvs_vmp, vmp) assert np.isclose(pvs['p_mp'], pmp) assert np.isclose(pvs['i_x'], ix) pvs_ixx = pvsystem.i_from_v(rsh, rs, nnsvt, (voc + vmp)/2, io, il, method='lambertw') assert np.isclose(pvs_ixx, ixx)
def test_newton_fs_495(method, cec_module_fs_495): """test pvsystem.singlediode with different methods on FS495""" fs_495 = cec_module_fs_495 x = pvsystem.calcparams_desoto( effective_irradiance=POA, temp_cell=TCELL, alpha_sc=fs_495['alpha_sc'], a_ref=fs_495['a_ref'], I_L_ref=fs_495['I_L_ref'], I_o_ref=fs_495['I_o_ref'], R_sh_ref=fs_495['R_sh_ref'], R_s=fs_495['R_s'], EgRef=1.475, dEgdT=-0.0003) il, io, rs, rsh, nnsvt = x x += (101, ) pvs = pvsystem.singlediode(*x, method='lambertw') out = pvsystem.singlediode(*x, method=method) isc, voc, imp, vmp, pmp, ix, ixx, i, v = out.values() assert np.isclose(pvs['i_sc'], isc) assert np.isclose(pvs['v_oc'], voc) # the singlediode method doesn't actually get the MPP correct pvs_imp = pvsystem.i_from_v(rsh, rs, nnsvt, vmp, io, il, method='lambertw') pvs_vmp = pvsystem.v_from_i(rsh, rs, nnsvt, imp, io, il, method='lambertw') assert np.isclose(pvs_imp, imp) assert np.isclose(pvs_vmp, vmp) assert np.isclose(pvs['p_mp'], pmp) assert np.isclose(pvs['i_x'], ix) pvs_ixx = pvsystem.i_from_v(rsh, rs, nnsvt, (voc + vmp)/2, io, il, method='lambertw') assert np.isclose(pvs_ixx, ixx)
def test_singlediode_array(): # github issue 221 photocurrent = np.linspace(0, 10, 11) resistance_shunt = 16 resistance_series = 0.094 nNsVth = 0.473 saturation_current = 1.943e-09 sd = pvsystem.singlediode(photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth, method='lambertw') expected = np.array([ 0., 0.54538398, 1.43273966, 2.36328163, 3.29255606, 4.23101358, 5.16177031, 6.09368251, 7.02197553, 7.96846051, 8.88220557 ]) assert_allclose(sd['i_mp'], expected, atol=0.01) sd = pvsystem.singlediode(photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth) expected = pvsystem.i_from_v(resistance_shunt, resistance_series, nNsVth, sd['v_mp'], saturation_current, photocurrent, method='lambertw') assert_allclose(sd['i_mp'], expected, atol=0.01)
def test_singlediode(): cecmodule = sam_data['cecmod'].Example_Module IL, I0, Rs, Rsh, nNsVth = pvsystem.calcparams_desoto(S=irrad_data.GHI, temp_cell=25, alpha_isc=cecmodule['Alpha_sc'], module_parameters=cecmodule, EgRef=1.121, dEgdT=-0.0002677) pvsystem.singlediode(module=cecmodule, IL=IL, I0=I0, Rs=Rs, Rsh=Rsh, nNsVth=nNsVth)
def test_fit_sandia_simple(get_test_iv_params, get_bad_iv_curves): test_params = get_test_iv_params testcurve = pvsystem.singlediode(photocurrent=test_params['IL'], saturation_current=test_params['I0'], resistance_shunt=test_params['Rsh'], resistance_series=test_params['Rs'], nNsVth=test_params['nNsVth'], ivcurve_pnts=300) expected = tuple(test_params[k] for k in ['IL', 'I0', 'Rs', 'Rsh', 'nNsVth']) result = sde.fit_sandia_simple(voltage=testcurve['v'], current=testcurve['i']) assert np.allclose(result, expected, rtol=5e-5) result = sde.fit_sandia_simple(voltage=testcurve['v'], current=testcurve['i'], v_oc=testcurve['v_oc'], i_sc=testcurve['i_sc']) assert np.allclose(result, expected, rtol=5e-5) result = sde.fit_sandia_simple(voltage=testcurve['v'], current=testcurve['i'], v_oc=testcurve['v_oc'], i_sc=testcurve['i_sc'], v_mp_i_mp=(testcurve['v_mp'], testcurve['i_mp'])) assert np.allclose(result, expected, rtol=5e-5) result = sde.fit_sandia_simple(voltage=testcurve['v'], current=testcurve['i'], vlim=0.1) assert np.allclose(result, expected, rtol=5e-5)
def test_singlediode_series_ivcurve(cec_module_params): times = pd.DatetimeIndex(start='2015-06-01', periods=3, freq='6H') poa_data = pd.Series([0, 400, 800], index=times) IL, I0, Rs, Rsh, nNsVth = pvsystem.calcparams_desoto( poa_data, temp_cell=25, alpha_isc=cec_module_params['alpha_sc'], module_parameters=cec_module_params, EgRef=1.121, dEgdT=-0.0002677) out = pvsystem.singlediode(IL, I0, Rs, Rsh, nNsVth, ivcurve_pnts=3) expected = OrderedDict([('i_sc', array([ nan, 3.01054475, 6.00675648])), ('v_oc', array([ nan, 9.96886962, 10.29530483])), ('i_mp', array([ nan, 2.65191983, 5.28594672])), ('v_mp', array([ nan, 8.33392491, 8.4159707 ])), ('p_mp', array([ nan, 22.10090078, 44.48637274])), ('i_x', array([ nan, 2.88414114, 5.74622046])), ('i_xx', array([ nan, 2.04340914, 3.90007956])), ('v', array([[ nan, nan, nan], [ 0. , 4.98443481, 9.96886962], [ 0. , 5.14765242, 10.29530483]])), ('i', array([[ nan, nan, nan], [ 3.01079860e+00, 2.88414114e+00, 3.10862447e-14], [ 6.00726296e+00, 5.74622046e+00, 0.00000000e+00]]))]) for k, v in out.items(): assert_allclose(expected[k], v, atol=1e-2)
def test_fit_desoto_sandia(cec_params_cansol_cs5p_220p): # this test computes a set of IV curves for the input fixture, fits # the De Soto model to the calculated IV curves, and compares the fitted # parameters to the starting values params = cec_params_cansol_cs5p_220p['params'] params.pop('Adjust') specs = cec_params_cansol_cs5p_220p['specs'] effective_irradiance = np.array([400., 500., 600., 700., 800., 900., 1000.]) temp_cell = np.array([15., 25., 35., 45.]) ee = np.tile(effective_irradiance, len(temp_cell)) tc = np.repeat(temp_cell, len(effective_irradiance)) iph, io, rs, rsh, nnsvth = pvsystem.calcparams_desoto( ee, tc, alpha_sc=specs['alpha_sc'], **params) sim_ivcurves = pvsystem.singlediode(iph, io, rs, rsh, nnsvth, 300) sim_ivcurves['ee'] = ee sim_ivcurves['tc'] = tc result = sdm.fit_desoto_sandia(sim_ivcurves, specs) modeled = pd.Series(index=params.keys(), data=np.nan) modeled['a_ref'] = result['a_ref'] modeled['I_L_ref'] = result['I_L_ref'] modeled['I_o_ref'] = result['I_o_ref'] modeled['R_s'] = result['R_s'] modeled['R_sh_ref'] = result['R_sh_ref'] expected = pd.Series(params) assert np.allclose(modeled[params.keys()].values, expected[params.keys()].values, rtol=5e-2)
def test_singlediode_series_ivcurve(cec_module_params): times = pd.DatetimeIndex(start='2015-06-01', periods=3, freq='6H') poa_data = pd.Series([0, 400, 800], index=times) IL, I0, Rs, Rsh, nNsVth = pvsystem.calcparams_desoto( poa_data, temp_cell=25, alpha_isc=cec_module_params['alpha_sc'], module_parameters=cec_module_params, EgRef=1.121, dEgdT=-0.0002677) out = pvsystem.singlediode(IL, I0, Rs, Rsh, nNsVth, ivcurve_pnts=3) expected = OrderedDict([('i_sc', array([0., 3.01054475, 6.00675648])), ('v_oc', array([0., 9.96886962, 10.29530483])), ('i_mp', array([0., 2.65191983, 5.28594672])), ('v_mp', array([0., 8.33392491, 8.4159707])), ('p_mp', array([0., 22.10090078, 44.48637274])), ('i_x', array([0., 2.88414114, 5.74622046])), ('i_xx', array([0., 2.04340914, 3.90007956])), ('v', array([[0., 0., 0.], [0., 4.98443481, 9.96886962], [0., 5.14765242, 10.29530483]])), ('i', array([[0., 0., 0.], [3.01079860e+00, 2.88414114e+00, 3.10862447e-14], [6.00726296e+00, 5.74622046e+00, 0.00000000e+00]]))]) for k, v in out.items(): assert_allclose(v, expected[k], atol=1e-2)
def _update_iv_params(voc, isc, vmp, imp, ee, iph, io, rs, rsh, nnsvth, u, maxiter, eps1): # Refine Rsh, Rs, Io and Iph in that order. # Helper function for fit_<model>_sandia. counter = 1. # counter variable for parameter updating while loop, # counts iterations prevconvergeparams = {} prevconvergeparams['state'] = 0.0 not_converged = np.array([True]) while not_converged.any() and counter <= maxiter: # update rsh to match max power point using a fixed point method. rsh[u] = _update_rsh_fixed_pt(vmp[u], imp[u], iph[u], io[u], rs[u], rsh[u], nnsvth[u]) # Calculate Rs to be consistent with Rsh and maximum power point _, phi = _calc_theta_phi_exact(vmp[u], imp[u], iph[u], io[u], rs[u], rsh[u], nnsvth[u]) rs[u] = (iph[u] + io[u] - imp[u]) * rsh[u] / imp[u] - \ nnsvth[u] * phi / imp[u] - vmp[u] / imp[u] # Update filter for good parameters u = _filter_params(ee, isc, io, rs, rsh) # Update value for io to match voc io[u] = _update_io(voc[u], iph[u], io[u], rs[u], rsh[u], nnsvth[u]) # Calculate Iph to be consistent with Isc and other parameters iph = isc + io * np.expm1(rs * isc / nnsvth) + isc * rs / rsh # update filter for good parameters u = _filter_params(ee, isc, io, rs, rsh) # compute the IV curve from the current parameter values result = singlediode(iph[u], io[u], rs[u], rsh[u], nnsvth[u]) # check convergence criteria # [5] Step 3d convergeparams = _check_converge( prevconvergeparams, result, vmp[u], imp[u], counter) prevconvergeparams = convergeparams counter += 1. t5 = prevconvergeparams['vmperrmeanchange'] >= eps1 t6 = prevconvergeparams['imperrmeanchange'] >= eps1 t7 = prevconvergeparams['pmperrmeanchange'] >= eps1 t8 = prevconvergeparams['vmperrstdchange'] >= eps1 t9 = prevconvergeparams['imperrstdchange'] >= eps1 t10 = prevconvergeparams['pmperrstdchange'] >= eps1 t11 = prevconvergeparams['vmperrabsmaxchange'] >= eps1 t12 = prevconvergeparams['imperrabsmaxchange'] >= eps1 t13 = prevconvergeparams['pmperrabsmaxchange'] >= eps1 not_converged = np.logical_or.reduce(np.array([t5, t6, t7, t8, t9, t10, t11, t12, t13])) return iph, io, rs, rsh, u
def pv_fit_loss_func(params): """Loss function for physical pv-cell parameters.""" I_ph, I_0, R_s, R_sh, v_th = params pv = pvsystem.singlediode(I_ph, I_0, R_s, R_sh, v_th) return np.sum(np.abs( np.asarray([ pv['i_sc'] - i_sc, pv['v_oc'] - v_oc, pv['i_mp'] - i_mp, pv['v_mp'] - v_mp ])), axis=0)
def test_singlediode_series(): cecmodule = sam_data['cecmod'].Example_Module IL, I0, Rs, Rsh, nNsVth = pvsystem.calcparams_desoto( irrad_data['ghi'], temp_cell=25, alpha_isc=cecmodule['alpha_sc'], module_parameters=cecmodule, EgRef=1.121, dEgdT=-0.0002677) out = pvsystem.singlediode(cecmodule, IL, I0, Rs, Rsh, nNsVth) assert isinstance(out, pd.DataFrame)
def test_singlediode_series(cec_module_params): times = pd.DatetimeIndex(start='2015-01-01', periods=2, freq='12H') poa_data = pd.Series([0, 800], index=times) IL, I0, Rs, Rsh, nNsVth = pvsystem.calcparams_desoto( poa_data, temp_cell=25, alpha_isc=cec_module_params['alpha_sc'], module_parameters=cec_module_params, EgRef=1.121, dEgdT=-0.0002677) out = pvsystem.singlediode(IL, I0, Rs, Rsh, nNsVth) assert isinstance(out, pd.DataFrame)
def test_singlediode_series(): cecmodule = sam_data['cecmod'].Example_Module out = pvsystem.singlediode(cecmodule, 7, 6e-7, .1, 20, .5) expected = {'i_xx': 4.2549732697234193, 'i_mp': 6.1390251797935704, 'v_oc': 8.1147298764528042, 'p_mp': 38.194165464983037, 'i_x': 6.7556075876880621, 'i_sc': 6.9646747613963198, 'v_mp': 6.221535886625464} assert isinstance(out, dict) for k, v in out.items(): assert_almost_equals(expected[k], v, 5)
def test_singlediode_floats(sam_data): module = 'Example_Module' module_parameters = sam_data['cecmod'][module] out = pvsystem.singlediode(7, 6e-7, .1, 20, .5) expected = {'i_xx': 4.2685798754011426, 'i_mp': 6.1390251797935704, 'v_oc': 8.1063001465863085, 'p_mp': 38.194165464983037, 'i_x': 6.7556075876880621, 'i_sc': 6.9646747613963198, 'v_mp': 6.221535886625464} assert isinstance(out, dict) for k, v in out.items(): assert_allclose(expected[k], v, atol=3)
def test_singlediode_floats_ivcurve(): out = pvsystem.singlediode(7, 6e-7, .1, 20, .5, ivcurve_pnts=3) expected = {'i_xx': 4.2685798754011426, 'i_mp': 6.1390251797935704, 'v_oc': 8.1063001465863085, 'p_mp': 38.194165464983037, 'i_x': 6.7556075876880621, 'i_sc': 6.9646747613963198, 'v_mp': 6.221535886625464, 'i': np.array([6.965172e+00, 6.755882e+00, 2.575717e-14]), 'v': np.array([0. , 4.05315, 8.1063])} assert isinstance(out, dict) for k, v in out.items(): assert_allclose(expected[k], v, atol=3)
def test_singlediode_floats(): module = 'Example_Module' module_parameters = sam_data['cecmod'][module] out = pvsystem.singlediode(module_parameters, 7, 6e-7, .1, 20, .5) expected = {'i_xx': 4.2549732697234193, 'i_mp': 6.1390251797935704, 'v_oc': 8.1147298764528042, 'p_mp': 38.194165464983037, 'i_x': 6.7556075876880621, 'i_sc': 6.9646747613963198, 'v_mp': 6.221535886625464} assert isinstance(out, dict) for k, v in out.items(): assert_almost_equals(expected[k], v, 5)
def test_singlediode_floats_ivcurve(): out = pvsystem.singlediode(7, 6e-7, .1, 20, .5, ivcurve_pnts=3) expected = {'i_xx': 4.2498, 'i_mp': 6.1275, 'v_oc': 8.1063, 'p_mp': 38.1937, 'i_x': 6.7558, 'i_sc': 6.9651, 'v_mp': 6.2331, 'i': np.array([6.965172e+00, 6.755882e+00, 2.575717e-14]), 'v': np.array([0., 4.05315, 8.1063])} assert isinstance(out, dict) for k, v in out.items(): assert_allclose(v, expected[k], atol=1e-3)
def cec_module_detail(request, cec_module_id): cec_mod = get_object_or_404(CEC_Module, pk=cec_module_id) fieldnames = CEC_Module._meta.get_fields() cec_mod_dict = {k.name: getattr(cec_mod, k.name) for k in fieldnames} # for k in ['IXO', 'IXXO', 'C4', 'C5', 'C6', 'C7']: # if cec_mod_dict[k] is None: # cec_mod_dict[k] = 0. celltemps = [0.0, 25.0, 50.0, 75.0, 100.0] effirrad = 1000 results = [] for tc in celltemps: params = calcparams_cec( effective_irradiance=effirrad, temp_cell=tc, alpha_sc=cec_mod_dict['alpha_sc'], a_ref=cec_mod_dict['a_ref'], I_L_ref=cec_mod_dict['I_L_ref'], I_o_ref=cec_mod_dict['I_o_ref'], R_sh_ref=cec_mod_dict['R_sh_ref'], R_s=cec_mod_dict['R_s'], Adjust=cec_mod_dict['Adjust']) results.append(singlediode(*params, ivcurve_pnts=100, method='newton')) current = np.concatenate([r['i'].reshape(1, 100) for r in results], axis=0) voltage = np.concatenate([r['v'].reshape(1, 100) for r in results], axis=0) # eff = results['p_mp'] / effirrad / cec_mod.Area * 100 / 1000 fig = figure( x_axis_label='voltage, V [V]', y_axis_label='current, I [A]', title=cec_mod.Name, plot_width=800, plot_height=600, sizing_mode='scale_width' ) plot = fig.multi_line( voltage.tolist(), current.tolist(), color=cmap, line_width=4) legend = Legend(items=[ LegendItem(label='{:d} [C]'.format(int(ct)), renderers=[plot], index=n) for n, ct in enumerate(celltemps)]) fig.scatter( 0, [r['i_sc'] for r in results], size=15, color=cmap, marker='square') fig.scatter( [r['v_oc'] for r in results], 0, size=15, color=cmap) fig.scatter( [r['v_mp'] for r in results], [r['i_mp'] for r in results], size=15, color=cmap, marker='triangle') fig.add_layout(legend) plot_script, plot_div = components(fig) return render( request, 'cec_module_detail.html', { 'path': request.path, 'cec_mod': cec_mod, 'plot_script': plot_script, 'plot_div': plot_div, 'cec_mod_dict': cec_mod_dict, 'cec_mod_tech': dict(CEC_Module.TECH)})
def test_singlediode_series(): cecmodule = sam_data["cecmod"].Example_Module out = pvsystem.singlediode(cecmodule, 7, 6e-7, 0.1, 20, 0.5) expected = { "i_xx": 4.2549732697234193, "i_mp": 6.1390251797935704, "v_oc": 8.1147298764528042, "p_mp": 38.194165464983037, "i_x": 6.7556075876880621, "i_sc": 6.9646747613963198, "v_mp": 6.221535886625464, } assert isinstance(out, dict) for k, v in out.items(): assert_almost_equals(expected[k], v, 5)
def test_singlediode_floats(): module = 'Example_Module' module_parameters = sam_data['cecmod'][module] out = pvsystem.singlediode(module_parameters, 7, 6e-7, .1, 20, .5) expected = { 'i_xx': 4.2685798754011426, 'i_mp': 6.1390251797935704, 'v_oc': 8.1063001465863085, 'p_mp': 38.194165464983037, 'i_x': 6.7556075876880621, 'i_sc': 6.9646747613963198, 'v_mp': 6.221535886625464 } assert isinstance(out, dict) for k, v in out.items(): yield assert_almost_equals, expected[k], v, 3
def test_singlediode_series(cec_module_params): times = pd.date_range(start='2015-01-01', periods=2, freq='12H') effective_irradiance = pd.Series([0.0, 800.0], index=times) IL, I0, Rs, Rsh, nNsVth = pvsystem.calcparams_desoto( effective_irradiance, temp_cell=25, alpha_sc=cec_module_params['alpha_sc'], a_ref=cec_module_params['a_ref'], I_L_ref=cec_module_params['I_L_ref'], I_o_ref=cec_module_params['I_o_ref'], R_sh_ref=cec_module_params['R_sh_ref'], R_s=cec_module_params['R_s'], EgRef=1.121, dEgdT=-0.0002677) out = pvsystem.singlediode(IL, I0, Rs, Rsh, nNsVth) assert isinstance(out, pd.DataFrame)
def test_singlediode_array(): # github issue 221 photocurrent = np.linspace(0, 10, 11) resistance_shunt = 16 resistance_series = 0.094 nNsVth = 0.473 saturation_current = 1.943e-09 sd = pvsystem.singlediode(photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth) expected = np.array([ 0. , 0.54538398, 1.43273966, 2.36328163, 3.29255606, 4.23101358, 5.16177031, 6.09368251, 7.02197553, 7.96846051, 8.88220557]) assert_allclose(sd['i_mp'], expected, atol=0.01)
def singlediodePVSYST(G, T, I0_ref, IL_ref, n_ref, Ns, u_sc, u_n, Rs, Rsh_ref, Rsh_0, Rsh_exp): ''' reference conditions assumed to be STC (1000 W/m^2, 25 C) G = module irradiance under test T = cell temperature under test I0_ref = diode saturation current at STC (from calc_ref_vals) IL_ref = cell photo current at STC (from calc_ref_vals) n_ref = diode ideality factor at STC (from calc_ref_vals) Nsc = number of cells in series (datasheet) u_sc = short circuit current temperature coefficient at 1000W/M^2 (datasheet) u_n = diode ideality factor temperature coefficient (solving for) Rs = series resistance (solving for) Rsh_ref = shunt resistance at 1000 W/m^2 (solving for) Rsh_0 = shunt resistance at 0 W/m^2 (solving for) Rsh_exp = shunt resistance exponential factor (solving for) ''' IL, I0, Rs, Rsh, nNsVth = calcparams_pvsyst(G, T, u_sc, n_ref, u_n, IL_ref, I0_ref, Rsh_ref, Rsh_0, Rs, Ns, Rsh_exp, EgRef=1.121, irrad_ref=1000, temp_ref=25) out = singlediode(IL, I0, Rs, Rsh, nNsVth, ivcurve_pnts=None, method='lambertw') return (out['p_mp'])
def test_singlediode_floats(sam_data): module = 'Example_Module' module_parameters = sam_data['cecmod'][module] out = pvsystem.singlediode(7, 6e-7, .1, 20, .5) expected = {'i_xx': 4.2498, 'i_mp': 6.1275, 'v_oc': 8.1063, 'p_mp': 38.1937, 'i_x': 6.7558, 'i_sc': 6.9651, 'v_mp': 6.2331, 'i': None, 'v': None} assert isinstance(out, dict) for k, v in out.items(): if k in ['i', 'v']: assert v is None else: assert_allclose(v, expected[k], atol=1e-3)
def test_singlediode_floats(): out = pvsystem.singlediode(7, 6e-7, .1, 20, .5, method='lambertw') expected = { 'i_xx': 4.2498, 'i_mp': 6.1275, 'v_oc': 8.1063, 'p_mp': 38.1937, 'i_x': 6.7558, 'i_sc': 6.9651, 'v_mp': 6.2331, 'i': None, 'v': None } assert isinstance(out, dict) for k, v in out.items(): if k in ['i', 'v']: assert v is None else: assert_allclose(v, expected[k], atol=1e-3)
def test_singlediode_floats(sam_data): module = 'Example_Module' module_parameters = sam_data['cecmod'][module] out = pvsystem.singlediode(7, 6e-7, .1, 20, .5) expected = {'i_xx': 4.2685798754011426, 'i_mp': 6.1390251797935704, 'v_oc': 8.1063001465863085, 'p_mp': 38.194165464983037, 'i_x': 6.7556075876880621, 'i_sc': 6.9646747613963198, 'v_mp': 6.221535886625464, 'i': None, 'v': None} assert isinstance(out, dict) for k, v in out.items(): if k in ['i', 'v']: assert v is None else: assert_allclose(expected[k], v, atol=3)
def _add_single_diode_iv_curve(sys_df, sys_params): """Add IV curve generated by single diode model. Args: sys_df (pd.DataFrame): time-series data that must contain 'g_poa' and 'temp_air' parameters sys_params (dict): organized like so: {'mod': pvlib.pvsystem.retrieve_same(...).SYSTEM_NAME, 'n_mod': int} Returns: pd.DataFrame with new columns (voltage_array_single_diode and current_array_single_diode) """ new_df = [] if 'temp_cell' not in sys_df.keys(): sys_df = _add_cell_module_temp(sys_df, sys_params['celltemp_model']) for i in range(len(sys_df)): ser = sys_df.iloc[i] # final two args are the band gap (assumed Si) and temperature dependence of bandgap at SRC il, i0, rs, rsh, nnsvth = pvsystem.calcparams_desoto( ser['g_poa'], ser['temp_cell'], sys_params['mod']['alpha_sc'], sys_params['mod'], 1.121, -0.0002677) params = pvsystem.singlediode(il, i0, rs, rsh, nnsvth, ivcurve_pnts=250) new_df.append({ 'voltage_array_single_diode': params['v'] * sys_params['n_mod'], 'current_array_single_diode': params['i'] }) new_df = pd.DataFrame(new_df, index=sys_df.index) sys_df['voltage_array_single_diode'] = new_df['voltage_array_single_diode'] sys_df['current_array_single_diode'] = new_df['current_array_single_diode'] return sys_df.copy()
def singlediode(self, photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth, ivcurve_pnts=None): """Wrapper around the :py:func:`pvsystem.singlediode` function. Parameters ---------- See pvsystem.singlediode for details Returns ------- See pvsystem.singlediode for details """ return pvsystem.singlediode(photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth, ivcurve_pnts=ivcurve_pnts)
def test_singlediode_series_ivcurve(cec_module_params): times = pd.date_range(start='2015-06-01', periods=3, freq='6H') effective_irradiance = pd.Series([0.0, 400.0, 800.0], index=times) IL, I0, Rs, Rsh, nNsVth = pvsystem.calcparams_desoto( effective_irradiance, temp_cell=25, alpha_sc=cec_module_params['alpha_sc'], a_ref=cec_module_params['a_ref'], I_L_ref=cec_module_params['I_L_ref'], I_o_ref=cec_module_params['I_o_ref'], R_sh_ref=cec_module_params['R_sh_ref'], R_s=cec_module_params['R_s'], EgRef=1.121, dEgdT=-0.0002677) out = pvsystem.singlediode(IL, I0, Rs, Rsh, nNsVth, ivcurve_pnts=3, method='lambertw') expected = OrderedDict([ ('i_sc', array([0., 3.01054475, 6.00675648])), ('v_oc', array([0., 9.96886962, 10.29530483])), ('i_mp', array([0., 2.65191983, 5.28594672])), ('v_mp', array([0., 8.33392491, 8.4159707])), ('p_mp', array([0., 22.10090078, 44.48637274])), ('i_x', array([0., 2.88414114, 5.74622046])), ('i_xx', array([0., 2.04340914, 3.90007956])), ('v', array([[0., 0., 0.], [0., 4.98443481, 9.96886962], [0., 5.14765242, 10.29530483]])), ('i', array([[0., 0., 0.], [3.01079860e+00, 2.88414114e+00, 3.10862447e-14], [6.00726296e+00, 5.74622046e+00, 0.00000000e+00]])) ]) for k, v in out.items(): assert_allclose(v, expected[k], atol=1e-2) out = pvsystem.singlediode(IL, I0, Rs, Rsh, nNsVth, ivcurve_pnts=3) expected['i_mp'] = pvsystem.i_from_v(Rsh, Rs, nNsVth, out['v_mp'], I0, IL, method='lambertw') expected['v_mp'] = pvsystem.v_from_i(Rsh, Rs, nNsVth, out['i_mp'], I0, IL, method='lambertw') expected['i'] = pvsystem.i_from_v(Rsh, Rs, nNsVth, out['v'].T, I0, IL, method='lambertw').T expected['v'] = pvsystem.v_from_i(Rsh, Rs, nNsVth, out['i'].T, I0, IL, method='lambertw').T for k, v in out.items(): assert_allclose(v, expected[k], atol=1e-2)
def _add_single_diode_params(sys_df, sys_params, extracted_params=True): """Compute expected IV parameters using single-diode equation. Single-diode parameters will be added to the self.df_dict dataframes using the following columns: isc_single_diode, voc_single_diode, ipmax_single_diode, vpmax_single_diode, pmax_single_diode, ix_single_diode, ixx_single_diode The measured values will also be normalized by single-diode parameters and stored in columns with names: isc_norm_single_diode, voc_norm_single_diode, ipmax_norm_single_diode, vpmax_norm_single_diode, pmax_norm_single_diode, ix_norm_single_diode, ixx_norm_single_diode Args: sys_df (pd.DataFrame): time-series data that must contain IV curves sys_params (dict): parameters needed for single-diode calculation from PVLIB; organized as follows: {'mod': pvlib.pvsystem.retrieve_same(...).SYSTEM_NAME, 'n_mod': int} Returns: pd.DataFrame with additional columns added """ diode_params = [] if 'temp_cell' not in sys_df.keys(): sys_df = _add_cell_module_temp(sys_df, sys_params['celltemp_model']) for i in range(len(sys_df)): ser = sys_df.iloc[i] # final two args are the band gap (assumed Si) and temperature dependence of bandgap at SRC il, i0, rs, rsh, nnsvth = pvsystem.calcparams_desoto( ser['g_poa'], ser['temp_cell'], sys_params['mod']['alpha_sc'], sys_params['mod'], 1.121, -0.0002677) params = pvsystem.singlediode(il, i0, rs, rsh, nnsvth) diode_params.append({ 'isc_single_diode': params['i_sc'], 'voc_single_diode': params['v_oc'] * sys_params['n_mod'], 'ipmax_single_diode': params['i_mp'], 'vpmax_single_diode': params['v_mp'] * sys_params['n_mod'], 'pmax_single_diode': params['p_mp'] * sys_params['n_mod'], 'ix_single_diode': params['i_x'], 'ixx_single_diode': params['i_xx'] }) param_df = pd.DataFrame(diode_params, index=sys_df.index) for key in param_df: sys_df[key] = param_df[key] if extracted_params: try: sys_df['isc_norm_single_diode'] = sys_df['isc_extracted'] / sys_df[ 'isc_single_diode'] sys_df['voc_norm_single_diode'] = sys_df['voc_extracted'] / sys_df[ 'voc_single_diode'] sys_df['ipmax_norm_single_diode'] = sys_df[ 'ipmax_extracted'] / sys_df['ipmax_single_diode'] sys_df['vpmax_norm_single_diode'] = sys_df[ 'vpmax_extracted'] / sys_df['vpmax_single_diode'] sys_df['pmax_norm_single_diode'] = sys_df[ 'pmax_extracted'] / sys_df['pmax_single_diode'] sys_df['ix_norm_single_diode'] = sys_df['ix_extracted'] / sys_df[ 'ix_single_diode'] sys_df['ixx_norm_single_diode'] = sys_df['ixx_extracted'] / sys_df[ 'ixx_single_diode'] except KeyError: raise KeyError( 'Call param_extraction() if extracted_params = True.') else: sys_df['isc_norm_single_diode'] = sys_df['isc'] / sys_df[ 'isc_single_diode'] sys_df['voc_norm_single_diode'] = sys_df['voc'] / sys_df[ 'voc_single_diode'] sys_df['ipmax_norm_single_diode'] = sys_df['ipmax'] / sys_df[ 'ipmax_single_diode'] sys_df['vpmax_norm_single_diode'] = sys_df['vpmax'] / sys_df[ 'vpmax_single_diode'] sys_df['pmax_norm_single_diode'] = sys_df['pmax'] / sys_df[ 'pmax_single_diode'] # data doesn't usually contain ix, ixx try: sys_df['ix_norm_single_diode'] = sys_df['ix'] / sys_df[ 'ix_single_diode'] except KeyError: pass try: sys_df['ixx_norm_single_diode'] = sys_df['ixx'] / sys_df[ 'ixx_single_diode'] except KeyError: pass return sys_df.copy()
def pvlib_single_diode( effective_irradiance, temperature_cell, resistance_shunt_ref, resistance_series_ref, diode_factor, cells_in_series, alpha_isc, photocurrent_ref, saturation_current_ref, Eg_ref=1.121, dEgdT=-0.0002677, conductance_shunt_extra=0, irradiance_ref=1000, temperature_ref=25, method='newton', ivcurve_pnts=None, output_all_params=False ): """ Find points of interest on the IV curve given module parameters and operating conditions. method 'newton is about twice as fast as method 'lambertw Parameters ---------- effective_irradiance : numeric effective irradiance in W/m^2 temp_cell : numeric Cell temperature in C resistance_shunt : numeric resistance_series : numeric diode_ideality_factor : numeric number_cells_in_series : numeric alpha_isc : in amps/c reference_photocurrent : numeric photocurrent at standard test conditions, in A. reference_saturation_current : numeric reference_Eg : numeric band gap in eV. reference_irradiance : numeric reference irradiance in W/m^2. Default is 1000. reference_temperature : numeric reference temperature in C. Default is 25. verbose : bool Whether to print information. Returns ------- OrderedDict or DataFrame The returned dict-like object always contains the keys/columns: * i_sc - short circuit current in amperes. * v_oc - open circuit voltage in volts. * i_mp - current at maximum power point in amperes. * v_mp - voltage at maximum power point in volts. * p_mp - power at maximum power point in watts. * i_x - current, in amperes, at ``v = 0.5*v_oc``. * i_xx - current, in amperes, at ``V = 0.5*(v_oc+v_mp)``. If ivcurve_pnts is greater than 0, the output dictionary will also include the keys: * i - IV curve current in amperes. * v - IV curve voltage in volts. The output will be an OrderedDict if photocurrent is a scalar, array, or ivcurve_pnts is not None. The output will be a DataFrame if photocurrent is a Series and ivcurve_pnts is None. """ kB = 1.381e-23 q = 1.602e-19 nNsVth_ref = diode_factor * cells_in_series * kB / q * ( 273.15 + temperature_ref) iph, io, rs, rsh, nNsVth = calcparams_pvpro(effective_irradiance, temperature_cell, alpha_isc, nNsVth_ref, photocurrent_ref, saturation_current_ref, resistance_shunt_ref, resistance_series_ref, conductance_shunt_extra, Eg_ref=Eg_ref, dEgdT=dEgdT, irradiance_ref=irradiance_ref, temperature_ref=temperature_ref) out = singlediode(iph, io, rs, rsh, nNsVth, method=method, ivcurve_pnts=ivcurve_pnts, ) # out = rename(out) if output_all_params: params = {'photocurrent': iph, 'saturation_current': io, 'resistance_series': rs, 'resistace_shunt': rsh, 'nNsVth': nNsVth} for p in params: out[p] = params[p] return out
conditions['Geff'], conditions['Tcell'], alpha_sc=parameters['alpha_sc'], a_ref=parameters['a_ref'], I_L_ref=parameters['I_L_ref'], I_o_ref=parameters['I_o_ref'], R_sh_ref=parameters['R_sh_ref'], R_s=parameters['R_s'], EgRef=1.121, dEgdT=-0.0002677) # plug the parameters into the SDE and solve for IV curves: curve_info = pvsystem.singlediode(photocurrent=IL, saturation_current=I0, resistance_series=Rs, resistance_shunt=Rsh, nNsVth=nNsVth, ivcurve_pnts=100, method='lambertw') # plot the calculated curves: plt.figure() for i, case in conditions.iterrows(): label = ("$G_{eff}$ " + f"{case['Geff']} $W/m^2$\n" "$T_{cell}$ " + f"{case['Tcell']} $C$") plt.plot(curve_info['v'][i], curve_info['i'][i], label=label) v_mp = curve_info['v_mp'][i] i_mp = curve_info['i_mp'][i] # mark the MPP plt.plot([v_mp], [i_mp], ls='', marker='o', c='k')
def test_fit_pvsyst_sandia(npts=3000): # get IV curve data iv_specs, ivcurves = _read_iv_curves_for_test('PVsyst_demo.csv', npts) # get known Pvsyst model parameters and five parameters from each fitted # IV curve pvsyst_specs, pvsyst = _read_pvsyst_expected('PVsyst_demo_model.csv') modeled = sdm.fit_pvsyst_sandia(ivcurves, iv_specs) # calculate IV curves using the fitted model, for comparison with input # IV curves param_res = pvsystem.calcparams_pvsyst( effective_irradiance=ivcurves['ee'], temp_cell=ivcurves['tc'], alpha_sc=iv_specs['alpha_sc'], gamma_ref=modeled['gamma_ref'], mu_gamma=modeled['mu_gamma'], I_L_ref=modeled['I_L_ref'], I_o_ref=modeled['I_o_ref'], R_sh_ref=modeled['R_sh_ref'], R_sh_0=modeled['R_sh_0'], R_s=modeled['R_s'], cells_in_series=iv_specs['cells_in_series'], EgRef=modeled['EgRef']) iv_res = pvsystem.singlediode(*param_res) # assertions assert np.allclose( ivcurves['p_mp'], iv_res['p_mp'], equal_nan=True, rtol=0.038) assert np.allclose( ivcurves['v_mp'], iv_res['v_mp'], equal_nan=True, rtol=0.029) assert np.allclose( ivcurves['i_mp'], iv_res['i_mp'], equal_nan=True, rtol=0.021) assert np.allclose( ivcurves['i_sc'], iv_res['i_sc'], equal_nan=True, rtol=0.003) assert np.allclose( ivcurves['v_oc'], iv_res['v_oc'], equal_nan=True, rtol=0.019) # cells_in_series, alpha_sc, beta_voc, descr assert all((iv_specs[k] == pvsyst_specs[k]) for k in iv_specs.keys()) # I_L_ref, I_o_ref, EgRef, R_sh_ref, R_sh_0, R_sh_exp, R_s, gamma_ref, # mu_gamma assert np.isclose(modeled['I_L_ref'], pvsyst['I_L_ref'], rtol=6.5e-5) assert np.isclose(modeled['I_o_ref'], pvsyst['I_o_ref'], rtol=0.15) assert np.isclose(modeled['R_s'], pvsyst['R_s'], rtol=0.0035) assert np.isclose(modeled['R_sh_ref'], pvsyst['R_sh_ref'], rtol=0.091) assert np.isclose(modeled['R_sh_0'], pvsyst['R_sh_0'], rtol=0.013) assert np.isclose(modeled['EgRef'], pvsyst['EgRef'], rtol=0.037) assert np.isclose(modeled['gamma_ref'], pvsyst['gamma_ref'], rtol=0.0045) assert np.isclose(modeled['mu_gamma'], pvsyst['mu_gamma'], rtol=0.064) # Iph, Io, Rsh, Rs, u mask = np.ones(modeled['u'].shape, dtype=bool) # exclude one curve with different convergence umask = mask.copy() umask[2540] = False assert all(modeled['u'][umask] == pvsyst['u'][:npts][umask]) assert np.allclose( modeled['iph'][modeled['u']], pvsyst['iph'][:npts][modeled['u']], equal_nan=True, rtol=0.0009) assert np.allclose( modeled['io'][modeled['u']], pvsyst['io'][:npts][modeled['u']], equal_nan=True, rtol=0.096) assert np.allclose( modeled['rs'][modeled['u']], pvsyst['rs'][:npts][modeled['u']], equal_nan=True, rtol=0.035) # exclude one curve with Rsh outside 63% tolerance rshmask = modeled['u'].copy() rshmask[2545] = False assert np.allclose( modeled['rsh'][rshmask], pvsyst['rsh'][:npts][rshmask], equal_nan=True, rtol=0.63)
def pvsyst_parameter_estimation(ivcurves, specs, const=const_default, maxiter=5, eps1=1.e-3, graphic=False): """ pvsyst_parameter_estimation estimates parameters fro the PVsyst module performance model Syntax ------ PVsyst, oflag = pvsyst_paramter_estimation(ivcurves, specs, const, maxiter, eps1, graphic) Description ----------- pvsyst_paramter_estimation estimates parameters for the PVsyst module performance model [2,3,4]. Estimation methods are documented in [5,6,7]. Parameters ---------- ivcurves: a dict containing IV curve data in the following fields where j denotes the jth data set ivcurves['i'][j] - a numpy array of current (A) (same length as v) ivcurves['v'][j] - a numpy array of voltage (V) (same length as i) ivcurves['ee'][j] - effective irradiance (W / m^2), i.e., POA broadband irradiance adjusted by solar spectrum modifier ivcurves['tc'][j] - cell temperature (C) ivcurves['isc'][j] - short circuit current of IV curve (A) ivcurves['voc'][j] - open circuit voltage of IV curve (V) ivcurves['imp'][j] - current at max power point of IV curve (A) ivcurves['vmp'][j] - voltage at max power point of IV curve (V) specs: a dict containing module-level values specs['ns'] - number of cells in series specs['aisc'] - temperature coefficeint of isc (A/C) const: an optional OrderedDict containing physical and other constants const['E0'] - effective irradiance at STC, normally 1000 W/m2 constp['T0'] - cell temperature at STC, normally 25 C const['k'] - 1.38066E-23 J/K (Boltzmann's constant) const['q'] - 1.60218E-19 Coulomb (elementary charge) maxiter: an optional numpy array input that sets the maximum number of iterations for the parameter updating part of the algorithm. Default value is 5. eps1: the desired tolerance for the IV curve fitting. The iterative parameter updating stops when absolute values of the percent change in mean, max and standard deviation of Imp, Vmp and Pmp between iterations are all less than eps1, or when the number of iterations exceeds maxiter. Default value is 1e-3 (.0001%). graphic: a boolean, if true then plots are produced during the parameter estimation process. Default is false Returns ------- pvsyst: a OrderedDict containing the model parameters pvsyst['IL_ref'] - light current (A) at STC pvsyst['Io_ref'] - dark current (A) at STC pvsyst['eG'] - effective band gap (eV) at STC pvsyst['Rsh_ref'] - shunt resistance (ohms) at STC pvsyst['Rsh0'] - shunt resistance (ohms) at zero irradiance pvsyst['Rshexp'] - exponential factor defining decrease in rsh with increasing effective irradiance pvsyst['Rs_ref'] - series resistance (ohms) at STC pvsyst['gamma_ref'] - diode (ideality) factor at STC pvsyst['mugamma'] - temperature coefficient for diode (ideality) factor pvsyst['Iph'] - numpy array of values of light current Iph estimated for each IV curve pvsyst['Io'] - numpy array of values of dark current Io estimated for each IV curve pvsyst['Rsh'] - numpy array of values of shunt resistance Rsh estimated for each IV curve pvsyst['Rs'] - numpy array of values of series resistance Rs estimated for each IV curve pvsyst.u - filter indicating IV curves with parameter values deemed reasonable by the private function filter_params oflag: Boolean indicating success or failure of estimation of the diode (ideality) factor parameter. If failure, then no parameter values are returned References ---------- [1] PVLib MATLAB [2] K. Sauer, T. Roessler, C. W. Hansen, Modeling the Irradiance and Temperature Dependence of Photovoltaic Modules in PVsyst, IEEE Journal of Photovoltaics v5(1), January 2015. [3] A. Mermoud, PV Modules modeling, Presentation at the 2nd PV Performance Modeling Workshop, Santa Clara, CA, May 2013 [4] A. Mermoud, T. Lejeuene, Performance Assessment of a Simulation Model for PV modules of any available technology, 25th European Photovoltaic Solar Energy Conference, Valencia, Spain, Sept. 2010 [5] C. Hansen, Estimating Parameters for the PVsyst Version 6 Photovoltaic Module Performance Model, Sandia National Laboratories Report SAND2015-8598 [6] C. Hansen, Parameter Estimation for Single Diode Models of Photovoltaic Modules, Sandia National Laboratories Report SAND2015-2065 [7] C. Hansen, Estimation of Parameters for Single Diode Models using Measured IV Curves, Proc. of the 39th IEEE PVSC, June 2013. """ ee = ivcurves['ee'] tc = ivcurves['tc'] tck = tc + 273.15 isc = ivcurves['isc'] voc = ivcurves['voc'] imp = ivcurves['imp'] vmp = ivcurves['vmp'] # Cell Thermal Voltage vth = const['k'] / const['q'] * tck n = len(ivcurves['voc']) # Initial estimate of Rsh used to obtain the diode factor gamma0 and diode # temperature coefficient mugamma. Rsh is estimated using the co-content # integral method. pio = np.ones(n) piph = np.ones(n) prsh = np.ones(n) prs = np.ones(n) pn = np.ones(n) for j in range(n): current, voltage = rectify_iv_curve(ivcurves['i'][j], ivcurves['v'][j], voc[j], isc[j]) # initial estimate of Rsh, from integral over voltage regression # [5] Step 3a; [6] Step 3a pio[j], piph[j], prs[j], prsh[j], pn[j] = \ est_single_diode_param(current, voltage, vth[j] * specs['ns']) # Estimate the diode factor gamma from Isc-Voc data. Method incorporates # temperature dependence by means of the equation for Io y = np.log(isc - voc / prsh) - 3. * np.log(tck / (const['T0'] + 273.15)) x1 = const['q'] / const['k'] * (1. / (const['T0'] + 273.15) - 1. / tck) x2 = voc / (vth * specs['ns']) t0 = np.isnan(y) t1 = np.isnan(x1) t2 = np.isnan(x2) uu = np.logical_or(t0, t1) uu = np.logical_or(uu, t2) x = np.vstack((np.ones(len(x1[~uu])), x1[~uu], -x1[~uu] * (tck[~uu] - (const['T0'] + 273.15)), x2[~uu], -x2[~uu] * (tck[~uu] - (const['T0'] + 273.15)))).T alpha = np.linalg.lstsq(x, y[~uu])[0] gamma_ref = 1. / alpha[3] mugamma = alpha[4] / alpha[3] ** 2 if np.isnan(gamma_ref) or np.isnan(mugamma) or not np.isreal(gamma_ref) \ or not np.isreal(mugamma): badgamma = True else: badgamma = False pvsyst = OrderedDict() if ~badgamma: gamma = gamma_ref + mugamma * (tc - const['T0']) if graphic: f1 = plt.figure() ax10 = f1.add_subplot(111) ax10.plot(x2, y, 'b+', x2, x * alpha, 'r.') ax10.set_xlabel('X = Voc / Ns * Vth') ax10.set_ylabel('Y = log(Isc - Voc/Rsh)') ax10.legend(['I-V Data', 'Regression Model'], loc=2) ax10.text(np.min(x2) + 0.85 * (np.max(x2) - np.min(x2)), 1.05 * np.max(y), ['\gamma_0 = %s' % gamma_ref]) ax10.text(np.min(x2) + 0.85 * (np.max(x2) - np.min(x2)), 0.98 * np.max(y), ['\mu_\gamma = %s' % mugamma]) plt.show() nnsvth = gamma * (vth * specs['ns']) # For each IV curve, sequentially determine initial values for Io, Rs, # and Iph [5] Step 3a; [6] Step 3 io = np.ones(n) iph = np.ones(n) rs = np.ones(n) rsh = prsh for j in range(n): curr, volt = rectify_iv_curve(ivcurves['i'][j], ivcurves['v'][j], voc[j], isc[j]) if rsh[j] > 0: # Initial estimate of Io, evaluate the single diode model at # voc and approximate Iph + Io = Isc [5] Step 3a; [6] Step 3b io[j] = (isc[j] - voc[j] / rsh[j]) * np.exp(-voc[j] / nnsvth[j]) # initial estimate of rs from dI/dV near Voc # [5] Step 3a; [6] Step 3c [didv, d2id2v] = numdiff(volt, curr) t3 = volt > .5 * voc[j] t4 = volt < .9 * voc[j] u = np.logical_and(t3, t4) tmp = -rsh[j] * didv - 1. v = np.logical_and(u, tmp > 0) if np.sum(v) > 0: vtrs = nnsvth[j] / isc[j] * \ (np.log(tmp[v] * nnsvth[j] / (rsh[j] * io[j])) - volt[v] / nnsvth[j]) rs[j] = np.mean(vtrs[vtrs > 0], axis=0) else: rs[j] = 0. # Initial estimate of Iph, evaluate the single diode model at # Isc [5] Step 3a; [6] Step 3d iph[j] = isc[j] - io[j] + io[j] * np.exp(isc[j] / nnsvth[j]) \ + isc[j] * rs[j] / rsh[j] else: io[j] = float("Nan") rs[j] = float("Nan") iph[j] = float("Nan") # Filter IV curves for good initial values # [5] Step 3b u = filter_params(io, rsh, rs, ee, isc) # Refine Io to match Voc # [5] Step 3c tmpiph = iph tmpio = update_io_known_n(rsh[u], rs[u], nnsvth[u], io[u], tmpiph[u], voc[u]) io[u] = tmpio # Calculate Iph to be consistent with Isc and current values of other # parameters [6], Step 3c iph = isc - io + io * np.exp(rs * isc / nnsvth) + isc * rs / rsh # Refine Rsh, Rs, Io and Iph in that order. counter = 1. # counter variable for parameter updating while loop, # counts iterations prevconvergeparams = OrderedDict() prevconvergeparams['state'] = 0. if graphic: h = plt.figure() if graphic: # create a new handle for the converge parameter figure convergeparamsfig = plt.figure() t14 = np.array([True]) while t14.any() and counter <= maxiter: # update rsh to match max power point using a fixed point method. tmprsh = update_rsh_fixed_pt(rsh[u], rs[u], io[u], iph[u], nnsvth[u], imp[u], vmp[u]) if graphic: ax11 = h.add_subplot(111) ax11.plot(counter, np.mean(np.abs(tmprsh - rsh[u])), 'k') ax11.hold(True) ax11.set_xlabel('Iteration') ax11.set_ylabel('mean(abs(tmprsh[u] - rsh[u]))') ax11.set_title('Update Rsh') plt.show() rsh[u] = tmprsh # Calculate Rs to be consistent with Rsh and maximum power point [a, phi] = calc_theta_phi_exact(imp[u], iph[u], vmp[u], io[u], nnsvth[u], rs[u], rsh[u]) rs[u] = (iph[u] + io[u] - imp[u]) * rsh[u] / imp[u] - \ nnsvth[u] * phi / imp[u] - vmp[u] / imp[u] # Update filter for good parameters u = filter_params(io, rsh, rs, ee, isc) # Update value for io to match voc tmpio = update_io_known_n(rsh[u], rs[u], nnsvth[u], io[u], iph[u], voc[u]) io[u] = tmpio # Calculate Iph to be consistent with Isc and other parameters iph = isc - io + io * np.exp(rs * isc / nnsvth) + isc * rs / rsh # update filter for good parameters u = filter_params(io, rsh, rs, ee, isc) # compute the IV curve from the current parameter values result = singlediode(iph[u], io[u], rs[u], rsh[u], nnsvth[u]) # check convergence criteria # [5] Step 3d if graphic: convergeparams = check_converge(prevconvergeparams, result, vmp[u], imp[u], graphic, convergeparamsfig, counter) else: convergeparams = check_converge(prevconvergeparams, result, vmp[u], imp[u], graphic, 0., counter) prevconvergeparams = convergeparams counter += 1. t5 = prevconvergeparams['vmperrmeanchange'] >= eps1 t6 = prevconvergeparams['imperrmeanchange'] >= eps1 t7 = prevconvergeparams['pmperrmeanchange'] >= eps1 t8 = prevconvergeparams['vmperrstdchange'] >= eps1 t9 = prevconvergeparams['imperrstdchange'] >= eps1 t10 = prevconvergeparams['pmperrstdchange'] >= eps1 t11 = prevconvergeparams['vmperrabsmaxchange'] >= eps1 t12 = prevconvergeparams['imperrabsmaxchange'] >= eps1 t13 = prevconvergeparams['pmperrabsmaxchange'] >= eps1 t14 = np.logical_or(t5, t6) t14 = np.logical_or(t14, t7) t14 = np.logical_or(t14, t8) t14 = np.logical_or(t14, t9) t14 = np.logical_or(t14, t10) t14 = np.logical_or(t14, t11) t14 = np.logical_or(t14, t12) t14 = np.logical_or(t14, t13) # Extract coefficients for auxillary equations # Estimate Io0 and eG tok = const['T0'] + 273.15 # convert to to K x = const['q'] / const['k'] * (1. / tok - 1. / tck[u]) / gamma[u] y = np.log(io[u]) - 3. * np.log(tck[u] / tok) new_x = sm.add_constant(x) res = sm.RLM(y, new_x).fit() beta = res.params io0 = np.exp(beta[0]) eg = beta[1] if graphic: # Predict Io and Eg pio = io0 * ((tc[u] + 273.15) / const['T0'] + 273.15) ** 3. * \ np.exp((const['q'] / const['k']) * (eg / gamma[u]) * (1. / (const['T0'] + 273.15) - 1. / (tc[u] + 273.15))) iofig = plt.figure() ax12 = iofig.add_subplot(311) ax13 = iofig.add_subplot(312) ax14 = iofig.add_subplot(313) ax12.hold(True) ax12.plot(tc[u], y, 'r+', tc[u], beta[0] + x * beta[1], 'b.') ax12.set_xlabel('Cell temp. (C)') ax12.set_ylabel('log(Io)-3log(T_C/T_0)') ax12.legend(['Data', 'Model'], loc=2) ax13.hold(True) ax13.plot(tc[u], io[u], 'r+', tc[u], pio, '.') ax13.set_xlabel('Cell temp. (C)') ax13.set_ylabel('I_O (A)') ax13.legend(['Extracted', 'Predicted'], loc=2) ax14.hold(True) ax14.plot(tc[u], (pio - io[u]) / io[u] * 100., 'x') ax14.set_xlabel('Cell temp. (C)') ax14.set_ylabel('Percent Deviation in I_O') iofig1 = plt.figure() ax15 = iofig1.add_subplot(111) ax15.hold(True) ax15.plot(tc[u], y + 3. * (tc[u] / const['T0']), 'k.', tc[u], beta[0] + x * beta[1] + 3 * (tc[u] / const['T0']), 'g.') ax15.set_xlabel('Cell temp. (C)') ax15.set_ylabel('log(Io)-3log(T_C/T_0)') ax15.legend(['Data', 'Regression Model'], loc=2) iofig2 = plt.figure() ax16 = iofig2.add_subplot(111) ax16.hold(True) ax16.plot(tc[u], io[u], 'b+', tc[u], pio, 'r.') ax16.set_xlabel('Cell temp. (C)') ax16.set_ylabel('I_O (A)') ax16.legend(['Extracted from IV Curves', 'Predicted by Eq. 3'], loc=2) ax16.text(np.min(tc[u]), np.min(io[u]) + .83 * (np.max(io[u]) - np.min(io[u])), ['I_{O0} = %s' % io0]) ax16.text(np.min(tc[u]), np.min(io[u]) + .83 * (np.max(io[u]) - np.min(io[u])), ['eG = %s' % eg]) # Estimate Iph0 x = tc[u] - const['T0'] y = iph[u] * (const['E0'] / ee[u]) # average over non-NaN values of Y and X nans = np.isnan(y - specs['aisc'] * x) iph0 = np.mean(y[~nans] - specs['aisc'] * x[~nans]) if graphic: # Predict Iph piph = (ee[u] / const['E0']) * (iph0 + specs['aisc'] * (tc[u] - const['T0'])) iphfig = plt.figure() ax17 = iphfig.add_subplot(311) ax18 = iphfig.add_subplot(312) ax19 = iphfig.add_subplot(313) ax17.hold(True) ax17.plot(ee[u], piph, 'r+', [0., np.max(ee[u])], [iph0, iph0]) ax17.set_xlabel('Irradiance (W/m^2)') ax17.set_ylabel('I_L') ax17.legend(['Data', 'I_L at STC'], loc=4) ax18.hold(True) ax18.plot(ee[u], iph[u], 'r+', ee[u], piph, '.') ax18.set_xlabel('Irradiance (W/m^2)') ax18.set_ylabel('I_L (A)') ax18.legend(['Extracted', 'Predicted'], loc=2) ax19.hold(True) ax19.plot(ee[u], (piph - iph[u]) / iph[u] * 100., 'x', [np.min(ee[u]), np.max(ee[u])], [0., 0.]) ax19.set_xlabel('Irradiance (W/m^2)') ax19.set_ylabel('Percent Deviation from I_L') iphfig1 = plt.figure() ax20 = iphfig1.add_subplot(111) ax20.hold(True) ax20.plot(tc[u], iph[u], 'b+', tc[u], piph, 'r.', [0., 80.], [iph0, iph0]) ax20.set_xlabel('Cell temp. (C)') ax20.set_ylabel('I_L (W/m^2)') ax20.legend(['Extracted from IV Curves', 'Predicted by Eq. 2', 'I_L at STC'], loc=2) ax20.text(1.1 * np.min(tc[u]), 1.05 * iph0, ['I_{L0} = %s' % iph0]) # Additional filter for Rsh and Rs; Restrict effective irradiance to be # greater than 400 W/m^2 vfil = ee > 400 # Estimate Rsh0, Rsh_ref and Rshexp # Initial Guesses. Rsh0 is value at Ee=0. nans = np.isnan(rsh) if any(ee < 400): grsh0 = np.mean(rsh[np.logical_and(~nans, ee < 400)]) else: grsh0 = np.max(rsh) # Rsh_ref is value at Ee = 1000 if any(vfil): grshref = np.mean(rsh[np.logical_and(~nans, vfil)]) else: grshref = np.min(rsh) # PVsyst default for Rshexp is 5.5 rshexp = 5.5 # Here we use a nonlinear least squares technique. Lsqnonlin minimizes # the sum of squares of the objective function (here, tf). x0 = np.array([grsh0, grshref]) beta = optimize.least_squares(fun_rsh, x0, args=(rshexp, ee[u], const['E0'], rsh[u]), bounds=np.array([[1., 1.], [1.e7, 1.e6]]) ) # Extract PVsyst parameter values rsh0 = beta.x[0] rshref = beta.x[1] if graphic: # Predict Rsh prsh = estrsh(beta, rshexp, ee, const['E0']) rshfig = plt.figure() ax21 = rshfig.add_subplot(211) ax22 = rshfig.add_subplot(212) ax21.hold(True) ax21.plot(ee[u], np.log10(rsh[u]), 'r.', ee[u], np.log10(prsh[u]), 'b.') ax21.set_xlabel('Irradiance (W/m^2)') ax21.set_ylabel('log_{10}(R_{sh})') ax21.legend(['Extracted', 'Predicted'], loc=2) ax22.hold(True) ax22.plot(ee[u], (np.log10(prsh[u]) - np.log10(rsh[u])) / np.log10(rsh[u]) * 100., 'x', [np.min(ee[u]), np.max(ee[u])], [0., 0.]) ax22.set_xlabel('Irradiance (W/m^2)') ax22.set_ylabel('Percent Deviation in log_{10}(R_{sh})') rshfig1 = plt.figure() ax23 = rshfig1.add_subplot(111) ax23.hold(True) ax23.plot(ee[u], np.log10(rsh[u]), 'b.', ee[u], np.log10(prsh[u]), 'r.') ax23.set_xlabel('Irradiance (W/m^2)') ax23.set_ylabel('log_{10}(R_{sh})') ax23.legend(['Extracted from IV Curves', 'Predicted by Eq. 5'], loc=3) ax23.text(150, 3.65, ['R_{SH0} = %s' % rsh0]) ax23.text(150, 3.5, ['R_{SH,ref} = %s' % rshref]) ax23.text(150, 3.35, ['R_{SHexp} = %s' % rshexp]) # Estimate Rs0 t15 = np.logical_and(u, vfil) rs0 = np.mean(rs[t15]) if graphic: rsfig = plt.figure() ax24 = rsfig.add_subplot(211) ax25 = rsfig.add_subplot(212) ax24.hold(True) ax24.plot(ee[t15], rs[t15], 'r.', ee[t15], rs0 * np.ones(len(ee[t15])), 'b.') ax24.set_xlabel('Irradiance (W/m^2)') ax24.set_ylabel('R_S') ax24.legend(['R_S values', 'Model']) ax24.set_xlim([0, 1]) ax24.set_ylin([0, 1200]) ax25.hold(True) ax25.plot(ee[u], (rs0 - rs[u]) / rs[u] * 100., 'x', [np.min(ee[u]), np.max(ee[u])], [0., 0.]) ax25.set_xlabel('Irradiance (W/m^2)') ax25.set_ylabel('Percent Deviation in R_S') rsfig1 = plt.figure() ax26 = rsfig1.add_subplot(111) ax26.hold(True) ax26.plot(ee[t15], rs[t15], 'b.', [0., np.max(ee[u])], [rs0, rs0], 'r') ax26.set_xlabel('Irradiance (W/m^2)') ax26.set_ylabel('R_S') ax26.legend(['Extracted from IV Curves', 'Predicted by Eq. 7'], loc=3) ax26.text(800, 1.2 * rs0, ['R_{S0} = %s' % rs0]) # Save parameter estimates in output structure pvsyst['IL_ref'] = iph0 pvsyst['Io_ref'] = io0 pvsyst['eG'] = eg pvsyst['Rs_ref'] = rs0 pvsyst['gamma_ref'] = gamma_ref pvsyst['mugamma'] = mugamma pvsyst['Iph'] = iph pvsyst['Io'] = io pvsyst['Rsh0'] = rsh0 pvsyst['Rsh_ref'] = rshref pvsyst['Rshexp'] = rshexp pvsyst['Rs'] = rs pvsyst['Rsh'] = rsh pvsyst['Ns'] = specs['ns'] pvsyst['u'] = u oflag = True else: oflag = False pvsyst['IL_ref'] = float("Nan") pvsyst['Io_ref'] = float("Nan") pvsyst['eG'] = float("Nan") pvsyst['Rs_ref'] = float("Nan") pvsyst['gamma_ref'] = float("Nan") pvsyst['mugamma'] = float("Nan") pvsyst['Iph'] = float("Nan") pvsyst['Io'] = float("Nan") pvsyst['Rsh0'] = float("Nan") pvsyst['Rsh_ref'] = float("Nan") pvsyst['Rshexp'] = float("Nan") pvsyst['Rs'] = float("Nan") pvsyst['Rsh'] = float("Nan") pvsyst['Ns'] = specs['ns'] pvsyst['u'] = np.zeros(n) return pvsyst, oflag