def f_optim(c): ''' Objective function. Returns the profitability of the system. Note that some global variables are used. Global variables: Demand,eta_inv,eta_bat,CapacityFactorPV,coef,CountryData, Inv, country ''' [r_PV, r_bat] = c # if the ratios are negative, set them to zero and add a penalty to the objective function penalty_PV = -1E10 * np.minimum(0, r_PV) penalty_bat = -1E10 * np.minimum(0, r_bat) r_PV = np.maximum(0, r_PV) r_bat = np.maximum(0, r_bat) E = EnergyFlows(r_PV, r_bat, Demand, eta_inv, eta_bat, CapacityFactorPV, coef) F = FinancialAnalysis(E, CountryData, Inv) #print 'PV = ' + str(r_PV) + ', BAT = ' + str(r_bat) + ', PR = ' + str(PR) + ', LCOE = ' + str(LCOE) return F['LCOE'] + penalty_PV + penalty_bat
def SCoptim(CapacityFactorPV, CountryData, Inv, coef, max_RPV=10): ''' Main optimization function :param CapacityFactorPV: Yearly capacity factor, kWh/kWp :param CountryData: Dictionary with the financial variables of the considered country :param Inv: Investment data. Defined as a dictionary with the fields 'FixedPVCost','PVCost_kW','FixedBatteryCost','BatteryCost_kWh','PVLifetime','BatteryLifetime','OM' :param coef: Coefficient of the empirical SSR function the number of coefficients depends on the version of the function :return: list with the optimal values [r_pv, r_bat, LCOE] ''' # Checking that country data is a dictionary: if not isinstance(CountryData, dict): sys.exit('CountryData must be a dictionnary in the SCoptim function') # Definition of the objective functionS of the optimization def f_optim(c): ''' Objective function. Returns the profitability of the system. Note that some global variables are used. Global variables: Demand,eta_inv,eta_bat,CapacityFactorPV,coef,CountryData, Inv, country ''' [r_PV, r_bat] = c # if the ratios are negative, set them to zero and add a penalty to the objective function penalty_PV = -1E10 * np.minimum(0, r_PV) penalty_bat = -1E10 * np.minimum(0, r_bat) r_PV = np.maximum(0, r_PV) r_bat = np.maximum(0, r_bat) E = EnergyFlows(r_PV, r_bat, Demand, eta_inv, eta_bat, CapacityFactorPV, coef) F = FinancialAnalysis(E, CountryData, Inv) #print 'PV = ' + str(r_PV) + ', BAT = ' + str(r_bat) + ', PR = ' + str(PR) + ', LCOE = ' + str(LCOE) return F['LCOE'] + penalty_PV + penalty_bat def f_optim_0(c): ''' Objective function. Returns the profitability of the system with r_bat = 0. Note that some global variables are used. Global variables: Demand,eta_inv,eta_bat,CapacityFactorPV,coef,CountryData, Inv, country ''' [r_PV] = c r_bat = 0 # if the ratios are negative, set them to zero and add a penalty to the objective function penalty_PV = -1E10 * np.minimum(0, r_PV) penalty_bat = -1E10 * np.minimum(0, r_bat) r_PV = np.maximum(0, r_PV) r_bat = np.maximum(0, r_bat) E = EnergyFlows(r_PV, r_bat, Demand, eta_inv, eta_bat, CapacityFactorPV, coef) F = FinancialAnalysis(E, CountryData, Inv) #print 'PV = ' + str(r_PV) + ', BAT = ' + str(r_bat) + ', PR = ' + str(PR) + ', LCOE = ' + str(LCOE) return F['LCOE'] + penalty_PV + penalty_bat def f_optim_fixedPV(c): ''' Objective function. Returns the profitability of the system with r_PV fixed. Note that some global variables are used. Global variables: Demand,eta_inv,eta_bat,CapacityFactorPV,coef,CountryData, Inv, country ''' r_PV = max_RPV [r_bat] = c # if the ratios are negative, set them to zero and add a penalty to the objective function penalty_PV = -1E10 * np.minimum(0, r_PV) penalty_bat = -1E10 * np.minimum(0, r_bat) r_PV = np.maximum(0, r_PV) r_bat = np.maximum(0, r_bat) E = EnergyFlows(r_PV, r_bat, Demand, eta_inv, eta_bat, CapacityFactorPV, coef) F = FinancialAnalysis(E, CountryData, Inv) #print 'PV = ' + str(r_PV) + ', BAT = ' + str(r_bat) + ', PR = ' + str(PR) + ', LCOE = ' + str(LCOE) return F['LCOE'] + penalty_PV + penalty_bat # Constraints: cons = ({ 'type': 'ineq', 'fun': lambda x: x[0] }, { 'type': 'ineq', 'fun': lambda x: x[1] }) bnds = ((0, max_RPV), (0, 10)) # Hard coded system efficiencies: eta_inv = 0.96 eta_bat = 0.92 Demand = 3500 # Since there are 3 discrete variants of the problem, the optimization is performed 3 times and the best one is selected # With PV and Battery: result = optimize.minimize(f_optim, [1.1, 1.1], method='Nelder-Mead', tol=1e-5, constraints=cons, bounds=bnds).values() # With PV, without Battery result2 = optimize.minimize(f_optim_0, [1.1], method='Nelder-Mead', tol=1e-5, constraints=cons, bounds=bnds).values() # Without PV, without battery: E_0 = EnergyFlows(0, 0, Demand, eta_inv, eta_bat, CapacityFactorPV, coef) F_0 = FinancialAnalysis(E_0, CountryData, Inv) # Selecting the best solution: if F_0 <= result2[4] and F_0 <= result[4]: r_PV = 0 r_bat = 0 LCOE = F_0 elif result2[4] <= F_0 and result2[4] <= result[4]: r_PV = result2[5][0] r_bat = 0 LCOE = result2[4] elif result[4] <= F_0 and result[4] <= result2[4]: r_PV = result[5][0] r_bat = result[5][1] LCOE = result[4] # if r_PV is unbounded, do the univariate optimization with its max value if r_PV > max_RPV: result_fixedPV = optimize.minimize(f_optim_fixedPV, [1.1], method='Nelder-Mead', tol=1e-5, constraints=cons, bounds=bnds).values() E_fixedPV = EnergyFlows(max_RPV, 0, Demand, eta_inv, eta_bat, CapacityFactorPV, coef) F_fixedPV = FinancialAnalysis(E_fixedPV, CountryData, Inv) if F_fixedPV <= result_fixedPV[3]: r_PV = max_RPV r_bat = 0 LCOE = F_fixedPV else: r_PV = max_RPV r_bat = result_fixedPV[5][0] LCOE = result_fixedPV[4] return [r_PV, r_bat, LCOE]
costs = np.arange(100,250,2) LCOEs = [] BAT = [] PV=[] LCOE_stor = [] for c in costs: # Optimisation Inv['BatteryCost_kWh'] = c [r_PV, r_bat, LCOE] = SCoptim(CapacityFactorPV,CountryData.loc[country,:].to_dict(),Inv,coef) BAT.append(r_bat) LCOEs.append(LCOE) PV.append(r_PV) # Recalculating the whole set of values with the optimum inputs: E = EnergyFlows(r_PV,r_bat,Demand,eta_inv,eta_bat,CapacityFactorPV,coef) F = FinancialAnalysis(E,CountryData.loc[country,:].to_dict(),Inv) LCOE_stor.append(F['LCOE_stor']) # Plotting: fig4 = plt.figure(2,figsize=[7,5]) plt.subplot(211) plt.plot(costs,BAT,linestyle='--',linewidth=2, marker='o',label='Battery') plt.plot(costs,PV,label='PV', marker='o',linewidth=2) plt.ylabel('Relative PV/Battery sizes [-]') plt.ylim(0,2.5) plt.legend(fontsize=16) plt.grid() #remove xaxis: ax = plt.gca()