From 95c37b76f201d0ea94b521b882920e4fff3def7a Mon Sep 17 00:00:00 2001 From: Malcolm Ross Date: Mon, 2 Sep 2024 13:48:52 -0500 Subject: [PATCH] added some new parameters and changed the import of Economincs in SBT Economics --- src/geophires_x/Economics.py | 18 ++++++++++----- src/geophires_x/Outputs.py | 4 ++-- src/geophires_x/SBTEconomics.py | 39 +++++++++++++++++---------------- src/geophires_x/SBTReservoir.py | 1 + 4 files changed, 35 insertions(+), 27 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index c525eb71..75b80963 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -1766,8 +1766,14 @@ def __init__(self, model: Model): PreferredUnits=CurrencyUnit.MDOLLARS, CurrentUnits=CurrencyUnit.MDOLLARS ) - self.cost_nonvertical_section = self.OutputParameterDict[self.cost_nonvertical_section.Name] = OutputParameter( - Name="Cost of the non-vertical section of a well", + self.cost_lateral_section = self.OutputParameterDict[self.cost_lateral_section.Name] = OutputParameter( + Name="Cost of the entire (multi-) lateral section of a well", + UnitType=Units.CURRENCY, + PreferredUnits=CurrencyUnit.MDOLLARS, + CurrentUnits=CurrencyUnit.MDOLLARS + ) + self.cost_to_junction_section = self.OutputParameterDict[self.cost_to_junction_section.Name] = OutputParameter( + Name="Cost of the entire section of a well from bottom of vertical to junction with laterals", UnitType=Units.CURRENCY, PreferredUnits=CurrencyUnit.MDOLLARS, CurrentUnits=CurrencyUnit.MDOLLARS @@ -2177,7 +2183,7 @@ def Calculate(self, model: Model) -> None: (self.cost_one_injection_well.value * model.wellbores.ninj.value)) else: if hasattr(model.wellbores, 'numnonverticalsections') and model.wellbores.numnonverticalsections.Provided: - self.cost_nonvertical_section.value = 0.0 + self.cost_lateral_section.value = 0.0 if not model.wellbores.IsAGS.value: input_vert_depth_km = model.reserv.depth.quantity().to('km').magnitude output_vert_depth_km = 0.0 @@ -2218,7 +2224,7 @@ def Calculate(self, model: Model) -> None: self.injection_well_cost_adjustment_factor.value) if hasattr(model.wellbores, 'numnonverticalsections') and model.wellbores.numnonverticalsections.Provided: - self.cost_nonvertical_section.value = calculate_cost_of_non_vertical_section(model, tot_horiz_m, + self.cost_lateral_section.value = calculate_cost_of_non_vertical_section(model, tot_horiz_m, self.wellcorrelation.value, self.Nonvertical_drilling_cost_per_m.value, model.wellbores.numnonverticalsections.value, @@ -2226,12 +2232,12 @@ def Calculate(self, model: Model) -> None: model.wellbores.NonverticalsCased.value, self.production_well_cost_adjustment_factor.value) else: - self.cost_nonvertical_section.value = 0.0 + self.cost_lateral_section.value = 0.0 # cost of the well field # 1.05 for 5% indirect costs self.Cwell.value = 1.05 * ((self.cost_one_production_well.value * model.wellbores.nprod.value) + (self.cost_one_injection_well.value * model.wellbores.ninj.value) + - self.cost_nonvertical_section.value) + self.cost_lateral_section.value) # reservoir stimulation costs (M$/injection well). These are calculated whether totalcapcost.Valid = 1 if self.ccstimfixed.Valid: diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index 80a1f547..006e0125 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -1788,10 +1788,10 @@ def PrintOutputs(self, model: Model): model.economics.cost_one_injection_well.value != -1: f.write(f' Drilling and completion costs per production well: {econ.cost_one_production_well.value:10.2f} ' + econ.cost_one_production_well.CurrentUnits.value + NL) f.write(f' Drilling and completion costs per injection well: {econ.cost_one_injection_well.value:10.2f} ' + econ.cost_one_injection_well.CurrentUnits.value + NL) - elif econ.cost_nonvertical_section.value > 0.0: + elif econ.cost_lateral_section.value > 0.0: f.write(f' Drilling and completion costs per vertical production well: {econ.cost_one_production_well.value:10.2f} ' + econ.cost_one_production_well.CurrentUnits.value + NL) f.write(f' Drilling and completion costs per vertical injection well: {econ.cost_one_injection_well.value:10.2f} ' + econ.cost_one_injection_well.CurrentUnits.value + NL) - f.write(f' Drilling and completion costs per non-vertical sections: {econ.cost_nonvertical_section.value:10.2f} ' + econ.cost_nonvertical_section.CurrentUnits.value + NL) + f.write(f' Drilling and completion costs per non-vertical sections: {econ.cost_lateral_section.value:10.2f} ' + econ.cost_lateral_section.CurrentUnits.value + NL) else: f.write(f' Drilling and completion costs per well: {model.economics.Cwell.value/(model.wellbores.nprod.value+model.wellbores.ninj.value):10.2f} ' + model.economics.Cwell.CurrentUnits.value + NL) f.write(f' Stimulation costs: {model.economics.Cstim.value:10.2f} ' + model.economics.Cstim.CurrentUnits.value + NL) diff --git a/src/geophires_x/SBTEconomics.py b/src/geophires_x/SBTEconomics.py index 01c23510..8c1a528c 100644 --- a/src/geophires_x/SBTEconomics.py +++ b/src/geophires_x/SBTEconomics.py @@ -1,7 +1,8 @@ import sys, math import numpy as np import geophires_x.Model as Model -import geophires_x.Economics as Economics +from .Economics import Economics, calculate_cost_of_one_vertical_well, BuildPTCModel, BuildPricingModel, \ + CalculateRevenue, CalculateFinancialPerformance, CalculateLCOELCOHLCOC from .OptionList import Configuration, WellDrillingCostCorrelation, PlantType from geophires_x.Parameter import floatParameter from geophires_x.Units import * @@ -124,7 +125,7 @@ def calculate_cost_of_lateral_section(model: Model, length_m: float, well_correl return cost_of_lateral_section -class SBTEconomics(Economics.Economics): +class SBTEconomics(Economics): """ SBTEconomics Child class of Economics; it is the same, but has advanced SBT closed-loop functionality """ @@ -269,7 +270,7 @@ def Calculate(self, model: Model) -> None: else: # calculate the cost of one vertical production well # 1.05 for 5% indirect costs - self.cost_one_production_well.value = 1.05 * Economics.calculate_cost_of_one_vertical_well(model, model.wellbores.vertical_section_length.value, + self.cost_one_production_well.value = 1.05 * calculate_cost_of_one_vertical_well(model, model.wellbores.vertical_section_length.value, self.wellcorrelation.value, self.Vertical_drilling_cost_per_m.value, self.per_production_well_cost.Name, @@ -299,7 +300,7 @@ def Calculate(self, model: Model) -> None: # This section is not vertical, but it is cased, so we will estimate the cost # of this section as if it were a vertical section. if model.wellbores.Configuration.value == Configuration.EAVORLOOP: - self.cost_to_junction_section.value = 1.05 * Economics.calculate_cost_of_one_vertical_well(model, + self.cost_to_junction_section.value = 1.05 * calculate_cost_of_one_vertical_well(model, model.wellbores.tot_to_junction_m.value, self.wellcorrelation.value, self.Vertical_drilling_cost_per_m.value, @@ -744,32 +745,32 @@ def Calculate(self, model: Model) -> None: self.PTCCoolingPrice = [0.0] * model.surfaceplant.plant_lifetime.value self.PTCCarbonPrice = [0.0] * model.surfaceplant.plant_lifetime.value if self.PTCElec.Provided: - self.PTCElecPrice = Economics.BuildPTCModel(model.surfaceplant.plant_lifetime.value, + self.PTCElecPrice = BuildPTCModel(model.surfaceplant.plant_lifetime.value, self.PTCDuration.value, self.PTCElec.value, self.PTCInflationAdjusted.value, self.RINFL.value) if self.PTCHeat.Provided: - self.PTCHeatPrice = Economics.BuildPTCModel(model.surfaceplant.plant_lifetime.value, + self.PTCHeatPrice = BuildPTCModel(model.surfaceplant.plant_lifetime.value, self.PTCDuration.value, self.PTCHeat.value, self.PTCInflationAdjusted.value, self.RINFL.value) if self.PTCCooling.Provided: - self.PTCCoolingPrice = Economics.BuildPTCModel(model.surfaceplant.plant_lifetime.value, + self.PTCCoolingPrice = BuildPTCModel(model.surfaceplant.plant_lifetime.value, self.PTCDuration.value, self.PTCCooling.value, self.PTCInflationAdjusted.value, self.RINFL.value) # build the price models - self.ElecPrice.value = Economics.BuildPricingModel(model.surfaceplant.plant_lifetime.value, + self.ElecPrice.value = BuildPricingModel(model.surfaceplant.plant_lifetime.value, self.ElecStartPrice.value, self.ElecEndPrice.value, self.ElecEscalationStart.value, self.ElecEscalationRate.value, self.PTCElecPrice) - self.HeatPrice.value = Economics.BuildPricingModel(model.surfaceplant.plant_lifetime.value, + self.HeatPrice.value = BuildPricingModel(model.surfaceplant.plant_lifetime.value, self.HeatStartPrice.value, self.HeatEndPrice.value, self.HeatEscalationStart.value, self.HeatEscalationRate.value, self.PTCHeatPrice) - self.CoolingPrice.value = Economics.BuildPricingModel(model.surfaceplant.plant_lifetime.value, + self.CoolingPrice.value = BuildPricingModel(model.surfaceplant.plant_lifetime.value, self.CoolingStartPrice.value, self.CoolingEndPrice.value, self.CoolingEscalationStart.value, self.CoolingEscalationRate.value, self.PTCCoolingPrice) - self.CarbonPrice.value = Economics.BuildPricingModel(model.surfaceplant.plant_lifetime.value, + self.CarbonPrice.value = BuildPricingModel(model.surfaceplant.plant_lifetime.value, self.CarbonStartPrice.value, self.CarbonEndPrice.value, self.CarbonEscalationStart.value, self.CarbonEscalationRate.value, self.PTCCarbonPrice) @@ -796,19 +797,19 @@ def Calculate(self, model: Model) -> None: # Based on the style of the project, calculate the revenue & cumulative revenue if model.surfaceplant.enduse_option.value == EndUseOptions.ELECTRICITY: - self.ElecRevenue.value, self.ElecCummRevenue.value = Economics.CalculateRevenue( + self.ElecRevenue.value, self.ElecCummRevenue.value = CalculateRevenue( model.surfaceplant.plant_lifetime.value, model.surfaceplant.construction_years.value, model.surfaceplant.NetkWhProduced.value, self.ElecPrice.value) self.TotalRevenue.value = self.ElecRevenue.value #self.TotalCummRevenue.value = self.ElecCummRevenue.value elif model.surfaceplant.enduse_option.value == EndUseOptions.HEAT and model.surfaceplant.plant_type.value not in [PlantType.ABSORPTION_CHILLER]: - self.HeatRevenue.value, self.HeatCummRevenue.value = Economics.CalculateRevenue( + self.HeatRevenue.value, self.HeatCummRevenue.value = CalculateRevenue( model.surfaceplant.plant_lifetime.value, model.surfaceplant.construction_years.value, model.surfaceplant.HeatkWhProduced.value, self.HeatPrice.value) self.TotalRevenue.value = self.HeatRevenue.value #self.TotalCummRevenue.value = self.HeatCummRevenue.value elif model.surfaceplant.enduse_option.value == EndUseOptions.HEAT and model.surfaceplant.plant_type.value in [PlantType.ABSORPTION_CHILLER]: - self.CoolingRevenue.value, self.CoolingCummRevenue.value = Economics.CalculateRevenue( + self.CoolingRevenue.value, self.CoolingCummRevenue.value = CalculateRevenue( model.surfaceplant.plant_lifetime.value, model.surfaceplant.construction_years.value, model.surfaceplant.cooling_kWh_Produced.value, self.CoolingPrice.value) self.TotalRevenue.value = self.CoolingRevenue.value @@ -820,10 +821,10 @@ def Calculate(self, model: Model) -> None: EndUseOptions.COGENERATION_PARALLEL_EXTRA_HEAT, EndUseOptions.COGENERATION_PARALLEL_EXTRA_ELECTRICITY]: # co-gen # else: - self.ElecRevenue.value, self.ElecCummRevenue.value = Economics.CalculateRevenue( + self.ElecRevenue.value, self.ElecCummRevenue.value = CalculateRevenue( model.surfaceplant.plant_lifetime.value, model.surfaceplant.construction_years.value, model.surfaceplant.NetkWhProduced.value, self.ElecPrice.value) - self.HeatRevenue.value, self.HeatCummRevenue.value = Economics.CalculateRevenue( + self.HeatRevenue.value, self.HeatCummRevenue.value = CalculateRevenue( model.surfaceplant.plant_lifetime.value, model.surfaceplant.construction_years.value, model.surfaceplant.HeatkWhProduced.value, self.HeatPrice.value) @@ -834,7 +835,7 @@ def Calculate(self, model: Model) -> None: if self.DoCarbonCalculations.value: self.CarbonRevenue.value, self.CarbonCummCashFlow.value, self.CarbonThatWouldHaveBeenProducedAnnually.value, \ - self.CarbonThatWouldHaveBeenProducedTotal.value = Economics.CalculateCarbonRevenue(model, + self.CarbonThatWouldHaveBeenProducedTotal.value = CalculateCarbonRevenue(model, model.surfaceplant.plant_lifetime.value, model.surfaceplant.construction_years.value, self.CarbonPrice.value, self.GridCO2Intensity.value, self.NaturalGasCO2Intensity.value, model.surfaceplant.NetkWhProduced.value, model.surfaceplant.HeatkWhProduced.value) @@ -871,7 +872,7 @@ def Calculate(self, model: Model) -> None: # Calculate more financial values using numpy financials self.ProjectNPV.value, self.ProjectIRR.value, self.ProjectVIR.value, self.ProjectMOIC.value = \ - Economics.CalculateFinancialPerformance(model.surfaceplant.plant_lifetime.value, self.FixedInternalRate.value, + CalculateFinancialPerformance(model.surfaceplant.plant_lifetime.value, self.FixedInternalRate.value, self.TotalRevenue.value, self.TotalCummRevenue.value, self.CCap.value, self.Coam.value) @@ -887,7 +888,7 @@ def Calculate(self, model: Model) -> None: # Calculate LCOE/LCOH - self.LCOE.value, self.LCOH.value, self.LCOC.value = Economics.CalculateLCOELCOHLCOC(self, model) + self.LCOE.value, self.LCOH.value, self.LCOC.value = CalculateLCOELCOHLCOC(self, model) model.logger.info(f'complete {__class__!s}: {sys._getframe().f_code.co_name}') diff --git a/src/geophires_x/SBTReservoir.py b/src/geophires_x/SBTReservoir.py index 6d46a1a4..6821aed4 100644 --- a/src/geophires_x/SBTReservoir.py +++ b/src/geophires_x/SBTReservoir.py @@ -233,6 +233,7 @@ def __init__(self, model: Model): Max=1.0, UnitType=Units.PERCENT, PreferredUnits=PercentUnit.TENTH, + CurrentUnits=PercentUnit.TENTH, ErrMessage="assume default percent implicit (1.0)", ToolTipText="Should be between 0 and 1. Most stable is setting it to 1 which results in \ a fully implicit Euler scheme when calculating the fluid temperature at each time step. \