Source code for pvpltools.power_conversion

"""
This module provides useful functions related to power conversion for
PV systems, including a flexible implementation of the ADR power loss
model, aka ADR inverter model.

Copyright (c) 2021 Anton Driesse, PV Performance Labs.
"""

import numpy as np
import pandas as pd

from scipy.optimize import curve_fit

from pvlib.inverter import _sandia_eff


[docs] def adr_converter_core(v_norm, p_norm, b): ''' Calculate power loss in a power converter for the given normalized operating conditions and the ADR model "b" coefficients. The ADR model input and output quantities are normalized using a nominal voltage and power level, allowing the converter characteristics to be scaled up or down easily. Voltage and current values may correspond to electrical inputs or outputs, and may be DC or AC, hence different types of converters can be modeled. However in general a set of coefficients is only valid for a specific type. Parameters ---------- v_norm, p_norm : numeric Normalized voltage and power, unitless. These arguments must be "broadcastable" to the same shape. b : 1-D or 2-D Nine model "b" coefficients as described in [1]. Returns ------- p_loss_norm : numeric Normalized power loss, unitless. References ---------- .. [1] A. Driesse, "Beyond the Curves: Modeling the Electrical Efficiency of Photovoltaic Inverters", 33rd IEEE Photovoltaic Specialist Conference (PVSC), June 2008 Author: Anton Driesse, PV Performance Labs ''' v_norm = np.asanyarray(v_norm) p_norm = np.asanyarray(p_norm) # ensure the coefficients have a 3x3 shape b = np.reshape(b, (3, 3)) # calculate the voltage terms with np.errstate(divide='ignore'): v_terms = np.stack([v_norm ** 0, v_norm - 1, 1 / v_norm - 1]) v_terms[np.isinf(v_terms)] = 0.0 # calculate the power term coefficients a0, a1, a2 a = np.einsum('v...,vp->p...', v_terms, b) # calculate the power terms and the power losses p_terms = np.stack([p_norm ** 0, p_norm, p_norm ** 2]) p_loss_norm = np.einsum('p...,p...->...', p_terms, a) return p_loss_norm
[docs] def fit_adr_converter_core(v_norm, p_norm, p_loss_norm, method='trf', **kwargs): ''' Determine the ADR model "b" coefficients for power converter losses. This is a convenience function that calls the scipy `curve_fit` function with suitable parameters and defaults. Parameters ---------- v_norm, p_norm, p_loss_norm : numeric Normalized voltage, power and power loss, unitless. These arguments must be "broadcastable" to the same shape. method : {'lm', 'trf', 'dogbox'}, optional Method to use for optimization. See `least_squares` for more details. Default is 'trf'. kwargs : Optional keyword arguments passed to `curve_fit`. Returns ------- b : 1-D array Nine optimal values for the model "b" coefficients. pcov : 2-D array The estimated covariance of b. See `curve_fit` for details. Author: Anton Driesse, PV Performance Labs ''' def model_wrapper(xdata, *params): return adr_converter_core(*xdata, params) # broadcast the inputs first, then flatten them for use with curve_fit v_n, p_n, p_loss_n = np.broadcast_arrays(v_norm, p_norm, p_loss_norm) xdata = np.stack([np.ravel(v_n), np.ravel(p_n)]) ydata = np.ravel(p_loss_n) # the coefficient matrix must also be flat b0 = np.array([0.01, 0.01, 0.01, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00]) b, pcov = curve_fit(model_wrapper, xdata, ydata, p0=b0, method=method, **kwargs) return b.reshape(3, 3), pcov
[docs] def create_cec_matrix_sandia(inverter): ''' Create a matrix of operating points that approximates the cec inverter test matrix using the given Sandia inverter model parameters. Author: Anton Driesse, PV Performance Labs ''' CEC_LEVELS = [0.1, 0.2, 0.30, 0.50, 0.75, 1.00] p_dc = inverter.Pdco * np.array(CEC_LEVELS) v_dc = [inverter.Mppt_low, inverter.Vdco, inverter.Mppt_high] v_dc, p_dc = np.meshgrid(v_dc, p_dc) p_ac = _sandia_eff(v_dc.flatten(), p_dc.flatten(), inverter) return v_dc, p_dc, p_ac.reshape(v_dc.shape)
[docs] def fit_adr_to_sandia(snl_params): ''' Create an ADR inverter parameter set that is equivalent to the given Sandia inverter model. Author: Anton Driesse, PV Performance Labs ''' ADR_KEYS = ['Manufacturer', 'Model', 'Source', 'Vac', 'Vintage', 'Pacmax', 'Pnom', 'Vnom', 'Vmin', 'Vmax', 'ADRCoefficients', 'Pnt', 'Vdcmax', 'Idcmax', 'MPPTLow', 'MPPTHi', 'TambLow', 'TambHi', 'Weight'] SNL_SOURCES = ['Vac', 'Pnt', 'Vdcmax', 'Idcmax', 'Mppt_low', 'Mppt_high', 'Paco', 'Pdco', 'Vdco', 'Mppt_low', 'Mppt_high'] ADR_TARGETS = ['Vac', 'Pnt', 'Vdcmax', 'Idcmax', 'MPPTLow', 'MPPTHi', 'Pacmax', 'Pnom', 'Vnom', 'Vmin', 'Vmax'] # create a skeleton Series to receive the ADR parameter values adr_params = pd.Series(index=ADR_KEYS, dtype=object) adr_params.name = snl_params.name adr_params.Manufacturer, adr_params.Model, __ = snl_params.name.split('__') # fill in values that are available in the source for ks, ka in zip(SNL_SOURCES, ADR_TARGETS): adr_params[ka] = snl_params[ks] # round off the nominal power p_nom = adr_params['Pnom'] decimals = min(0, 2 - int(np.log10(p_nom) + 0.5)) adr_params['Pnom'] = np.round(p_nom, decimals) # create a set of operating points for fitting the ADR model v_dc, p_dc, p_ac = create_cec_matrix_sandia(snl_params) # normalize the operating points prior to fitting p_loss = (p_dc - p_ac) / adr_params.Pnom p_dc = p_dc / adr_params.Pnom v_dc = v_dc / adr_params.Vnom # fit b, __ = fit_adr_converter_core(v_dc, p_dc, p_loss) # round off the result adr_params['ADRCoefficients'] = b.round(5).ravel().tolist() return adr_params