popt.cost_functions.ecalc_pareto_npv
Net present value.
1"""Net present value.""" 2import numpy 3import numpy as np 4import csv 5from pathlib import Path 6import pandas as pd 7import sys 8import os 9 10HERE = Path().cwd() # fallback for ipynb's 11HERE = HERE.resolve() 12 13 14def ecalc_pareto_npv(pred_data, kwargs): 15 """ 16 Net present value cost function using eCalc to calculate emmisions 17 18 Parameters 19 ---------- 20 pred_data : array_like 21 Ensemble of predicted data. 22 23 **kwargs : dict 24 Other arguments sent to the npv function 25 26 keys_opt : list 27 Keys with economic data. 28 29 report : list 30 Report dates. 31 32 Returns 33 ------- 34 objective_values : array_like 35 Objective function values (NPV) for all ensemble members. 36 """ 37 38 from libecalc.application.energy_calculator import EnergyCalculator 39 from libecalc.common.time_utils import Frequency 40 from libecalc.presentation.yaml.model import YamlModel 41 42 # Get the necessary input 43 keys_opt = kwargs.get('input_dict', {}) 44 report = kwargs.get('true_order', []) 45 46 # Economic values 47 npv_const = {} 48 for name, value in keys_opt['npv_const']: 49 npv_const[name] = value 50 51 # Collect production data 52 Qop = [] 53 Qgp = [] 54 Qwp = [] 55 Qwi = [] 56 Dd = [] 57 for i in np.arange(1, len(pred_data)): 58 59 Qop.append(np.squeeze(pred_data[i]['fopt']) - np.squeeze(pred_data[i - 1]['fopt'])) 60 Qgp.append(np.squeeze(pred_data[i]['fgpt']) - np.squeeze(pred_data[i - 1]['fgpt'])) 61 Qwp.append(np.squeeze(pred_data[i]['fwpt']) - np.squeeze(pred_data[i - 1]['fwpt'])) 62 Qwi.append(np.squeeze(pred_data[i]['fwit']) - np.squeeze(pred_data[i - 1]['fwit'])) 63 Dd.append((report[1][i] - report[1][i - 1]).days) 64 65 # Write production data to .csv file for eCalc input, for each ensemble member 66 Qop = np.array(Qop).T 67 Qwp = np.array(Qwp).T 68 Qgp = np.array(Qgp).T 69 Qwi = np.array(Qwi).T 70 Dd = np.array(Dd) 71 if len(Qop.shape) == 1: 72 Qop = np.expand_dims(Qop,0) 73 Qwp = np.expand_dims(Qwp, 0) 74 Qgp = np.expand_dims(Qgp, 0) 75 Qwi = np.expand_dims(Qwi, 0) 76 77 N = Qop.shape[0] 78 T = Qop.shape[1] 79 values = [] 80 pareto_values = [] 81 for n in range(N): 82 with open('ecalc_input.csv', 'w') as csvfile: 83 writer = csv.writer(csvfile, delimiter=',') 84 writer.writerow(['dd/mm/yyyy', 'GAS_PROD', 'OIL_PROD', 'WATER_INJ']) 85 for t in range(T): 86 D = report[1][t] 87 writer.writerow([D.strftime("%d/%m/%Y"), Qgp[n, t]/Dd[t], Qop[n, t]/Dd[t], Qwi[n, t]/Dd[t]]) 88 89 # Config 90 model_path = HERE / "ecalc_config.yaml" # "drogn.yaml" 91 yaml_model = YamlModel(path=model_path, output_frequency=Frequency.NONE) 92 # comps = {c.name: id_hash for (id_hash, c) in yaml_model.graph.components.items()} 93 94 # Compute energy, emissions 95 model = EnergyCalculator(graph=yaml_model.graph) 96 consumer_results = model.evaluate_energy_usage(yaml_model.variables) 97 emission_results = model.evaluate_emissions(yaml_model.variables, consumer_results) 98 99 # Extract 100 # energy = results_as_df(yaml_model, consumer_results, lambda r: r.component_result.energy_usage) 101 emissions = results_as_df(yaml_model, emission_results, lambda r: r['co2_fuel_gas'].rate) 102 emissions_total = emissions.sum(1).rename("emissions_total") 103 emissions_total.to_csv(HERE / "emissions.csv") 104 Qem = emissions_total.values * Dd # total number of tons 105 106 value1 = (Qop[n, :] * npv_const['wop'] + Qgp[n, :] * npv_const['wgp'] - Qwp[n, :] * npv_const['wwp'] - 107 Qwi[n, :] * npv_const['wwi'] - Qem * npv_const['wem']) / ( 108 (1 + npv_const['disc']) ** (Dd / 365)) 109 value1 = np.sum(value1) 110 if 'obj_scaling' in npv_const: 111 value1 /= npv_const['obj_scaling'] 112 113 value2 = np.array([]) 114 if 'wemc' in npv_const: # multi-opjective with co2 cost correction 115 value2 = - Qem * npv_const['wemc'] / ((1 + npv_const['disc']) ** (Dd / 365)) 116 value2 = np.sum(value2) 117 if 'obj_scaling' in npv_const: 118 value2 /= npv_const['obj_scaling'] 119 elif 'w' in npv_const: # multi-objective with emission intensity 120 rho_o = 840.0 # oil density 121 rho_g = 1 # gas density 122 conv = 1000 # convert from kilo to tonne 123 value2 = np.sum(Qem*conv) / (np.sum(Qop[n, :]*rho_o + Qgp[n, :]*rho_g)/conv) # kg/toe 124 125 pareto_values.append([np.sum(Qem), value1, value2]) 126 if 'w' in npv_const: 127 values.append((1-npv_const['w'])*value1 + npv_const['w']*value2) 128 else: 129 values.append(value1 + value2) # total objective function 130 131 # Save emissions and both objective functions for later analysis 132 pareto_file = 'pareto_values.npz' 133 if os.path.exists(pareto_file): 134 pareto_iterations = np.load(pareto_file,allow_pickle=True)['pareto_iterations'][()] 135 else: 136 pareto_iterations = {} 137 num_eval = len(pareto_iterations) 138 pareto_iterations[str(num_eval)] = pareto_values 139 np.savez('pareto_values.npz', pareto_iterations=pareto_iterations) 140 141 return np.array(values) 142 143def results_as_df(yaml_model, results, getter) -> pd.DataFrame: 144 """Extract relevant values, as well as some meta (`attrs`).""" 145 df = {} 146 attrs = {} 147 res = None 148 for id_hash in results: 149 res = results[id_hash] 150 res = getter(res) 151 component = yaml_model.graph.get_node(id_hash) 152 df[component.name] = res.values 153 attrs[component.name] = {'id_hash': id_hash, 154 'kind': type(component).__name__, 155 'unit': res.unit} 156 if res is None: 157 sys.exit('No emission results from eCalc!') 158 df = pd.DataFrame(df, index=res.timesteps) 159 df.index.name = "dates" 160 df.attrs = attrs 161 return df
HERE =
PosixPath('/home/runner/work/PET/PET')
def
ecalc_pareto_npv(pred_data, kwargs):
15def ecalc_pareto_npv(pred_data, kwargs): 16 """ 17 Net present value cost function using eCalc to calculate emmisions 18 19 Parameters 20 ---------- 21 pred_data : array_like 22 Ensemble of predicted data. 23 24 **kwargs : dict 25 Other arguments sent to the npv function 26 27 keys_opt : list 28 Keys with economic data. 29 30 report : list 31 Report dates. 32 33 Returns 34 ------- 35 objective_values : array_like 36 Objective function values (NPV) for all ensemble members. 37 """ 38 39 from libecalc.application.energy_calculator import EnergyCalculator 40 from libecalc.common.time_utils import Frequency 41 from libecalc.presentation.yaml.model import YamlModel 42 43 # Get the necessary input 44 keys_opt = kwargs.get('input_dict', {}) 45 report = kwargs.get('true_order', []) 46 47 # Economic values 48 npv_const = {} 49 for name, value in keys_opt['npv_const']: 50 npv_const[name] = value 51 52 # Collect production data 53 Qop = [] 54 Qgp = [] 55 Qwp = [] 56 Qwi = [] 57 Dd = [] 58 for i in np.arange(1, len(pred_data)): 59 60 Qop.append(np.squeeze(pred_data[i]['fopt']) - np.squeeze(pred_data[i - 1]['fopt'])) 61 Qgp.append(np.squeeze(pred_data[i]['fgpt']) - np.squeeze(pred_data[i - 1]['fgpt'])) 62 Qwp.append(np.squeeze(pred_data[i]['fwpt']) - np.squeeze(pred_data[i - 1]['fwpt'])) 63 Qwi.append(np.squeeze(pred_data[i]['fwit']) - np.squeeze(pred_data[i - 1]['fwit'])) 64 Dd.append((report[1][i] - report[1][i - 1]).days) 65 66 # Write production data to .csv file for eCalc input, for each ensemble member 67 Qop = np.array(Qop).T 68 Qwp = np.array(Qwp).T 69 Qgp = np.array(Qgp).T 70 Qwi = np.array(Qwi).T 71 Dd = np.array(Dd) 72 if len(Qop.shape) == 1: 73 Qop = np.expand_dims(Qop,0) 74 Qwp = np.expand_dims(Qwp, 0) 75 Qgp = np.expand_dims(Qgp, 0) 76 Qwi = np.expand_dims(Qwi, 0) 77 78 N = Qop.shape[0] 79 T = Qop.shape[1] 80 values = [] 81 pareto_values = [] 82 for n in range(N): 83 with open('ecalc_input.csv', 'w') as csvfile: 84 writer = csv.writer(csvfile, delimiter=',') 85 writer.writerow(['dd/mm/yyyy', 'GAS_PROD', 'OIL_PROD', 'WATER_INJ']) 86 for t in range(T): 87 D = report[1][t] 88 writer.writerow([D.strftime("%d/%m/%Y"), Qgp[n, t]/Dd[t], Qop[n, t]/Dd[t], Qwi[n, t]/Dd[t]]) 89 90 # Config 91 model_path = HERE / "ecalc_config.yaml" # "drogn.yaml" 92 yaml_model = YamlModel(path=model_path, output_frequency=Frequency.NONE) 93 # comps = {c.name: id_hash for (id_hash, c) in yaml_model.graph.components.items()} 94 95 # Compute energy, emissions 96 model = EnergyCalculator(graph=yaml_model.graph) 97 consumer_results = model.evaluate_energy_usage(yaml_model.variables) 98 emission_results = model.evaluate_emissions(yaml_model.variables, consumer_results) 99 100 # Extract 101 # energy = results_as_df(yaml_model, consumer_results, lambda r: r.component_result.energy_usage) 102 emissions = results_as_df(yaml_model, emission_results, lambda r: r['co2_fuel_gas'].rate) 103 emissions_total = emissions.sum(1).rename("emissions_total") 104 emissions_total.to_csv(HERE / "emissions.csv") 105 Qem = emissions_total.values * Dd # total number of tons 106 107 value1 = (Qop[n, :] * npv_const['wop'] + Qgp[n, :] * npv_const['wgp'] - Qwp[n, :] * npv_const['wwp'] - 108 Qwi[n, :] * npv_const['wwi'] - Qem * npv_const['wem']) / ( 109 (1 + npv_const['disc']) ** (Dd / 365)) 110 value1 = np.sum(value1) 111 if 'obj_scaling' in npv_const: 112 value1 /= npv_const['obj_scaling'] 113 114 value2 = np.array([]) 115 if 'wemc' in npv_const: # multi-opjective with co2 cost correction 116 value2 = - Qem * npv_const['wemc'] / ((1 + npv_const['disc']) ** (Dd / 365)) 117 value2 = np.sum(value2) 118 if 'obj_scaling' in npv_const: 119 value2 /= npv_const['obj_scaling'] 120 elif 'w' in npv_const: # multi-objective with emission intensity 121 rho_o = 840.0 # oil density 122 rho_g = 1 # gas density 123 conv = 1000 # convert from kilo to tonne 124 value2 = np.sum(Qem*conv) / (np.sum(Qop[n, :]*rho_o + Qgp[n, :]*rho_g)/conv) # kg/toe 125 126 pareto_values.append([np.sum(Qem), value1, value2]) 127 if 'w' in npv_const: 128 values.append((1-npv_const['w'])*value1 + npv_const['w']*value2) 129 else: 130 values.append(value1 + value2) # total objective function 131 132 # Save emissions and both objective functions for later analysis 133 pareto_file = 'pareto_values.npz' 134 if os.path.exists(pareto_file): 135 pareto_iterations = np.load(pareto_file,allow_pickle=True)['pareto_iterations'][()] 136 else: 137 pareto_iterations = {} 138 num_eval = len(pareto_iterations) 139 pareto_iterations[str(num_eval)] = pareto_values 140 np.savez('pareto_values.npz', pareto_iterations=pareto_iterations) 141 142 return np.array(values)
Net present value cost function using eCalc to calculate emmisions
Parameters
- pred_data (array_like): Ensemble of predicted data.
**kwargs (dict): Other arguments sent to the npv function
keys_opt : list Keys with economic data.
report : list Report dates.
Returns
- objective_values (array_like): Objective function values (NPV) for all ensemble members.
def
results_as_df(yaml_model, results, getter) -> pandas.core.frame.DataFrame:
144def results_as_df(yaml_model, results, getter) -> pd.DataFrame: 145 """Extract relevant values, as well as some meta (`attrs`).""" 146 df = {} 147 attrs = {} 148 res = None 149 for id_hash in results: 150 res = results[id_hash] 151 res = getter(res) 152 component = yaml_model.graph.get_node(id_hash) 153 df[component.name] = res.values 154 attrs[component.name] = {'id_hash': id_hash, 155 'kind': type(component).__name__, 156 'unit': res.unit} 157 if res is None: 158 sys.exit('No emission results from eCalc!') 159 df = pd.DataFrame(df, index=res.timesteps) 160 df.index.name = "dates" 161 df.attrs = attrs 162 return df
Extract relevant values, as well as some meta (attrs
).