|
7 | 7 | """ |
8 | 8 |
|
9 | 9 | import numpy as np |
| 10 | +import pandas as pd |
10 | 11 |
|
11 | 12 | from scipy import constants |
12 | 13 | from scipy import optimize |
13 | 14 | from scipy.special import lambertw |
14 | 15 |
|
15 | | -from pvlib.pvsystem import calcparams_pvsyst, singlediode, v_from_i |
| 16 | +from pvlib.pvsystem import (calcparams_pvsyst, calcparams_cec, singlediode, |
| 17 | + v_from_i) |
16 | 18 | from pvlib.singlediode import bishop88_mpp |
17 | 19 |
|
18 | 20 | from pvlib.ivtools.utils import rectify_iv_curve, _numdiff |
|
24 | 26 | CONSTANTS = {'E0': 1000.0, 'T0': 25.0, 'k': constants.k, 'q': constants.e} |
25 | 27 |
|
26 | 28 |
|
| 29 | +IEC61853 = pd.DataFrame( |
| 30 | + columns=['effective_irradiance', 'temp_cell'], |
| 31 | + data = np.array( |
| 32 | + [[100, 100, 100, 100, 200, 200, 200, 200, 400, 400, 400, 400, |
| 33 | + 600, 600, 600, 600, 800, 800, 800, 800, 1000, 1000, 1000, 1000, |
| 34 | + 1100, 1100, 1100, 1100], |
| 35 | + [15, 25, 50, 75, 15, 25, 50, 75, 15, 25, 50, 75, 15, 25, 50, 75, |
| 36 | + 15, 25, 50, 75, 15, 25, 50, 75, 15, 25, 50, 75]]).T, |
| 37 | + dtype=np.float64) |
| 38 | + |
| 39 | + |
27 | 40 | def fit_cec_sam(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, |
28 | 41 | gamma_pmp, cells_in_series, temp_ref=25): |
29 | 42 | """ |
@@ -1354,3 +1367,278 @@ def maxp(temp_cell, irrad_ref, alpha_sc, gamma_ref, mu_gamma, I_L_ref, |
1354 | 1367 | gamma_pdc = _first_order_centered_difference(maxp, x0=temp_ref, args=args) |
1355 | 1368 |
|
1356 | 1369 | return gamma_pdc / pmp |
| 1370 | + |
| 1371 | + |
| 1372 | +def _pvsyst_objfun(pvs_mod, cec_ivs, ee, tc, cs): |
| 1373 | + |
| 1374 | + # translate the guess into named args that are used in the functions |
| 1375 | + # order : [alpha_sc, gamma_ref, mu_gamma, I_L_ref, I_o_ref, |
| 1376 | + # R_sh_mult, R_sh_ref, R_s] |
| 1377 | + # cec_ivs : DataFrame with columns i_sc, v_oc, i_mp, v_mp, p_mp |
| 1378 | + # ee : effective irradiance |
| 1379 | + # tc : cell temperature |
| 1380 | + # cs : cells in series |
| 1381 | + alpha_sc = pvs_mod[0] |
| 1382 | + gamma_ref = pvs_mod[1] |
| 1383 | + mu_gamma = pvs_mod[2] |
| 1384 | + I_L_ref = pvs_mod[3] |
| 1385 | + I_o_ref = pvs_mod[4] |
| 1386 | + R_sh_mult = pvs_mod[5] |
| 1387 | + R_sh_ref = pvs_mod[6] |
| 1388 | + R_s = pvs_mod[7] |
| 1389 | + |
| 1390 | + R_sh_0 = R_sh_ref * R_sh_mult |
| 1391 | + |
| 1392 | + pvs_params = calcparams_pvsyst( |
| 1393 | + ee, tc, alpha_sc, gamma_ref, mu_gamma, I_L_ref, I_o_ref, R_sh_ref, |
| 1394 | + R_sh_0, R_s, cs) |
| 1395 | + |
| 1396 | + pvsyst_ivs = singlediode(*pvs_params) |
| 1397 | + |
| 1398 | + isc_diff = np.abs((pvsyst_ivs['i_sc'] - cec_ivs['i_sc']) / |
| 1399 | + cec_ivs['i_sc']).mean() |
| 1400 | + imp_diff = np.abs((pvsyst_ivs['i_mp'] - cec_ivs['i_mp']) / |
| 1401 | + cec_ivs['i_mp']).mean() |
| 1402 | + voc_diff = np.abs((pvsyst_ivs['v_oc'] - cec_ivs['v_oc']) / |
| 1403 | + cec_ivs['v_oc']).mean() |
| 1404 | + vmp_diff = np.abs((pvsyst_ivs['v_mp'] - cec_ivs['v_mp']) / |
| 1405 | + cec_ivs['v_mp']).mean() |
| 1406 | + pmp_diff = np.abs((pvsyst_ivs['p_mp'] - cec_ivs['p_mp']) / |
| 1407 | + cec_ivs['p_mp']).mean() |
| 1408 | + |
| 1409 | + mean_abs_diff = (isc_diff + imp_diff + voc_diff + vmp_diff + pmp_diff) / 5 |
| 1410 | + |
| 1411 | + return mean_abs_diff |
| 1412 | + |
| 1413 | + |
| 1414 | +def convert_cec_pvsyst(cec_model, cells_in_series): |
| 1415 | + r""" |
| 1416 | + Convert a CEC model to a PVsyst model. |
| 1417 | +
|
| 1418 | + Uses optimization to fit the PVsyst model to :math:`I_{sc}`, |
| 1419 | + :math:`V_{oc}`, :math:`V_{mp}`, :math:`I_{mp}`, and :math:`P_{mp}`, |
| 1420 | + calculated using the input CEC model at the IEC 61853-3 conditions [2]_. |
| 1421 | +
|
| 1422 | + Parameters |
| 1423 | + ---------- |
| 1424 | + cec_model : dict or DataFrame |
| 1425 | + Must include keys: 'alpha_sc', 'a_ref', 'I_L_ref', 'I_o_ref', |
| 1426 | + 'R_sh_ref', 'R_s', 'Adjust' |
| 1427 | + cell_in_series : int |
| 1428 | + Number of cells in series. |
| 1429 | +
|
| 1430 | + Returns |
| 1431 | + ------- |
| 1432 | + dict with the following elements: |
| 1433 | + alpha_sc : float |
| 1434 | + Short-circuit current temperature coefficient [A/C] . |
| 1435 | + I_L_ref : float |
| 1436 | + The light-generated current (or photocurrent) at reference |
| 1437 | + conditions [A]. |
| 1438 | + I_o_ref : float |
| 1439 | + The dark or diode reverse saturation current at reference |
| 1440 | + conditions [A]. |
| 1441 | + EgRef : float |
| 1442 | + The energy bandgap at reference temperature [eV]. |
| 1443 | + R_s : float |
| 1444 | + The series resistance at reference conditions [ohm]. |
| 1445 | + R_sh_ref : float |
| 1446 | + The shunt resistance at reference conditions [ohm]. |
| 1447 | + R_sh_0 : float |
| 1448 | + Shunt resistance at zero irradiance [ohm]. |
| 1449 | + R_sh_exp : float |
| 1450 | + Exponential factor defining decrease in shunt resistance with |
| 1451 | + increasing effective irradiance [unitless]. |
| 1452 | + gamma_ref : float |
| 1453 | + Diode (ideality) factor at reference conditions [unitless]. |
| 1454 | + mu_gamma : float |
| 1455 | + Temperature coefficient for diode (ideality) factor at reference |
| 1456 | + conditions [1/K]. |
| 1457 | + cells_in_series : int |
| 1458 | + Number of cells in series. |
| 1459 | +
|
| 1460 | + Notes |
| 1461 | + ----- |
| 1462 | + Reference conditions are irradiance of 1000 W/m⁻² and cell temperature of |
| 1463 | + 25 °C. |
| 1464 | +
|
| 1465 | + References |
| 1466 | + ---------- |
| 1467 | + .. [1] L. Deville et al., "Parameter Translation for Photovoltaic Single |
| 1468 | + Diode Models", submitted. 2024 |
| 1469 | +
|
| 1470 | + .. [2] "IEC 61853-3 Photovoltaic (PV) module performance testing and energy |
| 1471 | + rating - Part 3: Energy rating of PV modules". IEC, Geneva, 2018. |
| 1472 | + """ |
| 1473 | + |
| 1474 | + # calculate target IV curve values |
| 1475 | + cec_params = calcparams_cec( |
| 1476 | + IEC61853['effective_irradiance'], |
| 1477 | + IEC61853['temp_cell'], |
| 1478 | + **cec_model) |
| 1479 | + cec_ivs = singlediode(*cec_params) |
| 1480 | + |
| 1481 | + # initial guess at PVsyst parameters |
| 1482 | + # Order in list is alpha_sc, gamma_ref, mu_gamma, I_L_ref, I_o_ref, |
| 1483 | + # Rsh_mult = R_sh_0 / R_sh_ref, R_sh_ref, R_s |
| 1484 | + initial = [0, 1.2, 0.001, cec_model['I_L_ref'], cec_model['I_o_ref'], |
| 1485 | + 12, 1000, cec_model['R_s']] |
| 1486 | + |
| 1487 | + # bounds for PVsyst parameters |
| 1488 | + b_alpha = (-1, 1) |
| 1489 | + b_gamma = (1, 2) |
| 1490 | + b_mu = (-1, 1) |
| 1491 | + b_IL = (1e-12, 100) |
| 1492 | + b_Io = (1e-24, 0.1) |
| 1493 | + b_Rmult = (1, 20) |
| 1494 | + b_Rsh = (100, 1e6) |
| 1495 | + b_Rs = (1e-12, 10) |
| 1496 | + bounds = [b_alpha, b_gamma, b_mu, b_IL, b_Io, b_Rmult, b_Rsh, b_Rs] |
| 1497 | + |
| 1498 | + # optimization to find PVsyst parameters |
| 1499 | + result = optimize.minimize( |
| 1500 | + _pvsyst_objfun, initial, |
| 1501 | + args=(cec_ivs, IEC61853['effective_irradiance'], |
| 1502 | + IEC61853['temp_cell'], cells_in_series), |
| 1503 | + method='Nelder-Mead', bounds=bounds, |
| 1504 | + options={'maxiter': 5000, 'maxfev': 5000, 'xatol': 0.001}) |
| 1505 | + alpha_sc, gamma, mu_gamma, I_L_ref, I_o_ref, Rsh_mult, R_sh_ref, R_s = \ |
| 1506 | + result.x |
| 1507 | + |
| 1508 | + R_sh_0 = Rsh_mult * R_sh_ref |
| 1509 | + R_sh_exp = 5.5 |
| 1510 | + EgRef = 1.121 # default for all modules in the CEC model |
| 1511 | + return {'alpha_sc': alpha_sc, |
| 1512 | + 'I_L_ref': I_L_ref, 'I_o_ref': I_o_ref, 'EgRef': EgRef, 'R_s': R_s, |
| 1513 | + 'R_sh_ref': R_sh_ref, 'R_sh_0': R_sh_0, 'R_sh_exp': R_sh_exp, |
| 1514 | + 'gamma_ref': gamma, 'mu_gamma': mu_gamma, |
| 1515 | + 'cells_in_series': cells_in_series, |
| 1516 | + } |
| 1517 | + |
| 1518 | + |
| 1519 | +def _cec_objfun(cec_mod, pvs_ivs, ee, tc, alpha_sc): |
| 1520 | + # translate the guess into named args that are used in the functions |
| 1521 | + # order : [I_L_ref, I_o_ref, a_ref, R_sh_ref, R_s, alpha_sc, Adjust] |
| 1522 | + # pvs_ivs : DataFrame with columns i_sc, v_oc, i_mp, v_mp, p_mp |
| 1523 | + # ee : effective irradiance |
| 1524 | + # tc : cell temperature |
| 1525 | + # alpha_sc : temperature coefficient for Isc |
| 1526 | + I_L_ref = cec_mod[0] |
| 1527 | + I_o_ref = cec_mod[1] |
| 1528 | + a_ref = cec_mod[2] |
| 1529 | + R_sh_ref = cec_mod[3] |
| 1530 | + R_s = cec_mod[4] |
| 1531 | + Adjust = cec_mod[5] |
| 1532 | + alpha_sc = alpha_sc |
| 1533 | + |
| 1534 | + cec_params = calcparams_cec( |
| 1535 | + ee, tc, alpha_sc, a_ref, I_L_ref, I_o_ref, R_sh_ref, R_s, Adjust) |
| 1536 | + cec_ivs = singlediode(*cec_params) |
| 1537 | + |
| 1538 | + isc_rss = np.sqrt(sum((cec_ivs['i_sc'] - pvs_ivs['i_sc'])**2)) |
| 1539 | + imp_rss = np.sqrt(sum((cec_ivs['i_mp'] - pvs_ivs['i_mp'])**2)) |
| 1540 | + voc_rss = np.sqrt(sum((cec_ivs['v_oc'] - pvs_ivs['v_oc'])**2)) |
| 1541 | + vmp_rss = np.sqrt(sum((cec_ivs['v_mp'] - pvs_ivs['v_mp'])**2)) |
| 1542 | + pmp_rss = np.sqrt(sum((cec_ivs['p_mp'] - pvs_ivs['p_mp'])**2)) |
| 1543 | + |
| 1544 | + mean_diff = (isc_rss+imp_rss+voc_rss+vmp_rss+pmp_rss) / 5 |
| 1545 | + |
| 1546 | + return mean_diff |
| 1547 | + |
| 1548 | + |
| 1549 | +def convert_pvsyst_cec(pvsyst_model): |
| 1550 | + r""" |
| 1551 | + Convert a PVsyst model to a CEC model. |
| 1552 | +
|
| 1553 | + Uses optimization to fit the CEC model to :math:`I_{sc}`, |
| 1554 | + :math:`V_{oc}`, :math:`V_{mp}`, :math:`I_{mp}`, and :math:`P_{mp}`, |
| 1555 | + calculated using the input PVsyst model at the IEC 61853-3 conditions [2]_. |
| 1556 | +
|
| 1557 | + Parameters |
| 1558 | + ---------- |
| 1559 | + cec_model : dict or DataFrame |
| 1560 | + Must include keys: 'alpha_sc', 'I_L_ref', 'I_o_ref', 'EgRef', 'R_s', |
| 1561 | + 'R_sh_ref', 'R_sh_0', 'R_sh_exp', 'gamma_ref', 'mu_gamma', |
| 1562 | + 'cells_in_series' |
| 1563 | +
|
| 1564 | + Returns |
| 1565 | + ------- |
| 1566 | + dict with the following elements: |
| 1567 | + I_L_ref : float |
| 1568 | + The light-generated current (or photocurrent) at reference |
| 1569 | + conditions [A]. |
| 1570 | + I_o_ref : float |
| 1571 | + The dark or diode reverse saturation current at reference |
| 1572 | + conditions [A]. |
| 1573 | + R_s : float |
| 1574 | + The series resistance at reference conditions [ohm]. |
| 1575 | + R_sh_ref : float |
| 1576 | + The shunt resistance at reference conditions [ohm]. |
| 1577 | + a_ref : float |
| 1578 | + The product of the usual diode ideality factor ``n`` (unitless), |
| 1579 | + number of cells in series ``Ns``, and cell thermal voltage at |
| 1580 | + reference conditions [V]. |
| 1581 | + Adjust : float |
| 1582 | + The adjustment to the temperature coefficient for short circuit |
| 1583 | + current, in percent. |
| 1584 | + EgRef : float |
| 1585 | + The energy bandgap at reference temperature [eV]. |
| 1586 | + dEgdT : float |
| 1587 | + The temperature dependence of the energy bandgap at reference |
| 1588 | + conditions [1/K]. |
| 1589 | +
|
| 1590 | + Notes |
| 1591 | + ----- |
| 1592 | + Reference conditions are irradiance of 1000 W/m⁻² and cell temperature of |
| 1593 | + 25 °C. |
| 1594 | +
|
| 1595 | + References |
| 1596 | + ---------- |
| 1597 | + .. [1] L. Deville et al., "Parameter Translation for Photovoltaic Single |
| 1598 | + Diode Models", submitted. 2024. |
| 1599 | +
|
| 1600 | + .. [2] "IEC 61853-3 Photovoltaic (PV) module performance testing and energy |
| 1601 | + rating - Part 3: Energy rating of PV modules". IEC, Geneva, 2018. |
| 1602 | + """ |
| 1603 | + # calculate target IV curve values |
| 1604 | + pvs_params = calcparams_pvsyst( |
| 1605 | + IEC61853['effective_irradiance'], |
| 1606 | + IEC61853['temp_cell'], |
| 1607 | + **pvsyst_model) |
| 1608 | + pvsyst_ivs = singlediode(*pvs_params) |
| 1609 | + |
| 1610 | + # set EgRef and dEgdT to CEC defaults |
| 1611 | + EgRef = 1.121 |
| 1612 | + dEgdT = -0.0002677 |
| 1613 | + |
| 1614 | + # initial guess |
| 1615 | + # order must match _pvsyst_objfun |
| 1616 | + # order : [I_L_ref, I_o_ref, a_ref, R_sh_ref, R_s, alpha_sc, Adjust] |
| 1617 | + nNsVth = pvsyst_model['gamma_ref'] * pvsyst_model['cells_in_series'] \ |
| 1618 | + * 0.025 |
| 1619 | + initial = [pvsyst_model['I_L_ref'], pvsyst_model['I_o_ref'], |
| 1620 | + nNsVth, pvsyst_model['R_sh_ref'], pvsyst_model['R_s'], |
| 1621 | + 0] |
| 1622 | + |
| 1623 | + # bounds for PVsyst parameters |
| 1624 | + b_IL = (1e-12, 100) |
| 1625 | + b_Io = (1e-24, 0.1) |
| 1626 | + b_aref = (1e-12, 1000) |
| 1627 | + b_Rsh = (100, 1e6) |
| 1628 | + b_Rs = (1e-12, 10) |
| 1629 | + b_Adjust = (-100, 100) |
| 1630 | + bounds = [b_IL, b_Io, b_aref, b_Rsh, b_Rs, b_Adjust] |
| 1631 | + |
| 1632 | + result = optimize.minimize( |
| 1633 | + _cec_objfun, initial, |
| 1634 | + args=(pvsyst_ivs, IEC61853['effective_irradiance'], |
| 1635 | + IEC61853['temp_cell'], pvsyst_model['alpha_sc']), |
| 1636 | + method='Nelder-Mead', bounds=bounds, |
| 1637 | + options={'maxiter': 5000, 'maxfev': 5000, 'xatol': 0.001}) |
| 1638 | + I_L_ref, I_o_ref, a_ref, R_sh_ref, R_s, Adjust = result.x |
| 1639 | + |
| 1640 | + return {'alpha_sc': pvsyst_model['alpha_sc'], |
| 1641 | + 'a_ref': a_ref, 'I_L_ref': I_L_ref, 'I_o_ref': I_o_ref, |
| 1642 | + 'R_sh_ref': R_sh_ref, 'R_s': R_s, 'Adjust': Adjust, |
| 1643 | + 'EgRef': EgRef, 'dEgdT': dEgdT |
| 1644 | + } |
0 commit comments