From ac087413d06779f373a115c2597310c7bd21d265 Mon Sep 17 00:00:00 2001 From: Malcolm Ross Date: Tue, 23 Apr 2024 12:14:08 -0600 Subject: [PATCH] Adjusted for all Jonathans suggestions, added pumping pressure output as additional table but only for when overpressure and split reservoirs are in use. --- src/geophires_x/AGSWellBores.py | 20 +- src/geophires_x/CylindricalReservoir.py | 8 +- src/geophires_x/Economics.py | 273 +++++++++++------- src/geophires_x/Outputs.py | 92 +++++- src/geophires_x/Reservoir.py | 4 +- src/geophires_x/SUTRAOutputs.py | 8 +- src/geophires_x/WellBores.py | 63 ++-- src/geophires_x_client/geophires_x_result.py | 4 + ...Closed-Loop_Geothermal_Energy_Recovery.out | 268 ++++++++--------- tests/examples/example_overpressure.out | 271 ++++++++--------- tests/examples/example_overpressure.txt | 12 +- .../test_cylindrical_reservoir.py | 8 +- 12 files changed, 596 insertions(+), 435 deletions(-) diff --git a/src/geophires_x/AGSWellBores.py b/src/geophires_x/AGSWellBores.py index df966a4a..e91edf77 100644 --- a/src/geophires_x/AGSWellBores.py +++ b/src/geophires_x/AGSWellBores.py @@ -871,12 +871,12 @@ def CalculateNonverticalPressureDrop(self, model:Model, time_operation: float, t # nonvertical wellbore fluid conditions based on current temperature rhowater = density_water_kg_per_m3( self.NonverticalProducedTemperature.value[year], - pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.Trock.value) + pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.InputDepth.quantity().to('m').magnitude) ) muwater = viscosity_water_Pa_sec( self.NonverticalProducedTemperature.value[year], - pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.Trock.value) + pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.InputDepth.quantity().to('m').magnitude) ) vhoriz = self.q_circulation / rhowater / (math.pi / 4. * self.nonverticalwellborediameter.value ** 2) @@ -957,15 +957,15 @@ def Calculate(self, model: Model) -> None: # MIR figure out how to calculate year and extract Tini from reserv Tresoutput array year = math.trunc(self.time_operation.value / self.al) self.NonverticalProducedTemperature.value[year] = inverselaplace( - self, 16, 0, model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.Trock.value)) + self, 16, 0, model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.InputDepth.quantity().to('m').magnitude)) # update alpha_fluid value based on next temperature of reservoir self.alpha_fluid = self.WaterThermalConductivity.value / density_water_kg_per_m3( self.NonverticalProducedTemperature.value[year], - pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.Trock.value) + pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.InputDepth.quantity().to('m').magnitude) ) / heat_capacity_water_J_per_kg_per_K( self.NonverticalProducedTemperature.value[year], - pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.Trock.value) + pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.InputDepth.quantity().to('m').magnitude) ) * 24.0 * 3600.0 self.time_operation.value += self.al @@ -979,7 +979,7 @@ def Calculate(self, model: Model) -> None: self.ProdTempDrop.value = self.tempdropprod.value model.reserv.cpwater.value = heat_capacity_water_J_per_kg_per_K( self.NonverticalProducedTemperature.value[0], - pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.Trock.value) + pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.InputDepth.quantity().to('m').magnitude) ) if self.rameyoptionprod.value: self.ProdTempDrop.value = RameyCalc(model.reserv.krock.value, @@ -1002,13 +1002,13 @@ def Calculate(self, model: Model) -> None: if self.productionwellpumping.value: self.rhowaterinj = density_water_kg_per_m3( model.reserv.Tsurf.value, - pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.Trock.value) + pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.InputDepth.quantity().to('m').magnitude) ) * np.linspace(1, 1, len(self.ProducedTemperature.value)) self.rhowaterprod = density_water_kg_per_m3( model.reserv.Trock.value, - pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.Trock.value) + pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.InputDepth.quantity().to('m').magnitude) ) * np.linspace(1, 1, len(self.ProducedTemperature.value)) self.DPProdWell.value, f3, vprod, self.rhowaterprod = WellPressureDrop(model, @@ -1113,13 +1113,13 @@ def Calculate(self, model: Model) -> None: rho_water = density_water_kg_per_m3( self.Tout[0], - pressure = model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.Trock.value), + pressure = model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.InputDepth.quantity().to('m').magnitude), ) model.reserv.cpwater.value = heat_capacity_water_J_per_kg_per_K( self.Tout[0], - pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.Trock.value), + pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.InputDepth.quantity().to('m').magnitude), ) # Need this for surface plant output calculation # set pumping power to zero for all times, assuming that the thermosphere wil always diff --git a/src/geophires_x/CylindricalReservoir.py b/src/geophires_x/CylindricalReservoir.py index 6ecf8225..12114301 100644 --- a/src/geophires_x/CylindricalReservoir.py +++ b/src/geophires_x/CylindricalReservoir.py @@ -244,11 +244,11 @@ def Calculate(self, model: Model) -> None: ) / 1e15 # 10^15 J self.cpwater.value = heat_capacity_water_J_per_kg_per_K( model.wellbores.Tinj.value * 0.5 + (self.Trock.value * 0.9 + model.wellbores.Tinj.value * 0.1) * 0.5, - pressure=self.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.Trock.value) + pressure=self.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.depth.quantity().to('m').magnitude) ) self.rhowater.value = density_water_kg_per_m3( model.wellbores.Tinj.value * 0.5 + (self.Trock.value * 0.9 + model.wellbores.Tinj.value * 0.1) * 0.5, - pressure=self.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.Trock.value) + pressure=self.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.depth.quantity().to('m').magnitude) ) model.logger.info(f'complete {str(__class__)}: {sys._getframe().f_code.co_name}') @@ -259,5 +259,5 @@ def Calculate(self, model: Model) -> None: Standard reservoir implementation uses depth but CylindricalReservoir sets depth to total drilled length """ - def lithostatic_pressure(self, rho_rock: float, depth: float) -> PlainQuantity: - return quantity(static_pressure_MPa(rho_rock, depth), 'MPa') + def lithostatic_pressure(self, rho_rock_kg_per_m3: float, depth_m: float) -> PlainQuantity: + return quantity(static_pressure_MPa(rho_rock_kg_per_m3, depth_m), 'MPa') diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 079a19ea..a5493301 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -9,6 +9,109 @@ from geophires_x.Units import * +def CalculateCostOfOneWell(model: Model, depth_m: float, well_correlation: int, vertical_drilling_cost_per_m: float, + fixed_well_cost_name: str, well_cost_adjustment_factor: float) -> float: + """ + CalculateCostOfOneWell calculates the cost of one well based on the depth of the well and the cost correlation. + :param model: The model object + :type model: :class:`~geophires + :param depth_m: The depth of the well in meters + :type depth_m: float + :param well_correlation: The well correlation + :type well_correlation: int + :param vertical_drilling_cost_per_m: The vertical drilling cost per meter in $/m + :type vertical_drilling_cost_per_m: float + :param fixed_well_cost_name: The fixed well cost name + :type fixed_well_cost_name: str + :param well_cost_adjustment_factor: The well cost adjustment factor + :type well_cost_adjustment_factor: float + :return: cost_of_one_well: The cost of one well in MUSD + :rtype: float + """ + # Check if well depth is out of standard bounds for cost correlation + correlations_min_valid_depth_m = 500. + correlations_max_valid_depth_m = 7000. + cost_of_one_well = 0.0 + + if depth_m < correlations_min_valid_depth_m and not well_correlation is WellDrillingCostCorrelation.SIMPLE: + well_correlation = WellDrillingCostCorrelation.SIMPLE + model.logger.warning( + f'Invalid cost correlation specified ({well_correlation}) for drilling depth ' + f'<{correlations_min_valid_depth_m}m ({depth_m}m). ' + f'Falling back to simple user-specified cost ' + f'({vertical_drilling_cost_per_m} per meter)' + ) + + if depth_m > correlations_max_valid_depth_m and not well_correlation is WellDrillingCostCorrelation.SIMPLE: + model.logger.warning( + f'{well_correlation} may be invalid for drilling depth ' + f'>{correlations_max_valid_depth_m}m ({depth_m}m). ' + f'Consider using {WellDrillingCostCorrelation.SIMPLE} (per-meter cost) or ' + f'{fixed_well_cost_name} (fixed cost per well) instead.' + ) + + if well_correlation is WellDrillingCostCorrelation.SIMPLE: + # using the "Configuration" keywords means we are doing an AGS calculation + if hasattr(model.wellbores, 'Configuration'): + if model.wellbores.Configuration.value is Configuration.ULOOP: + # found out if we are using simple cylindrical model, which has an Input and Output Depth + if hasattr(model.reserv, 'InputDepth'): + # cost of one closed-loop well = cost of 2 verticals + cost of horizontal section(s) + cost_of_one_well = ((vertical_drilling_cost_per_m * (model.reserv.InputDepth.value * 1000.0)) + + (vertical_drilling_cost_per_m * (model.reserv.OutputDepth.value * 1000.0)) + + (model.wellboew.numnonverticalsections.value * + model.wellbores.Nonvertical_drilling_cost_per_m.value * + model.wellbores.Nonvertical_length.value)) * 1E-6 + else: + if hasattr(model.wellbores, 'Nonvertical_length'): + cost_of_one_well = ((2 * vertical_drilling_cost_per_m * (model.reserv.depth.value * 1000.0)) + + (model.wellbores.Nonvertical_drilling_cost_per_m.value * model.wellbores.Nonvertical_length.value)) * 1E-6 + else: + cost_of_one_well = (2 * vertical_drilling_cost_per_m * (model.reserv.depth.value * 1000.0)) * 1E-6 + else: # Coaxial + cost_of_one_well = ((vertical_drilling_cost_per_m * (model.reserv.depth.value * 1000.0)) + + (model.wellbores.Nonvertical_drilling_cost_per_m.value * model.wellbores.Nonvertical_length.value)) * 1E-6 + else: + cost_of_one_well = vertical_drilling_cost_per_m * model.reserv.depth.quantity().to('m').magnitude * 1E-6 + + elif well_correlation is WellDrillingCostCorrelation.VERTICAL_SMALL: + cost_of_one_well = (0.30212 * depth_m ** 2 + 584.91124 * depth_m + 751368.47270) * 1E-6 + elif well_correlation is WellDrillingCostCorrelation.VERTICAL_LARGE: + cost_of_one_well = (0.28180 * depth_m ** 2 + 1275.52130 * depth_m + 632315.12640) * 1E-6 + elif well_correlation is WellDrillingCostCorrelation.DEVIATED_SMALL: + cost_of_one_well = (0.28977 * depth_m ** 2 + 882.15067 * depth_m + 680562.50150) * 1E-6 + elif well_correlation is WellDrillingCostCorrelation.DEVIATED_LARGE: + cost_of_one_well = (0.25528 * depth_m ** 2 + 1716.71568 * depth_m + 500866.89110) * 1E-6 + elif well_correlation is WellDrillingCostCorrelation.VERTICAL_SMALL_INT1: + cost_of_one_well = (0.13710 * depth_m ** 2 + 129.61033 * depth_m + 1205587.57100) * 1E-6 + elif well_correlation is WellDrillingCostCorrelation.VERTICAL_LARGE_INT1: + cost_of_one_well = (0.18927 * depth_m ** 2 + 293.45174 * depth_m + 1326526.31300) * 1E-6 + elif well_correlation is WellDrillingCostCorrelation.DEVIATED_SMALL_INT1: + cost_of_one_well = (0.15340 * depth_m ** 2 + 120.31700 * depth_m + 1431801.54400) * 1E-6 + elif well_correlation is WellDrillingCostCorrelation.DEVIATED_LARGE_INT1: + cost_of_one_well = (0.19950 * depth_m ** 2 + 296.13011 * depth_m + 1697867.70900) * 1E-6 + elif well_correlation is WellDrillingCostCorrelation.VERTICAL_SMALL_INT2: + cost_of_one_well = (0.00804 * depth_m ** 2 + 455.60507 * depth_m + 921007.68680) * 1E-6 + elif well_correlation is WellDrillingCostCorrelation.VERTICAL_LARGE_INT2: + cost_of_one_well = (0.00315 * depth_m ** 2 + 782.69676 * depth_m + 983620.25270) * 1E-6 + elif well_correlation is WellDrillingCostCorrelation.DEVIATED_SMALL_INT2: + cost_of_one_well = (0.00854 * depth_m ** 2 + 506.08357 * depth_m + 1057330.39000) * 1E-6 + elif well_correlation is WellDrillingCostCorrelation.DEVIATED_LARGE_INT2: + cost_of_one_well = (0.00380 * depth_m ** 2 + 838.90249 * depth_m + 1181947.04400) * 1E-6 + elif well_correlation is WellDrillingCostCorrelation.VERTICAL_SMALL_IDEAL: + cost_of_one_well = (0.00252 * depth_m ** 2 + 439.44503 * depth_m + 590611.90110) * 1E-6 + elif well_correlation is WellDrillingCostCorrelation.VERTICAL_LARGE_IDEAL: + cost_of_one_well = (-0.00240 * depth_m ** 2 + 752.93946 * depth_m + 524337.65380) * 1E-6 + elif well_correlation is WellDrillingCostCorrelation.DEVIATED_SMALL_IDEAL: + cost_of_one_well = (0.00719 * depth_m ** 2 + 455.85233 * depth_m + 753377.73080) * 1E-6 + elif well_correlation is WellDrillingCostCorrelation.DEVIATED_LARGE_IDEAL: + cost_of_one_well = (0.00376 * depth_m ** 2 + 762.52696 * depth_m + 765103.07690) * 1E-6 + + # account for adjustment factor + cost_of_one_well = well_cost_adjustment_factor * cost_of_one_well + return cost_of_one_well + + def BuildPTCModel(plantlifetime: int, duration: int, ptc_price: float, ptc_inflation_adjusted: bool, inflation_rate: float) -> list: """ @@ -494,7 +597,7 @@ def __init__(self, model: Model): Valid=True, ToolTipText="Multiplier for built-in exploration capital cost correlation" ) - self.ccwellfixed = self.ParameterDict[self.ccwellfixed.Name] = floatParameter( + self.per_production_well_cost = self.ParameterDict[self.per_production_well_cost.Name] = floatParameter( "Well Drilling and Completion Capital Cost", DefaultValue=-1.0, Min=0, @@ -506,7 +609,19 @@ def __init__(self, model: Model): Valid=False, ToolTipText="Well Drilling and Completion Capital Cost" ) - self.ccwelladjfactor = self.ParameterDict[self.ccwelladjfactor.Name] = floatParameter( + self.per_injection_well_cost = self.ParameterDict[self.per_injection_well_cost.Name] = floatParameter( + "Injection Well Drilling and Completion Capital Cost", + DefaultValue=self.per_production_well_cost.value, + Min=0, + Max=200, + UnitType=Units.CURRENCY, + PreferredUnits=CurrencyUnit.MDOLLARS, + CurrentUnits=CurrencyUnit.MDOLLARS, + Provided=False, + Valid=False, + ToolTipText="Injection Well Drilling and Completion Capital Cost" + ) + self.production_well_cost_adjustment_factor = self.ParameterDict[self.production_well_cost_adjustment_factor.Name] = floatParameter( "Well Drilling and Completion Capital Cost Adjustment Factor", DefaultValue=1.0, Min=0, @@ -518,6 +633,18 @@ def __init__(self, model: Model): Valid=True, ToolTipText="Well Drilling and Completion Capital Cost Adjustment Factor" ) + self.injection_well_cost_adjustment_factor = self.ParameterDict[self.injection_well_cost_adjustment_factor.Name] = floatParameter( + "Injection Well Drilling and Completion Capital Cost Adjustment Factor", + DefaultValue=self.production_well_cost_adjustment_factor.value, + Min=0, + Max=10, + UnitType=Units.PERCENT, + PreferredUnits=PercentUnit.TENTH, + CurrentUnits=PercentUnit.TENTH, + Provided=False, + Valid=True, + ToolTipText="Injection Well Drilling and Completion Capital Cost Adjustment Factor" + ) self.oamwellfixed = self.ParameterDict[self.oamwellfixed.Name] = floatParameter( "Wellfield O&M Cost", DefaultValue=-1.0, @@ -1327,7 +1454,6 @@ def __init__(self, model: Model): self.annualheatincome = 0.0 self.InputFile = "" self.Cplantcorrelation = 0.0 - self.C1well = 0.0 sclass = str(__class__).replace("", "") self.MyPath = os.path.abspath(__file__) @@ -1451,7 +1577,6 @@ def __init__(self, model: Model): PreferredUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR, CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR ) - # heat pump self.averageannualheatpumpelectricitycost = self.OutputParameterDict[ self.averageannualheatpumpelectricitycost.Name] = OutputParameter( @@ -1460,7 +1585,6 @@ def __init__(self, model: Model): PreferredUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR, CurrentUnits=CurrencyFrequencyUnit.MDOLLARSPERYEAR ) - # district heating self.peakingboilercost = self.OutputParameterDict[self.peakingboilercost.Name] = OutputParameter( Name="Peaking boiler cost", @@ -1609,6 +1733,18 @@ def __init__(self, model: Model): PreferredUnits=CurrencyUnit.MDOLLARS, CurrentUnits=CurrencyUnit.MDOLLARS ) + self.cost_one_production_well = self.OutputParameterDict[self.cost_one_production_well.Name] = OutputParameter( + Name="Cost of One Production Well", + UnitType=Units.CURRENCY, + PreferredUnits=CurrencyUnit.MDOLLARS, + CurrentUnits=CurrencyUnit.MDOLLARS + ) + self.cost_one_injection_well = self.OutputParameterDict[self.cost_one_injection_well.Name] = OutputParameter( + Name="Cost of One Injection Well", + UnitType=Units.CURRENCY, + PreferredUnits=CurrencyUnit.MDOLLARS, + CurrentUnits=CurrencyUnit.MDOLLARS + ) model.logger.info(f'Complete {__class__!s}: {sys._getframe().f_code.co_name}') @@ -1764,12 +1900,12 @@ def read_parameters(self, model: Model) -> None: " adjustment factor = 1.") ParameterToModify.value = 1.0 elif ParameterToModify.Name == "Well Drilling and Completion Capital Cost Adjustment Factor": - if self.ccwellfixed.Valid and ParameterToModify.Valid: + if self.per_production_well_cost.Valid and ParameterToModify.Valid: print("Warning: Provided well drilling and completion cost adjustment factor not" + " considered because valid total well drilling and completion cost provided.") model.logger.warning("Provided well drilling and completion cost adjustment factor not" + " considered because valid total well drilling and completion cost provided.") - elif not self.ccwellfixed.Provided and not self.ccwelladjfactor.Provided: + elif not self.per_production_well_cost.Provided and not self.production_well_cost_adjustment_factor.Provided: ParameterToModify.value = 1.0 print("Warning: No valid well drilling and completion total cost or adjustment" + " factor provided. GEOPHIRES will assume default built-in well drilling and" + @@ -1778,15 +1914,15 @@ def read_parameters(self, model: Model) -> None: "No valid well drilling and completion total cost or adjustment factor" + " provided. GEOPHIRES will assume default built-in well drilling and completion cost" + " correlation with adjustment factor = 1.") - elif self.ccwellfixed.Provided and not self.ccwellfixed.Valid: + elif self.per_production_well_cost.Provided and not self.per_production_well_cost.Valid: print("Warning: Provided well drilling and completion cost outside of range 0-1000." + " GEOPHIRES will assume default built-in well drilling and completion cost correlation" + " with adjustment factor = 1.") model.logger.warning("Provided well drilling and completion cost outside of range 0-1000." + " GEOPHIRES will assume default built-in well drilling and completion cost correlation with" + " adjustment factor = 1.") - self.ccwelladjfactor.value = 1.0 - elif not self.ccwellfixed.Provided and self.ccwelladjfactor.Provided and not self.ccwelladjfactor.Valid: + self.production_well_cost_adjustment_factor.value = 1.0 + elif not self.per_production_well_cost.Provided and self.production_well_cost_adjustment_factor.Provided and not self.production_well_cost_adjustment_factor.Valid: print("Warning: Provided well drilling and completion cost adjustment factor outside" + " of range 0-10. GEOPHIRES will assume default built-in well drilling and completion" + " cost correlation with adjustment factor = 1.") @@ -1794,7 +1930,7 @@ def read_parameters(self, model: Model) -> None: "Provided well drilling and completion cost adjustment factor outside" + " of range 0-10. GEOPHIRES will assume default built-in well drilling and completion" + " cost correlation with adjustment factor = 1.") - self.ccwelladjfactor.value = 1.0 + self.production_well_cost_adjustment_factor.value = 1.0 elif ParameterToModify.Name == "Wellfield O&M Cost Adjustment Factor": if self.oamtotalfixed.Valid: if self.oamwellfixed.Provided: @@ -2040,98 +2176,33 @@ def Calculate(self, model: Model) -> None: # well costs (using GeoVision drilling correlations). These are calculated whether totalcapcostvalid = 1 # start with the cost of one well # C1well is well drilling and completion cost in M$/well - if self.ccwellfixed.Valid: - self.C1well = self.ccwellfixed.value - self.Cwell.value = self.C1well * (model.wellbores.nprod.value + model.wellbores.ninj.value) + if self.per_production_well_cost.Valid: + self.cost_one_production_well.value = self.per_production_well_cost.value + if not self.per_injection_well_cost.Provided: + self.cost_one_injection_well.value = self.per_production_well_cost.value + else: + self.cost_one_injection_well.value = self.per_injection_well_cost.value + self.Cwell.value = ((self.cost_one_production_well.value * model.wellbores.nprod.value) + + (self.cost_one_injection_well.value * model.wellbores.ninj.value)) else: - # Check if well depth is out of standard bounds for cost correlation - checkdepth_m = model.reserv.depth.quantity().to('m').magnitude - - correlations_min_valid_depth_m = 500. - correlations_max_valid_depth_m = 7000. - - if (checkdepth_m < correlations_min_valid_depth_m - and not self.wellcorrelation.value == WellDrillingCostCorrelation.SIMPLE): - - self.wellcorrelation.value = WellDrillingCostCorrelation.SIMPLE - model.logger.warning( - f'Invalid cost correlation specified ({self.wellcorrelation.value}) for drilling depth ' - f'<{correlations_min_valid_depth_m}m ({checkdepth_m}m). ' - f'Falling back to simple user-specified cost ' - f'({self.Vertical_drilling_cost_per_m.value} per meter)' - ) - - if (checkdepth_m > correlations_max_valid_depth_m - and not self.wellcorrelation.value == WellDrillingCostCorrelation.SIMPLE): - model.logger.warning( - f'{self.wellcorrelation.value} may be invalid for drilling depth ' - f'>{correlations_max_valid_depth_m}m ({checkdepth_m}m). ' - f'Consider using {WellDrillingCostCorrelation.SIMPLE} (per-meter cost) or ' - f'{self.ccwellfixed.Name} (fixed cost per well) instead.' - ) - - - if self.wellcorrelation.value == WellDrillingCostCorrelation.SIMPLE: - # using the "Configuration" keywords means we are doing an AGS calculation - if hasattr(model.wellbores, 'Configuration'): - if model.wellbores.Configuration.value == Configuration.ULOOP: - # found out if we are using simple cylindrical model, which has an Input and Output Depth - if hasattr(model.reserv, 'InputDepth'): - # cost of one closed-loop well = cost of 2 verticals + cost of horizontal section(s) - self.C1well = ((self.Vertical_drilling_cost_per_m.value * (model.reserv.InputDepth.value * 1000.0)) + - (self.Vertical_drilling_cost_per_m.value * (model.reserv.OutputDepth.value * 1000.0)) + - (model.wellboew.numnonverticalsections.value * self.Nonvertical_drilling_cost_per_m.value * model.wellbores.Nonvertical_length.value)) * 1E-6 - else: - if hasattr(model.wellbores, 'Nonvertical_length'): - self.C1well = ((2 * self.Vertical_drilling_cost_per_m.value * (model.reserv.depth.value * 1000.0)) + - (self.Nonvertical_drilling_cost_per_m.value * model.wellbores.Nonvertical_length.value)) * 1E-6 - else: - self.C1well = (2 * self.Vertical_drilling_cost_per_m.value * (model.reserv.depth.value * 1000.0)) * 1E-6 - else: # Coaxial - self.C1well = ((self.Vertical_drilling_cost_per_m.value * (model.reserv.depth.value * 1000.0)) + - (self.Nonvertical_drilling_cost_per_m.value * model.wellbores.Nonvertical_length.value)) * 1E-6 - else: - self.C1well = self.Vertical_drilling_cost_per_m.value * model.reserv.depth.quantity().to( - 'm').magnitude * 1E-6 - - elif self.wellcorrelation.value == WellDrillingCostCorrelation.VERTICAL_SMALL: - self.C1well = (0.30212 * checkdepth_m ** 2 + 584.91124 * checkdepth_m + 751368.47270) * 1E-6 - elif self.wellcorrelation.value == WellDrillingCostCorrelation.VERTICAL_LARGE: - self.C1well = (0.28180 * checkdepth_m ** 2 + 1275.52130 * checkdepth_m + 632315.12640) * 1E-6 - elif self.wellcorrelation.value == WellDrillingCostCorrelation.DEVIATED_SMALL: - self.C1well = (0.28977 * checkdepth_m ** 2 + 882.15067 * checkdepth_m + 680562.50150) * 1E-6 - elif self.wellcorrelation.value == WellDrillingCostCorrelation.DEVIATED_LARGE: - self.C1well = (0.25528 * checkdepth_m ** 2 + 1716.71568 * checkdepth_m + 500866.89110) * 1E-6 - elif self.wellcorrelation.value == WellDrillingCostCorrelation.VERTICAL_SMALL_INT1: - self.C1well = (0.13710 * checkdepth_m ** 2 + 129.61033 * checkdepth_m + 1205587.57100) * 1E-6 - elif self.wellcorrelation.value == WellDrillingCostCorrelation.VERTICAL_LARGE_INT1: - self.C1well = (0.18927 * checkdepth_m ** 2 + 293.45174 * checkdepth_m + 1326526.31300) * 1E-6 - elif self.wellcorrelation.value == WellDrillingCostCorrelation.DEVIATED_SMALL_INT1: - self.C1well = (0.15340 * checkdepth_m ** 2 + 120.31700 * checkdepth_m + 1431801.54400) * 1E-6 - elif self.wellcorrelation.value == WellDrillingCostCorrelation.DEVIATED_LARGE_INT1: - self.C1well = (0.19950 * checkdepth_m ** 2 + 296.13011 * checkdepth_m + 1697867.70900) * 1E-6 - elif self.wellcorrelation.value == WellDrillingCostCorrelation.VERTICAL_SMALL_INT2: - self.C1well = (0.00804 * checkdepth_m ** 2 + 455.60507 * checkdepth_m + 921007.68680) * 1E-6 - elif self.wellcorrelation.value == WellDrillingCostCorrelation.VERTICAL_LARGE_INT2: - self.C1well = (0.00315 * checkdepth_m ** 2 + 782.69676 * checkdepth_m + 983620.25270) * 1E-6 - elif self.wellcorrelation.value == WellDrillingCostCorrelation.DEVIATED_SMALL_INT2: - self.C1well = (0.00854 * checkdepth_m ** 2 + 506.08357 * checkdepth_m + 1057330.39000) * 1E-6 - elif self.wellcorrelation.value == WellDrillingCostCorrelation.DEVIATED_LARGE_INT2: - self.C1well = (0.00380 * checkdepth_m ** 2 + 838.90249 * checkdepth_m + 1181947.04400) * 1E-6 - elif self.wellcorrelation.value == WellDrillingCostCorrelation.VERTICAL_SMALL_IDEAL: - self.C1well = (0.00252 * checkdepth_m ** 2 + 439.44503 * checkdepth_m + 590611.90110) * 1E-6 - elif self.wellcorrelation.value == WellDrillingCostCorrelation.VERTICAL_LARGE_IDEAL: - self.C1well = (-0.00240 * checkdepth_m ** 2 + 752.93946 * checkdepth_m + 524337.65380) * 1E-6 - elif self.wellcorrelation.value == WellDrillingCostCorrelation.DEVIATED_SMALL_IDEAL: - self.C1well = (0.00719 * checkdepth_m ** 2 + 455.85233 * checkdepth_m + 753377.73080) * 1E-6 - elif self.wellcorrelation.value == WellDrillingCostCorrelation.DEVIATED_LARGE_IDEAL: - self.C1well = (0.00376 * checkdepth_m ** 2 + 762.52696 * checkdepth_m + 765103.07690) * 1E-6 - # account for adjustment factor - self.C1well = self.ccwelladjfactor.value * self.C1well + self.cost_one_production_well.value = CalculateCostOfOneWell(model, model.reserv.depth.quantity().to('m').magnitude, + self.wellcorrelation.value, + self.Vertical_drilling_cost_per_m.value, + self.per_production_well_cost.Name, + self.production_well_cost_adjustment_factor.value) + if model.wellbores.ninj.value == 0: + self.cost_one_injection_well.value = -1.0 + else: + self.cost_one_injection_well.value = CalculateCostOfOneWell(model, model.wellbores.injection_reservoir_depth.value, + self.wellcorrelation.value, + self.Vertical_drilling_cost_per_m.value, + self.per_injection_well_cost.Name, + self.injection_well_cost_adjustment_factor.value) # cost of the well field - self.Cwell.value = 1.05 * self.C1well * ( - model.wellbores.nprod.value + model.wellbores.ninj.value) # 1.05 for 5% indirect costs + # 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)) # reservoir stimulation costs (M$/injection well). These are calculated whether totalcapcost.Valid = 1 if self.ccstimfixed.Valid: @@ -2403,7 +2474,7 @@ def Calculate(self, model: Model) -> None: self.Cexpl.value = self.ccexplfixed.value else: self.Cexpl.value = 1.15 * self.ccexpladjfactor.value * 1.12 * ( - 1. + self.C1well * 0.6) # 1.15 for 15% contingency and 1.12 for 12% indirect costs + 1. + self.cost_one_production_well.value * 0.6) # 1.15 for 15% contingency and 1.12 for 12% indirect costs # Surface Piping Length Costs (M$) #assumed $750k/km self.Cpiping.value = 750 / 1000 * model.surfaceplant.piping_length.value diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index f01fb258..39e21f6f 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -253,7 +253,8 @@ def Write_HTML_Output(html_path: str, simulation_metadata: list, summary: list, engineering_parameters: list, resource_characteristics: list, reservoir_parameters: list, reservoir_stimulation_results: list, CAPEX: list, OPEX: list, surface_equipment_results: list, sdac_results: list, addon_results: list, hce: pd.DataFrame, ahce: pd.DataFrame, - cashflow: pd.DataFrame, sdac_df: pd.DataFrame, addon_df: pd.DataFrame) -> None: + cashflow: pd.DataFrame, pumping_power_profiles: pd.DataFrame, + sdac_df: pd.DataFrame, addon_df: pd.DataFrame) -> None: """ This function writes out the HTML output :param html_path: the path to the HTML output file @@ -288,6 +289,8 @@ def Write_HTML_Output(html_path: str, simulation_metadata: list, summary: list, :type ahce: pd.DataFrame :param cashflow: the revenue & cashflow profile :type cashflow: pd.DataFrame + :param pumping_power_profiles: the pumping power profiles + :type pd.DataFrame :param sdac_df: the sdac dataframe :type sdac_df: pd.DataFrame :param addon_df: the addon dataframe @@ -325,6 +328,8 @@ def Write_HTML_Output(html_path: str, simulation_metadata: list, summary: list, Write_Complex_HTML_Table('HEATING, COOLING AND/OR ELECTRICITY PRODUCTION PROFILE', hce, 1, console) Write_Complex_HTML_Table('ANNUAL HEATING, COOLING AND/OR ELECTRICITY PRODUCTION PROFILE', ahce, 1, console) Write_Complex_HTML_Table('REVENUE & CASHFLOW PROFILE', cashflow, 1, console) + if len(pumping_power_profiles) > 0: + Write_Complex_HTML_Table('PUMPING POWER PROFILES', pumping_power_profiles, 1, console) if len(addon_df) > 0: Write_Complex_HTML_Table('ADD-ON PROFILE', addon_df, 1, console) if len(sdac_df) > 0: @@ -428,8 +433,8 @@ def Plot_Single_Graph(title: str, html_path: str, x: pd.array, y: pd.array, x_la def Plot_Tables_Into_HTML(enduse_option: intParameter, plant_type: intParameter, html_path: str, - hce: pd.DataFrame, ahce: pd.DataFrame, cashflow: pd.DataFrame, sdac_df: pd.DataFrame, - addon_df: pd.DataFrame) -> None: + hce: pd.DataFrame, ahce: pd.DataFrame, cashflow: pd.DataFrame, pumping_power_profiles: pd.DataFrame, + sdac_df: pd.DataFrame, addon_df: pd.DataFrame) -> None: """ This function plots the tables into the HTML :param enduse_option: the end use option @@ -444,6 +449,8 @@ def Plot_Tables_Into_HTML(enduse_option: intParameter, plant_type: intParameter, :type ahce: pd.DataFrame :param cashflow: the revenue & cashflow profile :type cashflow: pd.DataFrame + :param pumping_power_profiles: The pumping power profiles + :type pd.DataFrame :param sdac_df: the sdac dataframe :type sdac_df: pd.DataFrame :param addon_df: the addon dataframe @@ -553,6 +560,15 @@ def Plot_Tables_Into_HTML(enduse_option: intParameter, plant_type: intParameter, html_path, cashflow.values[0:, 1], cashflow.values[0:, 15], cashflow.values[0:, 16], cashflow.columns[1].split('|')[0], cashflow.columns[15].split('|')[0], cashflow.columns[16].split('|')[0]) + # Pumping Power Profiles Graphs + if len(pumping_power_profiles) > 0: + Plot_Twin_Graph('PUMPING POWER PROFILES: Production Pumping Power & Injection Pumping Power', html_path, + pumping_power_profiles.values[0:, 1], pumping_power_profiles.values[0:, 2], pumping_power_profiles.values[0:, 3], + pumping_power_profiles.columns[1].split('|')[0], pumping_power_profiles.columns[2].split('|')[0], pumping_power_profiles.columns[3].split('|')[0]) + Plot_Single_Graph('PUMPING POWER PROFILES: Pumping Power', html_path, + pumping_power_profiles.values[0:, 1], pumping_power_profiles.values[0:, 4], pumping_power_profiles.columns[1].split('|')[0], + pumping_power_profiles.columns[4].split('|')[0]) + if len (addon_df) > 0: Plot_Twin_Graph('ADD-ON PROFILE: Electricity Annual Price vs. Revenue', html_path, addon_df.values[0:, 1], addon_df.values[0:, 2], addon_df.values[0:, 3], @@ -759,6 +775,7 @@ def PrintOutputs(self, model: Model): surface_equipment_results = [] addon_results = [] sdac_resa_results = [] + pumping_power_results = [] simulation_metadata.append(OutputTableItem('GEOPHIRES Version', geophires_x.__version__)) simulation_metadata.append(OutputTableItem('GEOPHIRES Build Date', '2024-03-05')) @@ -1006,8 +1023,8 @@ def PrintOutputs(self, model: Model): model.wellbores.impedance.CurrentUnits.value)) else: reservoir_parameters.append(OutputTableItem('Average reservoir pressure', - '{0:10.2f}'.format(np.average(model.wellbores.production_reservoir_pressure.value)), - model.wellbores.production_reservoir_pressure.CurrentUnits.value)) + '{0:10.2f}'.format(model.wellbores.average_production_reservoir_pressure.value), + model.wellbores.average_production_reservoir_pressure.CurrentUnits.value)) reservoir_parameters.append(OutputTableItem('Plant outlet pressure', '{0:10.2f}'.format( model.surfaceplant.plant_outlet_pressure.value), model.surfaceplant.plant_outlet_pressure.CurrentUnits.value)) @@ -1099,8 +1116,18 @@ def PrintOutputs(self, model: Model): if not model.economics.totalcapcost.Valid: CAPEX.append(OutputTableItem('Drilling and completion costs', '{0:10.2f}'.format(model.economics.Cwell.value), model.economics.Cwell.CurrentUnits.value)) - CAPEX.append(OutputTableItem('Drilling and completion costs per well', '{0:10.2f}'.format( - model.economics.Cwell.value / (model.wellbores.nprod.value + model.wellbores.ninj.value)), + + if model.economics.cost_one_production_well.value != model.economics.cost_one_injection_well.value and \ + model.economics.cost_one_injection_well.value != -1: + CAPEX.append(OutputTableItem('Drilling and completion costs per production well', + '{0:10.2f}'.format(model.economics.cost_one_production_well.value, + model.economics.cost_one_production_well.CurrentUnits.value))) + CAPEX.append(OutputTableItem('Drilling and completion costs per injection well, ' + '{0:10.2f}'.format(model.economics.cost_one_injection_well.value, + model.economics.cost_one_injection_well.CurrentUnits.value))) + else: + CAPEX.append(OutputTableItem('Drilling and completion costs per well', '{0:10.2f}'.format( + model.economics.Cwell.value / (model.wellbores.nprod.value + model.wellbores.ninj.value)), model.economics.Cwell.CurrentUnits.value)) CAPEX.append(OutputTableItem('Stimulation costs', '{0:10.2f}'.format(model.economics.Cstim.value), model.economics.Cstim.CurrentUnits.value)) @@ -1492,6 +1519,26 @@ def PrintOutputs(self, model: Model): f'Project:Net Cashflow ({econ.TotalCummRevenue.CurrentUnits.value})|:5.2f'] = econ.TotalCummRevenue.value cashflow = cashflow.reset_index() + # Build the data frame to hold the pumping power profiles + pumping_power_profiles: pd.DataFrame = pd.DataFrame() + + if model.wellbores.overpressure_percentage.Provided and model.wellbores.injection_reservoir_depth.Provided: + # add the columns as needed based on the output. + # Note that the correct format for that column is stashed in the title of that column + # so that it can be used in the write statement. + pumping_power_profiles[f'Year|:2.0f'] = [i for i in range(1, (model.surfaceplant.plant_lifetime.value + 1))] + pumping_power_profiles[f'Prod Pump Power ({model.wellbores.PumpingPowerProd.CurrentUnits.value})|:8.4f'] = ShortenArrayToAnnual( + model.wellbores.PumpingPowerProd.value, + model.surfaceplant.plant_lifetime.value, model.economics.timestepsperyear.value) + pumping_power_profiles[f'Inject Pump Power ({model.wellbores.PumpingPowerInj.CurrentUnits.value})|:8.4f'] = ShortenArrayToAnnual( + model.wellbores.PumpingPowerInj.value, + model.surfaceplant.plant_lifetime.value, model.economics.timestepsperyear.value) + pumping_power_profiles[f'Pump Power ({model.wellbores.PumpingPower.CurrentUnits.value})|:8.4f'] = ShortenArrayToAnnual( + model.wellbores.PumpingPower.value, + model.surfaceplant.plant_lifetime.value, model.economics.timestepsperyear.value) + + pumping_power_profiles = pumping_power_profiles.reset_index() + addon_df = pd.DataFrame() sdac_df = pd.DataFrame() addon_results: list[OutputTableItem] = [] @@ -1673,7 +1720,7 @@ def PrintOutputs(self, model: Model): if model.wellbores.impedancemodelused.value: f.write(f' Reservoir impedance: {model.wellbores.impedance.value/1000:10.2f} ' + model.wellbores.impedance.CurrentUnits.value + NL) else: - f.write(f' Average reservoir pressure: {np.average(model.wellbores.production_reservoir_pressure.value):10.2f} ' + model.wellbores.production_reservoir_pressure.CurrentUnits.value + NL) + f.write(f' Average reservoir pressure: {model.wellbores.average_production_reservoir_pressure.value:10.2f} ' + model.wellbores.average_production_reservoir_pressure.CurrentUnits.value + NL) f.write(f' Plant outlet pressure: {model.surfaceplant.plant_outlet_pressure.value:10.2f} ' + model.surfaceplant.plant_outlet_pressure.CurrentUnits.value + NL) if model.wellbores.productionwellpumping.value: f.write(f' Production wellhead pressure: {model.wellbores.Pprodwellhead.value:10.2f} ' + model.wellbores.Pprodwellhead.CurrentUnits.value + NL) @@ -1726,7 +1773,12 @@ def PrintOutputs(self, model: Model): f.write(NL) if not model.economics.totalcapcost.Valid: f.write(f' Drilling and completion costs: {model.economics.Cwell.value:10.2f} ' + model.economics.Cwell.CurrentUnits.value + NL) - 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) + if econ.cost_one_production_well.value != econ.cost_one_injection_well.value and \ + 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) + 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) f.write(f' Surface power plant costs: {model.economics.Cplant.value:10.2f} ' + model.economics.Cplant.CurrentUnits.value + NL) if model.surfaceplant.plant_type.value == PlantType.ABSORPTION_CHILLER: @@ -2015,6 +2067,22 @@ def o(output_param: OutputParameter): f'{ii + 1:3.0f} {o(econ.ElecPrice).value[ii]:5.2f} {o(econ.ElecRevenue).value[ii]:5.2f} {o(econ.ElecCummRevenue).value[ii]:5.2f} | {o(econ.HeatPrice).value[ii]:5.2f} {o(econ.HeatRevenue).value[ii]:5.2f} {o(econ.HeatCummRevenue).value[ii]:5.2f} | {o(econ.CoolingPrice).value[ii]:5.2f} {o(econ.CoolingRevenue).value[ii]:5.2f} {o(econ.CoolingCummRevenue).value[ii]:5.2f} | {o(econ.CarbonPrice).value[ii]:5.2f} {o(econ.CarbonRevenue).value[ii]:5.2f} {o(econ.CarbonCummCashFlow).value[ii]:5.2f} | {opex:5.2f} {o(econ.TotalRevenue).value[ii]:5.2f} {o(econ.TotalCummRevenue).value[ii]:5.2f}\n') f.write(NL) + # if we are dealing with overpressure and two different reservoirs, show a table reporting the values + if model.wellbores.overpressure_percentage.Provided and model.wellbores.injection_reservoir_depth.Provided: + f.write(NL) + f.write(' ***************************************\n') + f.write(' * RESERVOIR POWER REQUIRED PROFILES *\n') + f.write(' ***************************************\n') + f.write(' YEAR PROD PUMP INJECT PUMP TOTAL PUMP\n') + f.write(' POWER POWER POWER\n') + f.write(' (' + model.wellbores.ProducedTemperature.CurrentUnits.value+') (' + model.wellbores.PumpingPower.CurrentUnits.value + ') (' + model.surfaceplant.NetElectricityProduced.CurrentUnits.value + ') (%)\n') + for i in range(0, model.surfaceplant.plant_lifetime.value): + f.write(' {0:2.0f} {1:8.4f} {2:8.4f} {3:8.4f}'.format(i+1, + model.wellbores.PumpingPowerProd.value[i*model.economics.timestepsperyear.value], + model.wellbores.PumpingPowerInj.value[i*model.economics.timestepsperyear.value], + model.wellbores.PumpingPower.value[i*model.economics.timestepsperyear.value])) + f.write(NL) + if model.economics.DoAddOnCalculations.value: addon_df, addon_results = model.addoutputs.PrintOutputs(model) if model.economics.DoSDACGTCalculations.value: @@ -2032,7 +2100,7 @@ def o(output_param: OutputParameter): if self.text_output_file.Provided: Write_Text_Output(self.output_file, simulation_metadata, summary, economic_parameters,engineering_parameters, resource_characteristics, reservoir_parameters, reservoir_stimulation_results, CAPEX, OPEX, - surface_equipment_results, sdac_results, addon_results, hce, ahce, cashflow, sdac_df, addon_df) + surface_equipment_results, sdac_results, addon_results, hce, ahce, cashflow, pumping_power_profiles, sdac_df, addon_df) # Get rid of any trailing spaces in that output file - they are confusing the testing code with open(self.output_file, 'r+') as fp: @@ -2050,10 +2118,10 @@ def o(output_param: OutputParameter): Write_HTML_Output(self.html_output_file.value, simulation_metadata, summary, economic_parameters, engineering_parameters, resource_characteristics, reservoir_parameters, reservoir_stimulation_results, CAPEX, OPEX, surface_equipment_results, sdac_results, - addon_results, hce, ahce, cashflow, sdac_df, addon_df) + addon_results, hce, ahce, cashflow, pumping_power_profiles, sdac_df, addon_df) Plot_Tables_Into_HTML(model.surfaceplant.enduse_option, model.surfaceplant.plant_type, - self.html_output_file.value, hce, ahce, cashflow, sdac_df, addon_df) + self.html_output_file.value, hce, ahce, cashflow, pumping_power_profiles, sdac_df, addon_df) # make district heating plot if model.surfaceplant.plant_type.value == PlantType.DISTRICT_HEATING: MakeDistrictHeatingPlot(self.html_output_file.value, model.surfaceplant.dh_geothermal_heating.value, diff --git a/src/geophires_x/Reservoir.py b/src/geophires_x/Reservoir.py index 03e4b727..5fc5d118 100644 --- a/src/geophires_x/Reservoir.py +++ b/src/geophires_x/Reservoir.py @@ -768,7 +768,7 @@ def Calculate(self, model: Model) -> None: model.logger.info(f'complete {str(__class__)}: {sys._getframe().f_code.co_name}') - def lithostatic_pressure(self, rho_rock: float, depth: float) -> PlainQuantity: - return quantity(static_pressure_MPa(rho_rock, depth), 'MPa') + def lithostatic_pressure(self, rho_rock_kg_per_m3: float, depth_m: float) -> PlainQuantity: + return quantity(static_pressure_MPa(rho_rock_kg_per_m3, depth_m), 'MPa') diff --git a/src/geophires_x/SUTRAOutputs.py b/src/geophires_x/SUTRAOutputs.py index dd9996dc..1226a40d 100644 --- a/src/geophires_x/SUTRAOutputs.py +++ b/src/geophires_x/SUTRAOutputs.py @@ -197,7 +197,13 @@ def PrintOutputs(self, model: Model): f.write(' ***CAPITAL COSTS (M$)***\n') f.write(NL) f.write(f" Drilling and Completion Costs: {model.economics.Cwell.value:10.2f} " + model.economics.Cwell.CurrentUnits.value + NL) - 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) + if model.economics.cost_one_production_well.value != model.economics.cost_one_injection_well.value: + f.write( + f' Drilling and completion costs per production well: {model.economics.cost_one_production_well.value:10.2f} ' + model.economics.cost_one_production_well.CurrentUnits.value + NL) + f.write( + f' Drilling and completion costs per injection well: {model.economics.cost_one_injection_well.value:10.2f} ' + model.economics.cost_one_injection_well.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" Auxiliary Heater Cost: {model.economics.peakingboilercost.value:10.2f} " + model.economics.peakingboilercost.CurrentUnits.value + NL) f.write(f" Pump Cost: {model.economics.Cpumps:10.2f} " + model.economics.peakingboilercost.CurrentUnits.value + NL) f.write(f" Total Capital Costs: {model.economics.CCap.value:10.2f} " + model.economics.CCap.CurrentUnits.value + NL) diff --git a/src/geophires_x/WellBores.py b/src/geophires_x/WellBores.py index 07153ee4..18139a05 100644 --- a/src/geophires_x/WellBores.py +++ b/src/geophires_x/WellBores.py @@ -11,18 +11,18 @@ from .OptionList import ReservoirModel -def InjectionReservoirPressurePredictor(project_lifetime: int, timesteps_per_year: int, initial_pressure: float, - inflation_rate: float) -> list: +def InjectionReservoirPressurePredictor(project_lifetime_yr: int, timesteps_per_year: int, initial_pressure_kPa: float, + inflation_rate: float) -> list: """ InjectionReservoirPressurePredictor builds the Injection Reservoir Pressure Array for the project lifetime based on the initial (perhaps hydrostatic) pressure and the inflation rate. There is no limit to how high the pressure can go. - :param project_lifetime: The lifetime of the project in years - :type project_lifetime: int + :param project_lifetime_yr: The lifetime of the project in years + :type project_lifetime_yr: int :param timesteps_per_year: The number of timesteps per year :type timesteps_per_year: int - :param initial_pressure: The initial pressure in kPa - :type initial_pressure: float + :param initial_pressure_kPa: The initial pressure in kPa + :type initial_pressure_kPa: float :param inflation_rate: The inflation rate in %/yr :type inflation_rate: float :return: pressure: The pressure array as a function of time. @@ -30,7 +30,7 @@ def InjectionReservoirPressurePredictor(project_lifetime: int, timesteps_per_yea """ # Initialize the pressure array with the initial hydrostatic pressure - pressure = [initial_pressure] * project_lifetime * timesteps_per_year + pressure = [initial_pressure_kPa] * project_lifetime_yr * timesteps_per_year # If the overpressure percentage is 0, # return the hydrostatic pressure array as a constant value equal to the initial hydrostatic pressure @@ -38,25 +38,25 @@ def InjectionReservoirPressurePredictor(project_lifetime: int, timesteps_per_yea return pressure # Calculate the initial pressure - pressure[0] = initial_pressure + pressure[0] = initial_pressure_kPa pressure_change_per_timestep = inflation_rate / timesteps_per_year - for current_timestep in range(1, project_lifetime * timesteps_per_year): - pressure[current_timestep] = initial_pressure + (pressure_change_per_timestep * current_timestep) + for current_timestep in range(1, project_lifetime_yr * timesteps_per_year): + pressure[current_timestep] = initial_pressure_kPa + (pressure_change_per_timestep * current_timestep) return pressure -def ReservoirPressurePredictor(project_lifetime: int, timesteps_per_year: int, initial_pressure: float, +def ReservoirPressurePredictor(project_lifetime_yr: int, timesteps_per_year: int, initial_pressure_kPa: float, overpressure_percentage: float, depletion_rate: float) -> list: """ ReservoirPressurePredictor builds the Reservoir Pressure Array for the project lifetime based on the initial (likely hydrostatic) pressure. the overpressure percentage and the depletion rate. Don't let the pressure drop below the initial pressure. - :param project_lifetime: The lifetime of the project in years - :type project_lifetime: int + :param project_lifetime_yr: The lifetime of the project in years + :type project_lifetime_yr: int :param timesteps_per_year: The number of timesteps per year :type timesteps_per_year: int - :param initial_pressure: The hydrostatic pressure in kPa - :type initial_pressure: float + :param initial_pressure_kPa: The hydrostatic pressure in kPa + :type initial_pressure_kPa: float :param overpressure_percentage: The overpressure percentage in % :type overpressure_percentage: float :param depletion_rate: The depletion rate in %/yr @@ -66,23 +66,23 @@ def ReservoirPressurePredictor(project_lifetime: int, timesteps_per_year: int, i """ # Initialize the hydrostatic pressure array with the initial hydrostatic pressure - pressure = [initial_pressure] * project_lifetime * timesteps_per_year + pressure = [initial_pressure_kPa] * project_lifetime_yr * timesteps_per_year - # If the overpressure percentage is 0, + # If the overpressure percentage is 100, # return the hydrostatic pressure array as a constant value equal to the initial pressure if overpressure_percentage == 100.0: return pressure # Calculate the initial overpressure - pressure[0] = initial_pressure * (overpressure_percentage / 100) - delta_pressure = (pressure[0] - initial_pressure) + pressure[0] = initial_pressure_kPa * (overpressure_percentage / 100) + delta_pressure = (pressure[0] - initial_pressure_kPa) depletion_timesteps = int((100.0 / depletion_rate) * timesteps_per_year) pressure_change_per_timestep = delta_pressure / depletion_timesteps for timestep in range(1, depletion_timesteps): pressure[timestep] = pressure[0] - (pressure_change_per_timestep * timestep) - if pressure[timestep] < initial_pressure: + if pressure[timestep] < initial_pressure_kPa: # If the pressure drops below the hydrostatic pressure, set it to the hydrostatic pressure and break out - pressure[timestep] = initial_pressure + pressure[timestep] = initial_pressure_kPa break return pressure @@ -164,7 +164,7 @@ def WellPressureDrop(model: Model, Taverage: float, wellflowrate: float, welldia rhowater = np.array([ density_water_kg_per_m3( t, - pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.depth.value), + pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.depth.quantity().to('m').magnitude), ) for t in Taverage ]) # replace with correlation based on Tprodaverage @@ -172,7 +172,7 @@ def WellPressureDrop(model: Model, Taverage: float, wellflowrate: float, welldia muwater = np.array([ viscosity_water_Pa_sec( t, - pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.depth.value), + pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, model.reserv.depth.quantity().to('m').magnitude), ) for t in Taverage ]) # replace with correlation based on Tprodaverage @@ -229,12 +229,12 @@ def InjectionWellPressureDrop(model: Model, Taverage: float, wellflowrate: float # start by calculating wellbore fluid conditions [kPa], noting that most temperature drop happens in # upper section (because surrounding rock temperature is lowest in upper section) rhowater = (density_water_kg_per_m3(Taverage, pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, - model.reserv.depth.value)) + model.reserv.depth.quantity().to('m').magnitude)) * np.linspace(1, 1, len(model.wellbores.ProducedTemperature.value))) # replace with correlation based on Tinjaverage muwater = viscosity_water_Pa_sec(Taverage, pressure=model.reserv.lithostatic_pressure(model.reserv.rhorock.value, - model.reserv.depth.value)) * np.linspace(1, 1, len(model.wellbores.ProducedTemperature.value)) + model.reserv.depth.quantity().to('m').magnitude)) * np.linspace(1, 1, len(model.wellbores.ProducedTemperature.value)) v = nprod / ninj * wellflowrate * (1.0 + waterloss) / rhowater / (math.pi / 4. * welldiam ** 2) Rewater = 4. * nprod / ninj * wellflowrate * (1.0 + waterloss) / ( muwater * math.pi * welldiam) # laminar or turbulent flow? @@ -896,6 +896,12 @@ def __init__(self, model: Model): PreferredUnits=PressureUnit.KPASCAL, CurrentUnits=PressureUnit.KPASCAL ) + self.average_production_reservoir_pressure = self.OutputParameterDict[self.average_production_reservoir_pressure.Name] = OutputParameter( + Name="Average Reservoir Pressure", + UnitType=Units.PRESSURE, + PreferredUnits=PressureUnit.KPASCAL, + CurrentUnits=PressureUnit.KPASCAL + ) self.injection_reservoir_pressure = self.OutputParameterDict[self.injection_reservoir_pressure.Name] = OutputParameter( Name="Calculated Injection Reservoir Pressure", value=-1, @@ -1082,7 +1088,7 @@ def Calculate(self, model: Model) -> None: model.reserv.depth.quantity().to('m').magnitude, model.reserv.averagegradient.value, model.reserv.lithostatic_pressure(model.reserv.rhorock.value, - model.reserv.depth.value)) if self.usebuiltinhydrostaticpressurecorrelation else self.Phydrostatic.quantity().to( + model.reserv.depth.quantity().to('m').magnitude)) if self.usebuiltinhydrostaticpressurecorrelation else self.Phydrostatic.quantity().to( self.production_reservoir_pressure.CurrentUnits).magnitude self.production_reservoir_pressure.value = ReservoirPressurePredictor(model.surfaceplant.plant_lifetime.value, @@ -1090,6 +1096,7 @@ def Calculate(self, model: Model) -> None: self.production_reservoir_pressure.value, self.overpressure_percentage.value, self.overpressure_depletion_rate.value) + self.average_production_reservoir_pressure.value = np.average(self.production_reservoir_pressure.value) if self.overpressure_percentage.Provided: # if we are doing an overpressure calculation, it is possible that the user has chosen to @@ -1106,13 +1113,13 @@ def Calculate(self, model: Model) -> None: self.injection_reservoir_depth.value, model.reserv.averagegradient.value * 1000.0, model.reserv.lithostatic_pressure(model.reserv.rhorock.value, - self.injection_reservoir_depth.value)) + self.injection_reservoir_depth.quantity().to('m').magnitude)) self.injection_reservoir_initial_pressure.value = self.injection_reservoir_pressure.value = get_hydrostatic_pressure_kPa(self.injection_reservoir_temperature.value, model.reserv.Tsurf.value, self.injection_reservoir_depth.value, model.reserv.averagegradient.value, model.reserv.lithostatic_pressure(model.reserv.rhorock.value, - self.injection_reservoir_depth.value)) + self.injection_reservoir_depth.quantity().to('m').magnitude)) # if not self.injection_reservoir_initial_pressure.Provided: self.injection_reservoir_pressure.value = InjectionReservoirPressurePredictor(model.surfaceplant.plant_lifetime.value, diff --git a/src/geophires_x_client/geophires_x_result.py b/src/geophires_x_client/geophires_x_result.py index 9231597f..aa7c167e 100644 --- a/src/geophires_x_client/geophires_x_result.py +++ b/src/geophires_x_client/geophires_x_result.py @@ -189,6 +189,8 @@ class GeophiresXResult: 'CAPITAL COSTS (M$)': [ 'Drilling and completion costs', 'Drilling and completion costs per well', + 'Drilling and completion costs per production well', + 'Drilling and completion costs per injection well', 'Stimulation costs', 'Surface power plant costs', 'of which Absorption Chiller Cost', @@ -205,6 +207,8 @@ class GeophiresXResult: # SUTRA 'Drilling and Completion Costs', 'Drilling and Completion Costs per Well', + 'Drilling and completion costs per production well', + 'Drilling and completion costs per injection well', 'Auxiliary Heater Cost', 'Pump Cost', 'Total Capital Costs', diff --git a/tests/examples/Wanju_Yuan_Closed-Loop_Geothermal_Energy_Recovery.out b/tests/examples/Wanju_Yuan_Closed-Loop_Geothermal_Energy_Recovery.out index cdbf7b20..451d2870 100644 --- a/tests/examples/Wanju_Yuan_Closed-Loop_Geothermal_Energy_Recovery.out +++ b/tests/examples/Wanju_Yuan_Closed-Loop_Geothermal_Energy_Recovery.out @@ -4,17 +4,17 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.4.24 + GEOPHIRES Version: 3.4.25 GEOPHIRES Build Date: 2024-03-05 - Simulation Date: 2024-04-15 - Simulation Time: 17:40 - Calculation Time: 3.005 sec + Simulation Date: 2024-04-23 + Simulation Time: 11:13 + Calculation Time: 2.927 sec ***SUMMARY OF RESULTS*** End-Use Option: Electricity - Average Net Electricity Production: 1.10 MW - Electricity breakeven price: 120.14 cents/kWh + Average Net Electricity Production: 1.11 MW + Electricity breakeven price: 119.74 cents/kWh Number of production wells: 1 Number of injection wells: 0 Flowrate per production well: 110.0 kg/sec @@ -28,7 +28,7 @@ Simulation Metadata Accrued financing during construction: 0.00 Project lifetime: 40 yr Capacity factor: 90.0 % - Project NPV: -134.75 MUSD + Project NPV: -134.71 MUSD Project IRR: 0.00 % Project VIR=PI=PIR: -0.06 Project MOIC: -0.89 @@ -78,10 +78,10 @@ The AGS models contain an intrinsic reservoir model that doesn't expose values t Drilling and completion costs per well: 68.08 MUSD Stimulation costs: 0.00 MUSD Surface power plant costs: 6.74 MUSD - Field gathering system costs: 0.51 MUSD - Total surface equipment costs: 7.24 MUSD + Field gathering system costs: 0.49 MUSD + Total surface equipment costs: 7.23 MUSD Exploration costs: 51.40 MUSD - Total capital costs: 126.72 MUSD + Total capital costs: 126.70 MUSD ***OPERATING AND MAINTENANCE COSTS (M$/yr)*** @@ -99,13 +99,13 @@ The AGS models contain an intrinsic reservoir model that doesn't expose values t Average Total Electricity Generation: 1.11 MW Minimum Total Electricity Generation: 0.94 MW Initial Total Electricity Generation: 1.66 MW - Maximum Net Electricity Generation: 1.66 MW - Average Net Electricity Generation: 1.10 MW + Maximum Net Electricity Generation: 1.67 MW + Average Net Electricity Generation: 1.11 MW Minimum Net Electricity Generation: 0.94 MW Initial Net Electricity Generation: 1.66 MW Average Annual Total Electricity Generation: 8.64 GWh - Average Annual Net Electricity Generation: 8.61 GWh - Initial pumping power/net installed power: 0.23 % + Average Annual Net Electricity Generation: 8.64 GWh + Initial pumping power/net installed power: 0.04 % Average Pumping Power: 0.00 MW ************************************************************ @@ -114,46 +114,46 @@ The AGS models contain an intrinsic reservoir model that doesn't expose values t YEAR THERMAL GEOFLUID PUMP NET FIRST LAW DRAWDOWN TEMPERATURE POWER POWER EFFICIENCY (degC) (MW) (MW) (%) - 1 1.0000 120.00 0.0038 1.6608 5.9406 - 2 0.9300 111.61 0.0038 1.2888 5.3599 - 3 0.9186 110.23 0.0038 1.2333 5.2698 - 4 0.9119 109.43 0.0038 1.2020 5.2187 - 5 0.9073 108.87 0.0038 1.1804 5.1832 - 6 0.9037 108.45 0.0038 1.1639 5.1561 - 7 0.9008 108.10 0.0038 1.1507 5.1343 - 8 0.8984 107.81 0.0038 1.1397 5.1160 - 9 0.8963 107.56 0.0038 1.1303 5.1004 - 10 0.8945 107.34 0.0038 1.1220 5.0867 - 11 0.8929 107.14 0.0038 1.1148 5.0746 - 12 0.8914 106.97 0.0038 1.1082 5.0637 - 13 0.8901 106.81 0.0038 1.1023 5.0539 - 14 0.8889 106.66 0.0038 1.0969 5.0448 - 15 0.8877 106.53 0.0038 1.0919 5.0365 - 16 0.8867 106.40 0.0038 1.0873 5.0289 - 17 0.8857 106.29 0.0038 1.0830 5.0217 - 18 0.8848 106.18 0.0038 1.0790 5.0150 - 19 0.8839 106.07 0.0038 1.0753 5.0088 - 20 0.8831 105.98 0.0038 1.0717 5.0028 - 21 0.8824 105.88 0.0038 1.0684 4.9973 - 22 0.8816 105.80 0.0038 1.0653 4.9920 - 23 0.8809 105.71 0.0038 1.0623 4.9870 - 24 0.8803 105.63 0.0038 1.0594 4.9822 - 25 0.8797 105.56 0.0038 1.0567 4.9776 - 26 0.8791 105.49 0.0038 1.0541 4.9733 - 27 0.8785 105.42 0.0038 1.0516 4.9691 - 28 0.8779 105.35 0.0038 1.0492 4.9651 - 29 0.8774 105.29 0.0038 1.0469 4.9612 - 30 0.8769 105.23 0.0038 1.0447 4.9575 - 31 0.8764 105.17 0.0038 1.0426 4.9540 - 32 0.8759 105.11 0.0038 1.0406 4.9505 - 33 0.8755 105.05 0.0038 1.0386 4.9472 - 34 0.8750 105.00 0.0038 1.0367 4.9440 - 35 0.8746 104.95 0.0038 1.0348 4.9409 - 36 0.8742 104.90 0.0038 1.0330 4.9379 - 37 0.8737 104.85 0.0038 1.0313 4.9350 - 38 0.8733 104.80 0.0038 1.0296 4.9321 - 39 0.8730 104.76 0.0038 1.0280 4.9294 - 40 0.8726 104.71 0.0038 1.0264 4.9267 + 1 1.0000 120.00 0.0007 1.6639 6.2352 + 2 0.9302 111.62 0.0007 1.2924 5.6297 + 3 0.9187 110.24 0.0007 1.2369 5.5357 + 4 0.9121 109.45 0.0007 1.2056 5.4824 + 5 0.9074 108.89 0.0007 1.1840 5.4453 + 6 0.9039 108.46 0.0007 1.1676 5.4171 + 7 0.9010 108.12 0.0007 1.1544 5.3943 + 8 0.8985 107.83 0.0007 1.1434 5.3753 + 9 0.8965 107.58 0.0007 1.1339 5.3590 + 10 0.8946 107.36 0.0007 1.1257 5.3448 + 11 0.8930 107.16 0.0007 1.1184 5.3321 + 12 0.8915 106.98 0.0007 1.1119 5.3208 + 13 0.8902 106.82 0.0007 1.1059 5.3105 + 14 0.8890 106.68 0.0007 1.1005 5.3011 + 15 0.8879 106.54 0.0007 1.0955 5.2925 + 16 0.8868 106.42 0.0007 1.0909 5.2844 + 17 0.8858 106.30 0.0007 1.0867 5.2770 + 18 0.8849 106.19 0.0007 1.0827 5.2700 + 19 0.8841 106.09 0.0007 1.0789 5.2635 + 20 0.8833 105.99 0.0007 1.0754 5.2573 + 21 0.8825 105.90 0.0007 1.0721 5.2515 + 22 0.8818 105.81 0.0007 1.0689 5.2460 + 23 0.8811 105.73 0.0007 1.0659 5.2408 + 24 0.8804 105.65 0.0007 1.0631 5.2358 + 25 0.8798 105.58 0.0007 1.0603 5.2310 + 26 0.8792 105.50 0.0007 1.0577 5.2265 + 27 0.8786 105.43 0.0007 1.0552 5.2221 + 28 0.8781 105.37 0.0007 1.0529 5.2180 + 29 0.8775 105.30 0.0007 1.0506 5.2140 + 30 0.8770 105.24 0.0007 1.0484 5.2101 + 31 0.8765 105.18 0.0007 1.0462 5.2064 + 32 0.8761 105.13 0.0007 1.0442 5.2028 + 33 0.8756 105.07 0.0007 1.0422 5.1993 + 34 0.8751 105.02 0.0007 1.0403 5.1960 + 35 0.8747 104.97 0.0007 1.0385 5.1928 + 36 0.8743 104.91 0.0007 1.0367 5.1896 + 37 0.8739 104.87 0.0007 1.0349 5.1866 + 38 0.8735 104.82 0.0007 1.0333 5.1836 + 39 0.8731 104.77 0.0007 1.0316 5.1808 + 40 0.8727 104.73 0.0007 1.0300 5.1780 ******************************************************************* @@ -162,46 +162,46 @@ The AGS models contain an intrinsic reservoir model that doesn't expose values t YEAR ELECTRICITY HEAT RESERVOIR PERCENTAGE OF PROVIDED EXTRACTED HEAT CONTENT TOTAL HEAT MINED (GWh/year) (GWh/year) (10^15 J) (%) - 1 12.1 210.1 32.75 2.26 - 2 9.5 182.1 32.09 4.21 - 3 9.8 185.9 31.43 6.21 - 4 9.2 178.4 30.78 8.13 - 5 9.4 180.4 30.13 10.07 - 6 9.0 176.0 29.50 11.96 - 7 9.1 177.2 28.86 13.86 - 8 8.9 174.2 28.23 15.73 - 9 8.9 175.1 27.60 17.61 - 10 8.8 172.9 26.98 19.47 - 11 8.8 173.4 26.36 21.33 - 12 8.7 171.8 25.74 23.18 - 13 8.7 172.1 25.12 25.03 - 14 8.6 170.8 24.50 26.86 - 15 8.6 171.0 23.89 28.70 - 16 8.5 170.0 23.28 30.53 - 17 8.5 170.0 22.67 32.35 - 18 8.5 169.3 22.06 34.17 - 19 8.5 169.1 21.45 35.99 - 20 8.4 168.7 20.84 37.80 - 21 8.4 168.4 20.23 39.61 - 22 8.4 168.2 19.63 41.42 - 23 8.4 167.7 19.02 43.22 - 24 8.4 167.7 18.42 45.02 - 25 8.3 167.0 17.82 46.82 - 26 8.3 167.3 17.22 48.61 - 27 8.3 166.4 16.62 50.40 - 28 8.3 166.9 16.02 52.20 - 29 8.2 165.7 15.42 53.98 - 30 8.3 166.6 14.82 55.77 - 31 8.2 165.1 14.23 57.54 - 32 8.3 166.4 13.63 59.33 - 33 8.1 164.4 13.04 61.10 - 34 8.3 166.4 12.44 62.88 - 35 8.0 163.6 11.85 64.64 - 36 8.3 166.6 11.25 66.43 - 37 7.9 162.3 10.66 68.18 - 38 8.4 167.9 10.06 69.98 - 39 7.7 158.3 9.49 71.68 - 40 7.5 140.7 8.98 73.19 + 1 12.1 200.5 32.78 2.15 + 2 9.5 173.8 32.16 4.02 + 3 9.9 177.5 31.52 5.93 + 4 9.2 170.3 30.91 7.76 + 5 9.4 172.2 30.29 9.61 + 6 9.0 168.1 29.68 11.42 + 7 9.1 169.2 29.07 13.23 + 8 8.9 166.4 28.47 15.02 + 9 9.0 167.2 27.87 16.82 + 10 8.8 165.1 27.28 18.59 + 11 8.8 165.6 26.68 20.37 + 12 8.7 164.0 26.09 22.13 + 13 8.7 164.3 25.50 23.90 + 14 8.6 163.1 24.91 25.65 + 15 8.6 163.2 24.32 27.40 + 16 8.6 162.3 23.74 29.15 + 17 8.6 162.3 23.16 30.89 + 18 8.5 161.7 22.57 32.63 + 19 8.5 161.5 21.99 34.36 + 20 8.5 161.1 21.41 36.10 + 21 8.4 160.8 20.83 37.82 + 22 8.4 160.6 20.26 39.55 + 23 8.4 160.1 19.68 41.27 + 24 8.4 160.1 19.10 42.99 + 25 8.3 159.4 18.53 44.70 + 26 8.4 159.7 17.95 46.42 + 27 8.3 158.8 17.38 48.12 + 28 8.3 159.4 16.81 49.84 + 29 8.2 158.3 16.24 51.54 + 30 8.3 159.1 15.67 53.25 + 31 8.2 157.7 15.10 54.94 + 32 8.3 158.9 14.53 56.65 + 33 8.1 157.0 13.96 58.33 + 34 8.3 158.9 13.39 60.04 + 35 8.1 156.2 12.83 61.72 + 36 8.3 159.1 12.25 63.43 + 37 8.0 155.0 11.70 65.10 + 38 8.4 160.3 11.12 66.82 + 39 7.7 151.2 10.57 68.44 + 40 7.6 134.4 10.09 69.89 ******************************** @@ -211,43 +211,43 @@ Year Electricity | Heat | Since Price Ann. Rev. Cumm. Rev. | Price Ann. Rev. Cumm. Rev. | Price Ann. Rev. Cumm. Rev. | Price Ann. Rev. Cumm. Rev. | OPEX Net Rev. Net Cashflow Start (cents/kWh)(MUSD/yr) (MUSD) |(cents/kWh) (MUSD/yr) (MUSD) |(cents/kWh) (MUSD/yr) (MUSD) |(USD/tonne) (MUSD/yr) (MUSD) |(MUSD/yr) (MUSD/yr) (MUSD) ________________________________________________________________________________________________________________________________________________________________________________________ - 1 0.00 -126.72 0.00 | 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 -126.72 -126.72 - 2 5.50 -0.38 0.66 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.38 -127.10 - 3 5.50 -0.52 1.19 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.52 -127.62 - 4 5.50 -0.51 1.73 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.51 -128.13 - 5 5.50 -0.54 2.24 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.54 -128.67 - 6 5.50 -0.53 2.75 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.53 -129.20 - 7 5.50 -0.55 3.25 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.55 -129.75 - 8 5.50 -0.55 3.75 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.55 -130.30 - 9 5.50 -0.56 4.24 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.56 -130.85 - 10 5.50 -0.55 4.73 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.55 -131.41 - 11 5.50 -0.56 5.21 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.56 -131.97 - 12 5.50 -0.56 5.70 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.56 -132.54 - 13 5.50 -0.57 6.17 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.57 -133.11 - 14 5.50 -0.57 6.65 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.57 -133.67 - 15 5.50 -0.57 7.12 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.57 -134.25 - 16 5.50 -0.57 7.60 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.57 -134.82 - 17 5.50 -0.58 8.07 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.58 -135.40 - 18 5.50 -0.58 8.54 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.58 -135.97 - 19 5.50 -0.58 9.00 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.58 -136.55 - 20 5.50 -0.58 9.47 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.58 -137.13 - 21 5.50 -0.58 9.93 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.58 -137.72 - 22 5.50 -0.58 10.40 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.58 -138.30 - 23 5.50 -0.58 10.86 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.58 -138.89 - 24 5.50 -0.59 11.32 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -139.47 - 25 5.50 -0.59 11.78 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -140.06 - 26 5.50 -0.59 12.23 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -140.65 - 27 5.50 -0.59 12.69 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -141.24 - 28 5.50 -0.59 13.14 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -141.83 - 29 5.50 -0.59 13.60 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -142.42 - 30 5.50 -0.60 14.05 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.60 -143.02 - 31 5.50 -0.59 14.51 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -143.61 - 32 5.50 -0.60 14.96 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.60 -144.21 - 33 5.50 -0.59 15.41 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -144.80 - 34 5.50 -0.60 15.86 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.60 -145.40 - 35 5.50 -0.59 16.31 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -145.99 - 36 5.50 -0.60 16.75 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.60 -146.60 - 37 5.50 -0.59 17.21 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -147.19 - 38 5.50 -0.61 17.64 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.61 -147.80 - 39 5.50 -0.59 18.11 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -148.38 - 40 5.50 -0.63 18.53 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.63 -149.01 + 1 0.00 -126.70 0.00 | 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 -126.70 -126.70 + 2 5.50 -0.38 0.67 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.38 -127.08 + 3 5.50 -0.52 1.19 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.52 -127.61 + 4 5.50 -0.50 1.73 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.50 -128.11 + 5 5.50 -0.54 2.24 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.54 -128.65 + 6 5.50 -0.53 2.76 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.53 -129.18 + 7 5.50 -0.55 3.26 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.55 -129.72 + 8 5.50 -0.54 3.76 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.54 -130.27 + 9 5.50 -0.56 4.25 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.56 -130.82 + 10 5.50 -0.55 4.74 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.55 -131.38 + 11 5.50 -0.56 5.23 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.56 -131.94 + 12 5.50 -0.56 5.71 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.56 -132.50 + 13 5.50 -0.57 6.19 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.57 -133.07 + 14 5.50 -0.57 6.67 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.57 -133.63 + 15 5.50 -0.57 7.15 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.57 -134.21 + 16 5.50 -0.57 7.62 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.57 -134.78 + 17 5.50 -0.58 8.09 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.58 -135.35 + 18 5.50 -0.58 8.56 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.58 -135.93 + 19 5.50 -0.58 9.03 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.58 -136.51 + 20 5.50 -0.58 9.50 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.58 -137.09 + 21 5.50 -0.58 9.96 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.58 -137.67 + 22 5.50 -0.58 10.43 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.58 -138.25 + 23 5.50 -0.58 10.89 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.58 -138.83 + 24 5.50 -0.59 11.35 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -139.42 + 25 5.50 -0.59 11.81 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -140.00 + 26 5.50 -0.59 12.27 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -140.59 + 27 5.50 -0.59 12.73 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -141.18 + 28 5.50 -0.59 13.19 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -141.77 + 29 5.50 -0.59 13.64 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -142.36 + 30 5.50 -0.59 14.10 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -142.95 + 31 5.50 -0.59 14.55 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -143.54 + 32 5.50 -0.60 15.00 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.60 -144.14 + 33 5.50 -0.59 15.46 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -144.73 + 34 5.50 -0.60 15.91 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.60 -145.33 + 35 5.50 -0.59 16.36 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -145.92 + 36 5.50 -0.60 16.81 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.60 -146.52 + 37 5.50 -0.59 17.26 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.59 -147.11 + 38 5.50 -0.61 17.70 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.61 -147.72 + 39 5.50 -0.58 18.17 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.58 -148.30 + 40 5.50 -0.62 18.59 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.05 -0.62 -148.92 diff --git a/tests/examples/example_overpressure.out b/tests/examples/example_overpressure.out index d33cc4c4..02d8723d 100644 --- a/tests/examples/example_overpressure.out +++ b/tests/examples/example_overpressure.out @@ -4,22 +4,22 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.4.24 + GEOPHIRES Version: 3.4.25 GEOPHIRES Build Date: 2024-03-05 - Simulation Date: 2024-04-16 - Simulation Time: 11:33 - Calculation Time: 1.372 sec + Simulation Date: 2024-04-21 + Simulation Time: 20:27 + Calculation Time: 1.553 sec ***SUMMARY OF RESULTS*** End-Use Option: Electricity - Average Net Electricity Production: 6.33 MW + Average Net Electricity Production: 5.25 MW Electricity breakeven price: 9.35 cents/kWh Number of production wells: 2 Number of injection wells: 2 Flowrate per production well: 70.0 kg/sec Well depth (or total length, if not vertical): 3.0 kilometer - Geothermal gradient: 0.0500 degC/m + Geothermal gradient: 0.0470 degC/m ***ECONOMIC PARAMETERS*** @@ -29,11 +29,11 @@ Simulation Metadata Accrued financing during construction: 0.00 Project lifetime: 30 yr Capacity factor: 90.0 % - Project NPV: 1.41 MUSD - Project IRR: 6.45 % - Project VIR=PI=PIR: 1.02 - Project MOIC: 0.81 - Project Payback Period: 14.70 yr + Project NPV: 1.70 MUSD + Project IRR: 6.54 % + Project VIR=PI=PIR: 1.04 + Project MOIC: 0.79 + Project Payback Period: 14.52 yr ***ENGINEERING PARAMETERS*** @@ -44,7 +44,7 @@ Simulation Metadata Pump efficiency: 80.0 Injection temperature: 50.0 degC Production Wellbore heat transmission calculated with Ramey's model - Average production well temperature drop: 2.5 degC + Average production well temperature drop: 2.4 degC Flowrate per production well: 70.0 kg/sec Injection well casing ID: 9.000 in Production well casing ID: 9.000 in @@ -56,18 +56,18 @@ Simulation Metadata Maximum reservoir temperature: 400.0 degC Number of segments: 1 - Geothermal gradient: 0.0500 degC/m + Geothermal gradient: 0.0470 degC/m ***RESERVOIR PARAMETERS*** Reservoir Model = Multiple Parallel Fractures Model - Bottom-hole temperature: 170.00 degC + Bottom-hole temperature: 161.00 degC Fracture model = Square Well separation: fracture height: 900.00 meter Fracture area: 810000.00 m**2 Reservoir volume: 1000000000 m**3 - Average reservoir pressure: 32172.94 kPa + Average reservoir pressure: 32283.22 kPa Plant outlet pressure: 381.21 kPa Production wellhead pressure: 450.16 kPa Productivity Index: 5.00 kg/sec/bar @@ -79,52 +79,53 @@ Simulation Metadata ***RESERVOIR SIMULATION RESULTS*** - Maximum Production Temperature: 167.7 degC - Average Production Temperature: 167.5 degC - Minimum Production Temperature: 166.0 degC - Initial Production Temperature: 166.0 degC - Average Reservoir Heat Extraction: 66.96 MW + Maximum Production Temperature: 158.8 degC + Average Production Temperature: 158.6 degC + Minimum Production Temperature: 157.2 degC + Initial Production Temperature: 157.2 degC + Average Reservoir Heat Extraction: 61.87 MW Production Wellbore Heat Transmission Model = Ramey Model - Average Production Well Temperature Drop: 2.5 degC - Average Injection Well Pump Pressure Drop: 4005.2 kPa - Average Production Well Pump Pressure Drop: -1627.7 kPa + Average Production Well Temperature Drop: 2.4 degC + Average Injection Well Pump Pressure Drop: 4010.8 kPa + Average Production Well Pump Pressure Drop: -1679.9 kPa ***CAPITAL COSTS (M$)*** - Drilling and completion costs: 21.95 MUSD - Drilling and completion costs per well: 5.49 MUSD + Drilling and completion costs: 14.42 MUSD + Drilling and completion costs per production well: 5.23 MUSD + Drilling and completion costs per injection well: 1.64 MUSD Stimulation costs: 3.02 MUSD - Surface power plant costs: 26.25 MUSD - Field gathering system costs: 3.05 MUSD - Total surface equipment costs: 29.30 MUSD + Surface power plant costs: 22.68 MUSD + Field gathering system costs: 3.04 MUSD + Total surface equipment costs: 25.72 MUSD Exploration costs: 5.33 MUSD - Total capital costs: 59.59 MUSD - Annualized capital costs: 2.98 MUSD + Total capital costs: 48.48 MUSD + Annualized capital costs: 2.42 MUSD ***OPERATING AND MAINTENANCE COSTS (M$/yr)*** - Wellfield maintenance costs: 0.49 MUSD/yr - Power plant maintenance costs: 1.10 MUSD/yr + Wellfield maintenance costs: 0.38 MUSD/yr + Power plant maintenance costs: 0.97 MUSD/yr Water costs: 0.07 MUSD/yr - Total operating and maintenance costs: 1.66 MUSD/yr + Total operating and maintenance costs: 1.43 MUSD/yr ***SURFACE EQUIPMENT SIMULATION RESULTS*** - Initial geofluid availability: 0.12 MW/(kg/s) - Maximum Total Electricity Generation: 7.20 MW - Average Total Electricity Generation: 7.17 MW - Minimum Total Electricity Generation: 6.98 MW - Initial Total Electricity Generation: 6.98 MW - Maximum Net Electricity Generation: 6.88 MW - Average Net Electricity Generation: 6.33 MW - Minimum Net Electricity Generation: 5.76 MW - Initial Net Electricity Generation: 6.81 MW - Average Annual Total Electricity Generation: 56.24 GWh - Average Annual Net Electricity Generation: 49.64 GWh - Initial pumping power/net installed power: 2.54 % + Initial geofluid availability: 0.10 MW/(kg/s) + Maximum Total Electricity Generation: 6.11 MW + Average Total Electricity Generation: 6.09 MW + Minimum Total Electricity Generation: 5.92 MW + Initial Total Electricity Generation: 5.92 MW + Maximum Net Electricity Generation: 5.80 MW + Average Net Electricity Generation: 5.25 MW + Minimum Net Electricity Generation: 4.68 MW + Initial Net Electricity Generation: 5.74 MW + Average Annual Total Electricity Generation: 47.72 GWh + Average Annual Net Electricity Generation: 41.16 GWh + Initial pumping power/net installed power: 3.03 % Average Pumping Power: 0.84 MW ************************************************************ @@ -133,36 +134,36 @@ Simulation Metadata YEAR THERMAL GEOFLUID PUMP NET FIRST LAW DRAWDOWN TEMPERATURE POWER POWER EFFICIENCY (degC) (MW) (MW) (%) - 1 1.0000 165.96 0.1731 6.8058 10.2983 - 2 1.0052 166.82 0.2085 6.8787 10.3321 - 3 1.0066 167.06 0.2439 6.8739 10.3036 - 4 1.0074 167.19 0.2793 6.8544 10.2633 - 5 1.0079 167.27 0.3146 6.8294 10.2188 - 6 1.0082 167.33 0.3500 6.8018 10.1721 - 7 1.0085 167.38 0.3854 6.7724 10.1241 - 8 1.0088 167.42 0.4208 6.7420 10.0753 - 9 1.0090 167.45 0.4562 6.7108 10.0258 - 10 1.0091 167.48 0.4916 6.6790 9.9759 - 11 1.0093 167.50 0.7344 6.4393 9.6159 - 12 1.0094 167.52 0.7698 6.4067 9.5655 - 13 1.0095 167.54 0.8052 6.3739 9.5148 - 14 1.0096 167.56 0.8405 6.3408 9.4640 - 15 1.0097 167.58 0.8759 6.3075 9.4130 - 16 1.0098 167.59 0.9113 6.2740 9.3618 - 17 1.0099 167.61 0.9467 6.2404 9.3106 - 18 1.0100 167.62 0.9820 6.2066 9.2592 - 19 1.0101 167.63 1.0174 6.1727 9.2077 - 20 1.0101 167.64 1.0528 6.1387 9.1562 - 21 1.0102 167.65 1.0882 6.1046 9.1045 - 22 1.0102 167.66 1.1235 6.0703 9.0527 - 23 1.0103 167.67 1.1589 6.0360 9.0008 - 24 1.0103 167.68 1.1943 6.0014 8.9488 - 25 1.0104 167.68 1.2297 5.9667 8.8967 - 26 1.0104 167.69 1.2651 5.9319 8.8444 - 27 1.0104 167.69 1.3005 5.8968 8.7920 - 28 1.0104 167.69 1.3359 5.8616 8.7393 - 29 1.0104 167.69 1.3713 5.8261 8.6864 - 30 1.0104 167.69 1.4067 5.7904 8.6333 + 1 1.0000 157.20 0.1741 5.7423 9.4056 + 2 1.0051 158.01 0.2095 5.8016 9.4317 + 3 1.0066 158.24 0.2448 5.7930 9.3979 + 4 1.0073 158.35 0.2802 5.7715 9.3528 + 5 1.0078 158.43 0.3156 5.7453 9.3037 + 6 1.0082 158.49 0.3510 5.7166 9.2524 + 7 1.0085 158.53 0.3864 5.6866 9.1999 + 8 1.0087 158.57 0.4218 5.6555 9.1466 + 9 1.0089 158.60 0.4572 5.6238 9.0927 + 10 1.0091 158.63 0.4926 5.5915 9.0383 + 11 1.0092 158.65 0.7259 5.3609 8.6637 + 12 1.0094 158.67 0.7613 5.3280 8.6089 + 13 1.0095 158.69 0.7966 5.2949 8.5539 + 14 1.0096 158.71 0.8320 5.2615 8.4986 + 15 1.0097 158.72 0.8674 5.2279 8.4432 + 16 1.0098 158.74 0.9027 5.1942 8.3877 + 17 1.0099 158.75 0.9381 5.1604 8.3321 + 18 1.0099 158.76 0.9735 5.1264 8.2763 + 19 1.0100 158.77 1.0089 5.0923 8.2205 + 20 1.0101 158.78 1.0443 5.0582 8.1646 + 21 1.0101 158.79 1.0796 5.0239 8.1085 + 22 1.0102 158.80 1.1150 4.9895 8.0524 + 23 1.0102 158.81 1.1504 4.9550 7.9962 + 24 1.0103 158.81 1.1858 4.9204 7.9398 + 25 1.0103 158.82 1.2212 4.8856 7.8833 + 26 1.0103 158.82 1.2566 4.8507 7.8267 + 27 1.0103 158.83 1.2920 4.8156 7.7699 + 28 1.0103 158.83 1.3274 4.7804 7.7129 + 29 1.0103 158.83 1.3628 4.7449 7.6558 + 30 1.0103 158.82 1.3982 4.7092 7.5984 ******************************************************************* @@ -171,36 +172,36 @@ Simulation Metadata YEAR ELECTRICITY HEAT RESERVOIR PERCENTAGE OF PROVIDED EXTRACTED HEAT CONTENT TOTAL HEAT MINED (GWh/year) (GWh/year) (10^15 J) (%) - 1 54.0 523.2 322.12 0.58 - 2 54.2 525.5 320.22 1.17 - 3 54.1 526.3 318.33 1.75 - 4 53.9 526.7 316.43 2.34 - 5 53.7 527.0 314.54 2.92 - 6 53.5 527.3 312.64 3.51 - 7 53.3 527.5 310.74 4.09 - 8 53.0 527.6 308.84 4.68 - 9 52.8 527.8 306.94 5.27 - 10 52.0 527.9 305.04 5.85 - 11 50.6 528.0 303.14 6.44 - 12 50.4 528.1 301.24 7.03 - 13 50.1 528.2 299.34 7.61 - 14 49.9 528.3 297.43 8.20 - 15 49.6 528.3 295.53 8.79 - 16 49.3 528.4 293.63 9.37 - 17 49.1 528.5 291.73 9.96 - 18 48.8 528.5 289.83 10.55 - 19 48.5 528.6 287.92 11.14 - 20 48.3 528.6 286.02 11.72 - 21 48.0 528.6 284.12 12.31 - 22 47.7 528.7 282.21 12.90 - 23 47.5 528.7 280.31 13.48 - 24 47.2 528.7 278.41 14.07 - 25 46.9 528.8 276.50 14.66 - 26 46.6 528.8 274.60 15.25 - 27 46.4 528.8 272.70 15.83 - 28 46.1 528.8 270.79 16.42 - 29 45.8 528.8 268.89 17.01 - 30 37.9 440.6 267.30 17.50 + 1 45.5 483.4 297.96 0.58 + 2 45.7 485.5 296.21 1.16 + 3 45.6 486.3 294.46 1.75 + 4 45.4 486.7 292.71 2.33 + 5 45.2 487.0 290.96 2.92 + 6 45.0 487.2 289.20 3.50 + 7 44.7 487.4 287.45 4.09 + 8 44.5 487.6 285.69 4.67 + 9 44.2 487.7 283.94 5.26 + 10 43.4 487.8 282.18 5.85 + 11 42.1 487.9 280.42 6.43 + 12 41.9 488.0 278.67 7.02 + 13 41.6 488.1 276.91 7.60 + 14 41.3 488.1 275.15 8.19 + 15 41.1 488.2 273.40 8.78 + 16 40.8 488.3 271.64 9.36 + 17 40.6 488.3 269.88 9.95 + 18 40.3 488.4 268.12 10.54 + 19 40.0 488.4 266.36 11.12 + 20 39.7 488.5 264.61 11.71 + 21 39.5 488.5 262.85 12.30 + 22 39.2 488.5 261.09 12.88 + 23 38.9 488.6 259.33 13.47 + 24 38.7 488.6 257.57 14.06 + 25 38.4 488.6 255.81 14.64 + 26 38.1 488.6 254.05 15.23 + 27 37.8 488.6 252.29 15.82 + 28 37.5 488.6 250.53 16.41 + 29 37.3 488.6 248.77 16.99 + 30 30.8 407.2 247.31 17.48 ******************************** @@ -210,33 +211,33 @@ Year Electricity | Heat | Since Price Ann. Rev. Cumm. Rev. | Price Ann. Rev. Cumm. Rev. | Price Ann. Rev. Cumm. Rev. | Price Ann. Rev. Cumm. Rev. | OPEX Net Rev. Net Cashflow Start (cents/kWh)(MUSD/yr) (MUSD) |(cents/kWh) (MUSD/yr) (MUSD) |(cents/kWh) (MUSD/yr) (MUSD) |(USD/tonne) (MUSD/yr) (MUSD) |(MUSD/yr) (MUSD/yr) (MUSD) ________________________________________________________________________________________________________________________________________________________________________________________ - 1 0.00 -59.59 0.00 | 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 -59.59 -59.59 - 2 9.00 3.20 4.86 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 3.20 -56.39 - 3 9.00 3.22 9.74 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 3.22 -53.17 - 4 9.00 3.21 14.61 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 3.21 -49.96 - 5 9.00 3.19 19.47 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 3.19 -46.77 - 6 9.00 3.17 24.30 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 3.17 -43.59 - 7 9.00 3.15 29.12 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 3.15 -40.44 - 8 10.20 3.77 34.55 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 3.77 -36.67 - 9 11.40 4.38 40.60 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 4.38 -32.28 - 10 12.60 4.99 47.25 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 4.99 -27.29 - 11 13.80 5.51 54.42 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.51 -21.79 - 12 15.00 5.93 62.01 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.93 -15.85 - 13 15.00 5.90 69.57 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.90 -9.96 - 14 15.00 5.86 77.09 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.86 -4.10 - 15 15.00 5.82 84.57 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.82 1.72 - 16 15.00 5.78 92.01 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.78 7.50 - 17 15.00 5.74 99.41 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.74 13.23 - 18 15.00 5.70 106.77 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.70 18.93 - 19 15.00 5.66 114.09 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.66 24.59 - 20 15.00 5.62 121.37 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.62 30.21 - 21 15.00 5.58 128.61 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.58 35.79 - 22 15.00 5.54 135.81 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.54 41.32 - 23 15.00 5.50 142.97 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.50 46.82 - 24 15.00 5.46 150.08 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.46 52.28 - 25 15.00 5.42 157.16 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.42 57.69 - 26 15.00 5.37 164.20 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.37 63.07 - 27 15.00 5.33 171.19 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.33 68.40 - 28 15.00 5.29 178.14 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.29 73.69 - 29 15.00 5.25 185.05 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.25 78.94 - 30 15.00 5.21 191.92 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.66 5.21 84.15 + 1 0.00 -48.48 0.00 | 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 -48.48 -48.48 + 2 9.00 2.67 4.10 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 2.67 -45.81 + 3 9.00 2.69 8.21 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 2.69 -43.12 + 4 9.00 2.68 12.32 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 2.68 -40.44 + 5 9.00 2.66 16.40 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 2.66 -37.78 + 6 9.00 2.64 20.47 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 2.64 -35.14 + 7 9.00 2.62 24.52 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 2.62 -32.52 + 8 10.20 3.13 29.08 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 3.13 -29.39 + 9 11.40 3.64 34.15 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 3.64 -25.75 + 10 12.60 4.14 39.72 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.14 -21.60 + 11 13.80 4.57 45.71 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.57 -17.03 + 12 15.00 4.89 52.03 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.89 -12.14 + 13 15.00 4.86 58.31 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.86 -7.28 + 14 15.00 4.82 64.55 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.82 -2.47 + 15 15.00 4.78 70.76 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.78 2.31 + 16 15.00 4.74 76.92 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.74 7.04 + 17 15.00 4.70 83.04 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.70 11.74 + 18 15.00 4.66 89.12 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.66 16.40 + 19 15.00 4.62 95.17 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.62 21.01 + 20 15.00 4.58 101.17 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.58 25.59 + 21 15.00 4.54 107.13 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.54 30.12 + 22 15.00 4.49 113.05 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.49 34.62 + 23 15.00 4.45 118.93 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.45 39.07 + 24 15.00 4.41 124.77 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.41 43.49 + 25 15.00 4.37 130.57 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.37 47.86 + 26 15.00 4.33 136.33 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.33 52.19 + 27 15.00 4.29 142.04 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.29 56.48 + 28 15.00 4.25 147.72 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.25 60.73 + 29 15.00 4.21 153.35 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.21 64.93 + 30 15.00 4.16 158.94 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 1.43 4.16 69.10 diff --git a/tests/examples/example_overpressure.txt b/tests/examples/example_overpressure.txt index aa8c9672..7700d386 100644 --- a/tests/examples/example_overpressure.txt +++ b/tests/examples/example_overpressure.txt @@ -15,10 +15,15 @@ Overpressure Depletion Rate, 10.0 Injection Reservoir Temperature, 101.1, degC Injection Reservoir Depth, 1001.1, meters Injection Reservoir Inflation Rate, 202.2, kPa/yr +Starting Electricity Sale Price,0.09 +Ending Electricity Sale Price,0.15 +Electricity Escalation Start Year,5 +Electricity Escalation Rate Per Year,0.012 + Reservoir Model,1, ---Multiple Fractures reservoir model Reservoir Depth,3, ---[km] Number of Segments,1, ---[-] -Gradient 1,50, ---[deg.C/km] +Gradient 1,47, ---[deg.C/km] Maximum Temperature,400, ---[deg.C] Number of Production Wells,2, ---[-] Number of Injection Wells,2, ---[-] @@ -57,11 +62,6 @@ Plant Lifetime,30, ---[years] Economic Model,1, ---[-] Fixed Charge Rate Model Fixed Charge Rate,.05, ---[-] between 0 and 1 Inflation Rate During Construction,0, ---[-] -Starting Electricity Sale Price,0.09 -Ending Electricity Sale Price,0.15 -Electricity Escalation Start Year,5 -Electricity Escalation Rate Per Year,0.012 - ***CAPITAL AND O&M COST PARAMETERS*** ************************************* diff --git a/tests/geophires_x_tests/test_cylindrical_reservoir.py b/tests/geophires_x_tests/test_cylindrical_reservoir.py index bbb21546..99bd8aca 100644 --- a/tests/geophires_x_tests/test_cylindrical_reservoir.py +++ b/tests/geophires_x_tests/test_cylindrical_reservoir.py @@ -119,7 +119,9 @@ def test_calculate_heat_capacity_water(self): reservoir.Calculate(model) expected_heat_capacity = heatcapacitywater( model.wellbores.Tinj.value * 0.5 + (reservoir.Trock.value * 0.9 + model.wellbores.Tinj.value * 0.1) * 0.5, - pressure=model.reserv.lithostatic_pressure(reservoir.rhorock.value, reservoir.Trock.value), + pressure=model.reserv.lithostatic_pressure( + reservoir.rhorock.value, reservoir.depth.quantity().to('m').magnitude + ), ) assert reservoir.cpwater.value == expected_heat_capacity @@ -130,7 +132,9 @@ def test_calculate_density_water(self): reservoir.Calculate(model) expected_density = density_water_kg_per_m3( model.wellbores.Tinj.value * 0.5 + (reservoir.Trock.value * 0.9 + model.wellbores.Tinj.value * 0.1) * 0.5, - pressure=reservoir.lithostatic_pressure(reservoir.rhorock.value, reservoir.Trock.value), + pressure=reservoir.lithostatic_pressure( + reservoir.rhorock.value, reservoir.depth.quantity().to('m').magnitude + ), ) assert expected_density == reservoir.rhowater.value