|
13 | 13 | import numpy as np |
14 | 14 | import pandas as pd |
15 | 15 | from numpy.polynomial.polynomial import polyfit # different than np.polyfit |
| 16 | +from scipy.optimize import minimize |
| 17 | +import statsmodels.api as sm |
| 18 | +from pvlib._deprecation import deprecated |
16 | 19 |
|
17 | 20 |
|
18 | 21 | def _sandia_eff(v_dc, p_dc, inverter): |
@@ -445,9 +448,10 @@ def pvwatts_multi(pdc, pdc0, eta_inv_nom=0.96, eta_inv_ref=0.9637): |
445 | 448 | return pvwatts(sum(pdc), pdc0, eta_inv_nom, eta_inv_ref) |
446 | 449 |
|
447 | 450 |
|
448 | | -def fit_sandia(ac_power, dc_power, dc_voltage, dc_voltage_level, p_ac_0, p_nt): |
| 451 | +def fit_sandia_lab(ac_power, dc_power, dc_voltage, dc_voltage_level, p_ac_0, |
| 452 | + p_nt): |
449 | 453 | r''' |
450 | | - Determine parameters for the Sandia inverter model. |
| 454 | + Determine parameters for the Sandia inverter model from laboratory data. |
451 | 455 |
|
452 | 456 | Parameters |
453 | 457 | ---------- |
@@ -551,3 +555,162 @@ def extract_c(x_d, add): |
551 | 555 | # prepare dict and return |
552 | 556 | return {'Paco': p_ac_0, 'Pdco': p_dc0, 'Vdco': v_nom, 'Pso': p_s0, |
553 | 557 | 'C0': c0, 'C1': c1, 'C2': c2, 'C3': c3, 'Pnt': p_nt} |
| 558 | + |
| 559 | + |
| 560 | +fit_sandia = deprecated(since="0.15.3", name="fit_sandia", |
| 561 | + alternative="fit_sandia_lab")(fit_sandia_lab) |
| 562 | + |
| 563 | + |
| 564 | +def _fit_sandia_field_a(pac, pdc, vdc, pac0, vdc0): |
| 565 | + r''' Estimate Pdco, Pso and C0 for the Sandia inverter model from |
| 566 | + time-series data. Uses robust regression on data greater than 5% of rated |
| 567 | + Pac and DC voltage near nominal voltage vdc0. |
| 568 | +
|
| 569 | + Parameters |
| 570 | + ---------- |
| 571 | + pac : numeric |
| 572 | + Measured AC power (W) |
| 573 | + pdc : numeric |
| 574 | + Measured DC input power (W) |
| 575 | + vdc |
| 576 | + Measured DC input voltage (V) |
| 577 | + pac0 : float |
| 578 | + Rated AC power (W) |
| 579 | + vdc0 : float |
| 580 | + Nominal DC input voltage (V) |
| 581 | +
|
| 582 | + Returns |
| 583 | + ------- |
| 584 | + dict |
| 585 | + Parameters Pdco, Pso and C0 for the Sandia inverter model. |
| 586 | + ''' |
| 587 | + # select data. Avoid very low power, clipping and DC voltage far from |
| 588 | + # nominal |
| 589 | + u = (pac > 0.05*pac0) & (pac < pac0) & (np.abs(vdc - vdc0)/vdc0 < 0.05) |
| 590 | + Y = pac[u] |
| 591 | + X = np.array([pdc[u]**2, pdc[u]]).T |
| 592 | + X = sm.add_constant(X) |
| 593 | + rlm_model = sm.RLM(Y, X) |
| 594 | + rlm_results = rlm_model.fit() |
| 595 | + rlmp = np.array(rlm_results.params) |
| 596 | + |
| 597 | + p = {} |
| 598 | + p['C0'] = rlmp[1] |
| 599 | + p['Pso'] = (-rlmp[2] + np.sqrt(rlmp[2]**2 - 4*p['C0']*rlmp[0])) / \ |
| 600 | + (2 * p['C0']) |
| 601 | + C = pac0 + rlmp[2]*p['Pso'] + p['C0']*p['Pso']**2 |
| 602 | + p['Pdco'] = (-rlmp[2] + np.sqrt(rlmp[2]**2 + 4*p['C0']*C)) / (2. * p['C0']) |
| 603 | + return p |
| 604 | + |
| 605 | + |
| 606 | +def _f2(params, pac0, vdc0, pdc0, ps0, C0, vdc, pdc, pac): |
| 607 | + # objective function for _fit_sandia_field. Assumes Pdco, Pso and C0 |
| 608 | + # are known. Returns the root sum of squared differences in AC power |
| 609 | + # Input params = [C1, C2, C3] |
| 610 | + p = {} |
| 611 | + p['Paco'] = pac0 # AC power |
| 612 | + p['Vdco'] = vdc0 # DC V |
| 613 | + p['Pdco'] = pdc0 # DC power |
| 614 | + p['Pso'] = ps0 # DC power, small |
| 615 | + p['C0'] = C0 # unitless, tiny |
| 616 | + p['C1'] = params[0] # 1/V, tiny |
| 617 | + p['C2'] = params[1] # 1/V, tiny |
| 618 | + p['C3'] = params[2] # 1/V tiny |
| 619 | + diff = (_sandia_eff(vdc, pdc, p) - pac) |
| 620 | + return np.sqrt(np.dot(diff, diff)) |
| 621 | + |
| 622 | + |
| 623 | +def _fit_sandia_field_b(resid, pac, pdc, vdc, pac0, vdc0, pdc0, ps0, C0): |
| 624 | + r''' Estimate C1, C2, and C3 for the Sandia inverter model from time-series |
| 625 | + data. Estimates are conditional on parameters Pdco, Pso and C0. |
| 626 | +
|
| 627 | + Parameters |
| 628 | + ---------- |
| 629 | + pac : numeric |
| 630 | + Measured AC power (W) |
| 631 | + pdc : numeric |
| 632 | + Measured DC input power (W) |
| 633 | + vdc |
| 634 | + Measured DC input voltage (V) |
| 635 | + pac0 : float |
| 636 | + Rated AC power (W) |
| 637 | + vdc0 : float |
| 638 | + Nominal DC input voltage (V) |
| 639 | + pdc0 : float |
| 640 | + DC input power that produces rated AC power at nominal voltage (W) |
| 641 | + ps0 : float |
| 642 | + Start-up DC power (W) |
| 643 | + C0 : float |
| 644 | + Empirical coefficient |
| 645 | +
|
| 646 | + Returns |
| 647 | + ------- |
| 648 | + dict |
| 649 | + Parameters for the Sandia inverter model including C1, C2 and C3. |
| 650 | + ''' |
| 651 | + # select data. Avoid very low power and clipping |
| 652 | + u = (pac > 0.05*pac0) & (pac < pac0) |
| 653 | + |
| 654 | + # initial guess |
| 655 | + x0 = np.array([0., 0., 0.]) |
| 656 | + |
| 657 | + args = (pac0, vdc0, pdc0, ps0, C0, vdc[u], pdc[u], pac[u]) |
| 658 | + options = {} |
| 659 | + options['gtol'] = 1e-5 |
| 660 | + result = minimize(_f2, x0, args=args, |
| 661 | + method='BFGS', |
| 662 | + options=options) |
| 663 | + |
| 664 | + params = result.x |
| 665 | + p = {} |
| 666 | + p['Paco'] = pac0 |
| 667 | + p['Vdco'] = vdc0 |
| 668 | + p['Pdco'] = pdc0 |
| 669 | + p['Pso'] = ps0 |
| 670 | + p['C0'] = C0 |
| 671 | + p['C1'] = params[0] |
| 672 | + p['C2'] = params[1] |
| 673 | + p['C3'] = params[2] |
| 674 | + |
| 675 | + return p |
| 676 | + |
| 677 | + |
| 678 | +def fit_sandia_field(pac, pdc, vdc, pac0, vdc0): |
| 679 | + r''' Estimate parameters for the Sandia inverter model from time-series |
| 680 | + data. |
| 681 | +
|
| 682 | + Parameters |
| 683 | + ---------- |
| 684 | + pac : numeric |
| 685 | + Measured AC power (W) |
| 686 | + pdc : numeric |
| 687 | + Measured DC input power (W) |
| 688 | + vdc |
| 689 | + Measured DC input voltage (V) |
| 690 | + pac0 : float |
| 691 | + Rated AC power (W) |
| 692 | + vdc0 : float |
| 693 | + Nominal DC input voltage (V) |
| 694 | +
|
| 695 | + Returns |
| 696 | + ------- |
| 697 | + dict |
| 698 | + Parameters for the Sandia inverter model. |
| 699 | +
|
| 700 | + References |
| 701 | + ---------- |
| 702 | + .. [1] C. W. Hansen, K. S. Anderson, M. Theristis, "Fitting the Sandia |
| 703 | + Inverter Model to Operational PV System Data", 54 IEEE Photovoltaic |
| 704 | + Specialist Conference, New Orleans, USA. 2026 |
| 705 | + .. [2] D. King, S. Gonzalez, G. Galbraith, W. Boyson, "Performance Model |
| 706 | + for Grid-Connected Photovoltaic Inverters", Sandia National |
| 707 | + Laboratories, Albuquerque, N.M., USA, SAND2007-5036, Sept. 2007. |
| 708 | + :doi:`10.2172/920449` |
| 709 | +
|
| 710 | + ''' |
| 711 | + # get Pdco, Pso and C0 first |
| 712 | + p = _fit_sandia_field_a(pac, pdc, vdc, pac0, vdc0) |
| 713 | + # add C1, C2, C3 |
| 714 | + p = _fit_sandia_field_b(_f2, pac, pdc, vdc, pac0, vdc0, |
| 715 | + p['Pdco'], p['Pso'], p['C0']) |
| 716 | + return p |
0 commit comments