diff --git a/.gitignore b/.gitignore index a922924b..a33b932b 100644 --- a/.gitignore +++ b/.gitignore @@ -134,3 +134,16 @@ SAVE TABLE rcc3.out tough_debug_output/ + +# Temp +ai.md +regenerate-schemas.sh +requirements_2025-08-11.txt +src/geophires_x/CO2TAB +src/geophires_x/Doublet.dat +src/geophires_x/Doublet.out +src/geophires_x/FOFT_A3616.csv +src/geophires_x/FOFT_A36_2.csv +src/geophires_x/GOFT_A3616___021.csv +src/geophires_x/GOFT_A36_2___012.csv +src/geophires_x/INFILE diff --git a/src/geophires_x/.gitignore b/src/geophires_x/.gitignore index 97dad037..8e97af21 100644 --- a/src/geophires_x/.gitignore +++ b/src/geophires_x/.gitignore @@ -135,4 +135,20 @@ Examples\Test2.json /temperature.txt all_messages_conf.log +Doublet_default.dat + +FOFT_A3Q23.csv +FOFT_A3Q28.csv +GENER +GOFT_A3Q23___012.csv +GOFT_A3Q28___021.csv +INCON +MESH +MESHA +MESHB +OUTPUT_ELEME.csv +SAVE +TABLE +tough2output.out + !sam_economics/Generic_400_MWe/*.json diff --git a/src/geophires_x/MPFReservoir.py b/src/geophires_x/MPFReservoir.py index fcf2953d..71ba3502 100644 --- a/src/geophires_x/MPFReservoir.py +++ b/src/geophires_x/MPFReservoir.py @@ -26,11 +26,11 @@ def __init__(self, model: Model): Setting up includes giving it a name, a default value, The Unit Type (length, volume, temperature, etc) and Unit Name of that value, sets it as required (or not), sets allowable range, the error message if that range is exceeded, the ToolTip Text, and the name of teh class that created it. - This includes setting up temporary variables that will be available to all the class but noy read in by user, + This includes setting up temporary variables that will be available to all the class but not read in by user, or used for Output This also includes all Parameters that are calculated and then published using the Printouts function. If you choose to subclass this master class, you can do so before or after you create your own parameters. - If you do, you can also choose to call this method from you class, + If you do, you can also choose to call this method from your class, which will effectively add and set all these parameters to your class. :param model: The container class of the application, giving access to everything else, including the logger :type model: :class:`~geophires_x.Model.Model` diff --git a/src/geophires_x/Reservoir.py b/src/geophires_x/Reservoir.py index b152b862..c848f9f5 100644 --- a/src/geophires_x/Reservoir.py +++ b/src/geophires_x/Reservoir.py @@ -545,7 +545,7 @@ def read_parameters(self, model: Model) -> None: # This also deals with all the special cases that need to be taken care of # after a value has been read in and checked. # If you choose to subclass this master class, you can also choose to override this method (or not), - # and if you do, do it before or after you call you own version of this method. If you do, you can + # and if you do, do it before or after you call your own version of this method. If you do, you can # also choose to call this method from you class, which can effectively modify all these # superclass parameters in your class. @@ -729,7 +729,7 @@ def Calculate(self, model: Model) -> None: # temperatureindex = max(loc for loc, val in enumerate(self.depth.value > totaldepth) if val is True) self.Trock.value = intersecttemperature[temperatureindex] + self.gradient.value[temperatureindex] * \ - (self.depth.value - totaldepth[temperatureindex]) + (self.depth.value - totaldepth[temperatureindex]) # calculate average geothermal gradient if self.numseg.value == 1: diff --git a/src/geophires_x/SBTReservoir.py b/src/geophires_x/SBTReservoir.py index f0fc3d6d..0b121c63 100644 --- a/src/geophires_x/SBTReservoir.py +++ b/src/geophires_x/SBTReservoir.py @@ -388,7 +388,7 @@ def Calculate_Coaxial(self, model): # SBT v2 for co-axial heat exchanger with high-temperature capability # last update: January 14th, 2024 - # 1. Input + # 1. Inputs # Generally, the user should only make changes to this section fluid = 1 # Heat transfer fluid selection: 1 = H2O; 2 = CO2 diff --git a/src/geophires_x/SurfacePlantAGS.py b/src/geophires_x/SurfacePlantAGS.py index 036a0383..08d84d93 100644 --- a/src/geophires_x/SurfacePlantAGS.py +++ b/src/geophires_x/SurfacePlantAGS.py @@ -129,7 +129,7 @@ def __init__(self, model: Model): ErrMessage="assume default Dead-state pressure (1e5 Pa)" ) - # Input data for electricity generation with CO2 + # Inputs data for electricity generation with CO2 self.Turbine_isentropic_efficiency = self.ParameterDict[ self.Turbine_isentropic_efficiency.Name] = floatParameter( "Isentropic Efficiency for CO2 Turbine", diff --git a/src/geophires_x/SurfacePlantAbsorptionChiller.py b/src/geophires_x/SurfacePlantAbsorptionChiller.py index 671a3de5..92aacc04 100644 --- a/src/geophires_x/SurfacePlantAbsorptionChiller.py +++ b/src/geophires_x/SurfacePlantAbsorptionChiller.py @@ -36,7 +36,7 @@ def __init__(self, model: Model): self.MyClass = self.__class__.__name__ self.MyPath = __file__ - # Input parameters absorption chiller + # Inputs parameters absorption chiller self.absorption_chiller_cop = self.ParameterDict[self.absorption_chiller_cop.Name] = floatParameter( "Absorption Chiller COP", value=0.7, diff --git a/src/geophires_x/TOUGH2Reservoir.py b/src/geophires_x/TOUGH2Reservoir.py index 131f1e33..c1236654 100644 --- a/src/geophires_x/TOUGH2Reservoir.py +++ b/src/geophires_x/TOUGH2Reservoir.py @@ -1,5 +1,8 @@ import sys + import numpy as np +from geophires_x.Parameter import intParameter + from .Parameter import floatParameter, strParameter from .Units import * import geophires_x.Model as Model @@ -8,7 +11,7 @@ class TOUGH2Reservoir(Reservoir): """ - This class models the TOUGH2 Reservoir. + This class models the TOUGH2/TOUGH3 Reservoir. """ def __init__(self, model: Model): """ @@ -39,15 +42,99 @@ def __init__(self, model: Model): ) self.tough2modelfilename = self.ParameterDict[self.tough2modelfilename.Name] = strParameter( "TOUGH2 Model/File Name", - value='Doublet', + DefaultValue='Doublet', UnitType=Units.NONE, ErrMessage="assume default built-in TOUGH2 model (Doublet).", ToolTipText="File name of reservoir output in case reservoir model 5 is selected" ) + self.injection_cell = self.ParameterDict[self.injection_cell.Name] = strParameter( + "Vertical Injection Well Cell ID", + DefaultValue='A3Q23', + UnitType=Units.NONE, + ) + self.production_cell = self.ParameterDict[self.production_cell.Name] = strParameter( + "Vertical Production Well Cell ID", + DefaultValue='A3Q28', + UnitType=Units.NONE, + ) + + # Horizontal Wells + + self.numhinjcell = self.ParameterDict[self.numhinjcell.Name] = intParameter( + "Number of Horizontal Injection Well Cells", + DefaultValue=0, + AllowableRange=[0, 1, 2, 3, 4, 5], + UnitType=Units.NONE, + Required=True, + ErrMessage="Assume no horizontal injection well", + ToolTipText="Number of cells assumed as horizontal injection wells" + ) + self.hrz_inj1 = self.ParameterDict[self.hrz_inj1.Name] = strParameter( + "Horizontal Injection Well Cell ID 1", + DefaultValue='A3R23', + UnitType=Units.NONE, + ) + self.hrz_inj2 = self.ParameterDict[self.hrz_inj2.Name] = strParameter( + "Horizontal Injection Well Cell ID 2", + DefaultValue='A3S23', + UnitType=Units.NONE, + ) + self.hrz_inj3 = self.ParameterDict[self.hrz_inj3.Name] = strParameter( + "Horizontal Injection Well Cell ID 3", + DefaultValue='A3T23', + UnitType=Units.NONE, + ) + self.hrz_inj4 = self.ParameterDict[self.hrz_inj4.Name] = strParameter( + "Horizontal Injection Well Cell ID 4", + DefaultValue='A3U23', + UnitType=Units.NONE, + ) + self.hrz_inj5 = self.ParameterDict[self.hrz_inj5.Name] = strParameter( + "Horizontal Injection Well Cell ID 5", + DefaultValue='A3V23', + UnitType=Units.NONE, + ) + self.numhprodcell = self.ParameterDict[self.numhprodcell.Name] = intParameter( + "Number of Horizontal Production Well Cells", + DefaultValue=0, + AllowableRange=[0, 1, 2, 3, 4, 5], + UnitType=Units.NONE, + Required=True, + ErrMessage="Assume no horizontal production well", + ToolTipText="Number of cells assumed as horizontal production wells" + ) + self.hrz_prod1 = self.ParameterDict[self.hrz_prod1.Name] = strParameter( + "Horizontal Production Well Cell ID 1", + DefaultValue='A3R28', + UnitType=Units.NONE, + ) + self.hrz_prod2 = self.ParameterDict[self.hrz_prod2.Name] = strParameter( + "Horizontal Production Well Cell ID 2", + DefaultValue='A3S28', + UnitType=Units.NONE, + ) + self.hrz_prod3 = self.ParameterDict[self.hrz_prod3.Name] = strParameter( + "Horizontal Production Well Cell ID 3", + DefaultValue='A3T28', + UnitType=Units.NONE, + ) + self.hrz_prod4 = self.ParameterDict[self.hrz_prod4.Name] = strParameter( + "Horizontal Production Well Cell ID 4", + DefaultValue='A3U28', + UnitType=Units.NONE, + ) + self.hrz_prod5 = self.ParameterDict[self.hrz_prod5.Name] = strParameter( + "Horizontal Production Well Cell ID 5", + DefaultValue='A3V28', + UnitType=Units.NONE, + ) + + # End of Horizontal Wells + self.resthickness = self.ParameterDict[self.resthickness.Name] = floatParameter( "Reservoir Thickness", value=250.0, - Min=10, + Min=5, Max=10000, UnitType=Units.LENGTH, PreferredUnits=LengthUnit.METERS, @@ -108,7 +195,7 @@ def read_parameters(self, model:Model) -> None: ParameterReadIn = model.InputParameters[key] # handle special cases if ParameterToModify.Name == "TOUGH2 Model/File Name": - if self.tough2modelfilename.value == 'Doublet': + if self.tough2modelfilename.value.startswith('Doublet'): self.usebuiltintough2model = True else: self.usebuiltintough2model = False @@ -128,16 +215,34 @@ def Calculate(self, model:Model): model.logger.info("Init " + str(__class__) + ": " + sys._getframe().f_code.co_name) super().Calculate(model) # run calculate for the parent. - # GEOPHIRES assumes TOUGH2 executable and input file are in same directory as GEOPHIRESv3.py - # create tough2 input file + # GEOPHIRES assumes TOUGH2 executable and input file are in same directory as GEOPHIRESv3.py, + # however the path can be specified inside GEOPHIRES input file + # Create TOUGH2/TOUGH3 input file path_to_exe = str(self.tough2_executable_path.value) + injection_cell_id = str(self.injection_cell.value) + production_cell_id = str(self.production_cell.value) + + """ Horizontal Well Cell IDs""" + num_injwellcells = int(self.numhinjcell.value) + num_prodwellcells = int(self.numhprodcell.value) + hrz_inj_id1 = str(self.hrz_inj1.value) + hrz_inj_id2 = str(self.hrz_inj2.value) + hrz_inj_id3 = str(self.hrz_inj3.value) + hrz_inj_id4 = str(self.hrz_inj4.value) + hrz_inj_id5 = str(self.hrz_inj5.value) + hrz_prod_id1 = str(self.hrz_prod1.value) + hrz_prod_id2 = str(self.hrz_prod2.value) + hrz_prod_id3 = str(self.hrz_prod3.value) + hrz_prod_id4 = str(self.hrz_prod4.value) + hrz_prod_id5 = str(self.hrz_prod5.value) + if not os.path.exists(os.path.join(os.getcwd(), path_to_exe)): model.logger.critical('TOUGH2 executable file does not exist in current working directory. \ GEOPHIRES will abort simulation.') print('TOUGH2 executable file does not exist in current working directory. \ GEOPHIRES will abort simulation.') sys.exit() - if model.reserv.tough2modelfilename.value == 'Doublet': + if model.reserv.tough2modelfilename.value != 'Doublet': infile = str('Doublet.dat') outfile = str('Doublet.out') initialtemp = model.reserv.Trock.value @@ -149,26 +254,44 @@ def Calculate(self, model:Model): reservoirthickness = model.reserv.resthickness.value reservoirwidth = model.reserv.reswidth.value wellseperation = model.wellbores.wellsep.value - DeltaXgrid = wellseperation/15 - DeltaYgrid = reservoirwidth/11 + DeltaXgrid = 10000/50 + DeltaYgrid = reservoirwidth/50 DeltaZgrid = reservoirthickness/5 - flowrate = model.wellbores.prodwellflowrate.value + + flowrate_inj = model.wellbores.prodwellflowrate.value / (num_injwellcells + 1) + flowrate_prod = model.wellbores.prodwellflowrate.value / (num_prodwellcells + 1) + print('Reservoir parameters passed to TOUGH from Reservoir.py \n') + print("Initial Temperature = ", initialtemp) + print("Rock Density = ", rockdensity) + print("Roch Heat Capacity = ", rockheatcap) + print("Rock Permeability = ", rockperm) + print("Rock Porosity = ", rockpor) + print("Rock Thermal Conductivity = ", rockthermalcond) + print("Reservoir Thickness = ", reservoirthickness) + print("Reservoir Width = ", reservoirwidth) + print("Well Separation = ", wellseperation) + print("Grid X = ", DeltaXgrid) + print("Grid Y = ", DeltaYgrid) + print("Grid Z = ", DeltaZgrid) + print("") # convert injection temperature to injection enthalpy arraytinj = np.array([1.8, 11.4, 23.4, 35.4, 47.4, 59.4, 71.3, 83.3, 95.2, 107.1, 118.9]) arrayhinj = np.array([1.0E4, 5.0E4, 1.0E5, 1.5E5, 2.0E5, 2.5E5, 3.0E5, 3.5E5, 4.0E5, 4.5E5, 5.0E5]) injenthalpy = np.interp(model.wellbores.Tinj.value,arraytinj,arrayhinj) + # write doublet input file f = open(infile,'w', encoding='UTF-8') f.write('Doublet\n') f.write('MESHMAKER1----*----2----*----3----*----4----*----5----*----6----*----7----*----8\n') f.write('XYZ\n') - f.write(' 0.\n') - f.write('NX 17 %9.3f\n' % DeltaXgrid) - f.write('NY 11 %9.3f\n' % DeltaYgrid) + f.write(' 0.\n') + f.write('NX 50 %9.3f\n' % DeltaXgrid) + f.write('NY 50 %9.3f\n' % DeltaYgrid) f.write('NZ 5 %9.3f\n' % DeltaZgrid) f.write('\n') f.write('\n') + f.write('\n') f.write('ROCKS----1----*----2----*----3----*----4----*----5----*----6----*----7----*----8\n') f.write('POMED 3%10.1f %9.4f %9.2E %9.2E %9.2E %9.4f %9.2f \n' % (rockdensity, rockpor, rockperm, rockperm, rockperm, rockthermalcond, rockheatcap)) f.write(' 0.0 0.0 2.0 0.0 0.0\n') @@ -189,53 +312,177 @@ def Calculate(self, model:Model): f.write('\n') f.write('\n') f.write('GENER----1----*----2----*----3----*----4----*----5----*----6----*----7----*----8\n') - f.write('A36 2 012 0 COM1 %9.3f %9.1f \n' % (flowrate, injenthalpy)) - f.write('A3616 021 0 MASS %9.3f \n' % (-flowrate)) + f.write('%s 012 1 COM1 %9.3f %9.1f \n' % (injection_cell_id, flowrate_inj, injenthalpy)) + f.write('%s 012 1 COM1 %9.3f %9.1f \n' % (hrz_inj_id1, flowrate_inj, injenthalpy)) + f.write('%s 012 1 COM1 %9.3f %9.1f \n' % (hrz_inj_id2, flowrate_inj, injenthalpy)) + f.write('%s 012 1 COM1 %9.3f %9.1f \n' % (hrz_inj_id3, flowrate_inj, injenthalpy)) + f.write('%s 012 1 COM1 %9.3f %9.1f \n' % (hrz_inj_id4, flowrate_inj, injenthalpy)) + f.write('%s 012 1 COM1 %9.3f %9.1f \n' % (hrz_inj_id5, flowrate_inj, injenthalpy)) + f.write('%s 021 1 MASS %9.3f \n' % (production_cell_id, -flowrate_prod)) + f.write('%s 021 1 MASS %9.3f \n' % (hrz_prod_id1, -flowrate_prod)) + f.write('%s 021 1 MASS %9.3f \n' % (hrz_prod_id2, -flowrate_prod)) + f.write('%s 021 1 MASS %9.3f \n' % (hrz_prod_id3, -flowrate_prod)) + f.write('%s 021 1 MASS %9.3f \n' % (hrz_prod_id4, -flowrate_prod)) + f.write('%s 021 1 MASS %9.3f \n' % (hrz_prod_id5, -flowrate_prod)) f.write('\n') f.write('INCON----1----*----2----*----3----*----4----*----5----*----6----*----7----*----8\n') f.write('\n') f.write('FOFT ----1----*----2----*----3----*----4----*----5----*----6----*----7----*----8\n') - f.write('A36 2\n') - f.write('A3616\n') + f.write(f'{injection_cell_id} \n') + f.write(f'{hrz_inj_id1} \n') + f.write(f'{hrz_inj_id2} \n') + f.write(f'{hrz_inj_id3} \n') + f.write(f'{hrz_inj_id4} \n') + f.write(f'{hrz_inj_id5} \n') + f.write(f'{production_cell_id} \n') + f.write(f'{hrz_prod_id1} \n') + f.write(f'{hrz_prod_id2} \n') + f.write(f'{hrz_prod_id3} \n') + f.write(f'{hrz_prod_id4} \n') + f.write(f'{hrz_prod_id5} \n') f.write('\n') f.write('GOFT ----1----*----2----*----3----*----4----*----5----*----6----*----7----*----8\n') - f.write('A36 2 012\n') - f.write('A3616 021\n') + f.write(f'{injection_cell_id} 012\n') + f.write(f'{hrz_inj_id1} 012\n') + f.write(f'{hrz_inj_id2} 012\n') + f.write(f'{hrz_inj_id3} 012\n') + f.write(f'{hrz_inj_id4} 012\n') + f.write(f'{hrz_inj_id5} 012\n') + f.write(f'{production_cell_id} 021\n') + f.write(f'{hrz_prod_id1} 021\n') + f.write(f'{hrz_prod_id2} 021\n') + f.write(f'{hrz_prod_id3} 021\n') + f.write(f'{hrz_prod_id4} 021\n') + f.write(f'{hrz_prod_id5} 021\n') f.write('\n') +# f.write('TIMES----1----*----2----*----3----*----4----*----5----*----6----*----7----*----8\n') +# f.write('10 \n') +# f.write('2.0000E+013.6000E+038.6400E+042.6784E+061.5898E+073.1536E+071.5768E+083.1536E+08\n') +# f.write('6.3072E+089.4610E+08\n') +# f.write('\n') f.write('ENDCY\n') f.close() print("GEOPHIRES will run TOUGH2 simulation with built-in Doublet model ...") else: infile = model.reserv.tough2modelfilename.value - outfile = str('tough2output.out') + outfile = str('Doublet.out') print("GEOPHIRES will run TOUGH2 simulation with user-provided input file = "+model.reserv.tough2modelfilename.value+" ...") # run TOUGH2 executable try: - os.system('%s < %s > %s' % (path_to_exe, infile, outfile)) - except: + os.system('%s %s %s' % (path_to_exe, infile, outfile)) + except Exception as e: print("Error: GEOPHIRES could not run TOUGH2 and will abort simulation.") - sys.exit() + raise RuntimeError(f'Error: GEOPHIRES could not run TOUGH2 and will abort simulation: {e!s}') from e # read output temperature and pressure try: - fname = 'FOFT' + fname = f'FOFT_{production_cell_id}.csv' with open(fname, encoding='UTF-8') as f: + first_line = f.readline() content = f.readlines() NumerOfResults = len(content) SimTimes = np.zeros(NumerOfResults) ProdPressure = np.zeros(NumerOfResults) ProdTemperature = np.zeros(NumerOfResults) + for i in range(0,NumerOfResults): - SimTimes[i] = float(content[i].split(',')[1].strip('\n')) - ProdPressure[i] = float(content[i].split(',')[8].strip('\n')) - ProdTemperature[i] = float(content[i].split(',')[9].strip('\n')) + """ + #changed: 1>0' + 8>1' + 9>2' + TODO - Audit index parameterization + """ + SimTimes[i] = float(content[i].split(',')[0].strip('\n')) # Simulation time + ProdPressure[i] = float(content[i].split(',')[1].strip('\n')) # Production well pressure + ProdTemperature[i] = float(content[i].split(',')[2].strip('\n')) # Production well temperature model.reserv.Tresoutput.value = np.interp(model.reserv.timevector.value*365*24*3600,SimTimes,ProdTemperature) - except: - print("Error: GEOPHIRES could not import production temperature and pressure from TOUGH2 output file (" + - infile + ") and will abort simulation.") + + # Read FOFT and GOFT files to calculate Productivity Index (PI) and Injectivity Index (II) + import pandas as pd + + df_0v = pd.read_csv(f'FOFT_{production_cell_id}.csv') + df_fv = pd.read_csv(f'FOFT_{production_cell_id}.csv') + # dfG_0 = pd.read_csv(f'GOFT_{production_cell_id}___021.csv') + df_01 = pd.read_csv(f'FOFT_{hrz_prod_id1}.csv') + df_f1 = pd.read_csv(f'FOFT_{hrz_prod_id1}.csv') + df_02 = pd.read_csv(f'FOFT_{hrz_prod_id2}.csv') + df_f2 = pd.read_csv(f'FOFT_{hrz_prod_id2}.csv') + df_03 = pd.read_csv(f'FOFT_{hrz_prod_id3}.csv') + df_f3 = pd.read_csv(f'FOFT_{hrz_prod_id3}.csv') + df_04 = pd.read_csv(f'FOFT_{hrz_prod_id4}.csv') + df_f4 = pd.read_csv(f'FOFT_{hrz_prod_id4}.csv') + df_05 = pd.read_csv(f'FOFT_{hrz_prod_id5}.csv') + df_f5 = pd.read_csv(f'FOFT_{hrz_prod_id5}.csv') + + ef_0v = pd.read_csv(f'FOFT_{injection_cell_id.replace(" ", "_")}.csv') + ef_fv = pd.read_csv(f'FOFT_{injection_cell_id.replace(" ", "_")}.csv') + # efG_0 = pd.read_csv(f'GOFT_{injection_cell_id.replace(" ", "_")}___012.csv') + ef_01 = pd.read_csv(f'FOFT_{hrz_inj_id1.replace(" ", "_")}.csv') + ef_f1 = pd.read_csv(f'FOFT_{hrz_inj_id1.replace(" ", "_")}.csv') + ef_02 = pd.read_csv(f'FOFT_{hrz_inj_id2.replace(" ", "_")}.csv') + ef_f2 = pd.read_csv(f'FOFT_{hrz_inj_id2.replace(" ", "_")}.csv') + ef_03 = pd.read_csv(f'FOFT_{hrz_inj_id3.replace(" ", "_")}.csv') + ef_f3 = pd.read_csv(f'FOFT_{hrz_inj_id3.replace(" ", "_")}.csv') + ef_04 = pd.read_csv(f'FOFT_{hrz_inj_id4.replace(" ", "_")}.csv') + ef_f4 = pd.read_csv(f'FOFT_{hrz_inj_id4.replace(" ", "_")}.csv') + ef_05 = pd.read_csv(f'FOFT_{hrz_inj_id5.replace(" ", "_")}.csv') + ef_f5 = pd.read_csv(f'FOFT_{hrz_inj_id5.replace(" ", "_")}.csv') + + P0v_production_well = df_0v[' PRES'].iloc[0] + Pfv_production_well = df_fv[' PRES'].iloc[-1] + P0v_injection_well = ef_0v[' PRES'].iloc[0] + Pfv_injection_well = ef_fv[' PRES'].iloc[-1] + P01_production_well = df_01[' PRES'].iloc[0] + Pf1_production_well = df_f1[' PRES'].iloc[-1] + P01_injection_well = ef_01[' PRES'].iloc[0] + Pf1_injection_well = ef_f1[' PRES'].iloc[-1] + P02_production_well = df_02[' PRES'].iloc[0] + Pf2_production_well = df_f2[' PRES'].iloc[-1] + P02_injection_well = ef_02[' PRES'].iloc[0] + Pf2_injection_well = ef_f2[' PRES'].iloc[-1] + P03_production_well = df_03[' PRES'].iloc[0] + Pf3_production_well = df_f3[' PRES'].iloc[-1] + P03_injection_well = ef_03[' PRES'].iloc[0] + Pf3_injection_well = ef_f3[' PRES'].iloc[-1] + P04_production_well = df_04[' PRES'].iloc[0] + Pf4_production_well = df_f4[' PRES'].iloc[-1] + P04_injection_well = ef_04[' PRES'].iloc[0] + Pf4_injection_well = ef_f4[' PRES'].iloc[-1] + P05_production_well = df_05[' PRES'].iloc[0] + Pf5_production_well = df_f5[' PRES'].iloc[-1] + P05_injection_well = ef_05[' PRES'].iloc[0] + Pf5_injection_well = ef_f5[' PRES'].iloc[-1] + + fr0_production_well = -model.wellbores.prodwellflowrate.value + fr0_injection_well = model.wellbores.prodwellflowrate.value + + tough3_PI = fr0_production_well / (((Pfv_production_well - P0v_production_well) + + (Pf1_production_well - P01_production_well) + + (Pf2_production_well - P02_production_well) + + (Pf3_production_well - P03_production_well) + + (Pf4_production_well - P04_production_well) + + (Pf5_production_well - P05_production_well)) / 100000) + tough3_II = fr0_injection_well / (((Pfv_injection_well - P0v_injection_well) + + (Pf1_injection_well - P01_injection_well) + + (Pf2_injection_well - P02_injection_well) + + (Pf3_injection_well - P03_injection_well) + + (Pf4_injection_well - P04_injection_well) + + (Pf5_injection_well - P05_injection_well)) / 100000) + + print("TOUGH PI = ", tough3_PI) + print("TOUGH II = ", tough3_II) + model.wellbores.PI.value = tough3_PI + model.wellbores.II.value = tough3_II + + except Exception as e: + raise RuntimeError(f'Error: GEOPHIRES could not import production temperature and pressure from TOUGH2' + f' output file ({infile}) and will abort simulation.') from e model.logger.info("Complete " + str(__class__) + ": " + sys._getframe().f_code.co_name) + + + diff --git a/src/geophires_x/WellBores.py b/src/geophires_x/WellBores.py index 9e6e9249..7e616c9d 100644 --- a/src/geophires_x/WellBores.py +++ b/src/geophires_x/WellBores.py @@ -432,11 +432,12 @@ def InjPressureDropsAndPumpingPowerUsingImpedenceModel(f1: float, vinj: float, r return newDPOverall, PumpingPower, DPInjWell + def get_hydrostatic_pressure_kPa( Trock_degC: float, Tsurf_degC: float, depth_m: float, - gradient_C_per_km: float, + gradient_C_per_m: float, # converted from C/km to C/m lithostatic_pressure: PlainQuantity) -> float: """ Correlation cited as being from Xie, Bloomfield, and Shook in @@ -446,7 +447,7 @@ def get_hydrostatic_pressure_kPa( CT = 9E-4 / (30.796 * Trock_degC ** (-0.552)) return 0 + 1. / CP * (math.exp( density_water_kg_per_m3(Tsurf_degC, pressure=lithostatic_pressure) * 9.81 * CP / 1000 * ( - depth_m - CT / 2 * gradient_C_per_km * depth_m ** 2)) - 1) + depth_m - CT / 2 * gradient_C_per_m * depth_m ** 2)) - 1) def ProdPressureDropAndPumpingPowerUsingIndexes( @@ -1039,7 +1040,7 @@ def __init__(self, model: Model): ToolTipText='; '.join([f'{it.int_value}: {it.value}' for it in WorkingFluid]) ) - # Input data for subsurface condition + # Inputs data for subsurface condition self.Nonvertical_length = self.ParameterDict[self.Nonvertical_length.Name] = floatParameter( "Nonvertical Length per Multilateral Section", DefaultValue=1000.0, diff --git a/src/geophires_x_schema_generator/geophires-request.json b/src/geophires_x_schema_generator/geophires-request.json index 0788b878..b1202087 100644 --- a/src/geophires_x_schema_generator/geophires-request.json +++ b/src/geophires_x_schema_generator/geophires-request.json @@ -21,6 +21,8 @@ "Cylindrical Reservoir Output Depth", "Cylindrical Reservoir Length", "Drilled length", + "Number of Horizontal Injection Well Cells", + "Number of Horizontal Production Well Cells", "Number of Production Wells", "Number of Injection Wells", "Production Well Diameter", @@ -606,7 +608,133 @@ "type": "string", "units": null, "category": "Reservoir", - "default": null, + "default": "Doublet", + "minimum": null, + "maximum": null + }, + "Vertical Injection Well Cell ID": { + "description": "", + "type": "string", + "units": null, + "category": "Reservoir", + "default": "A3Q23", + "minimum": null, + "maximum": null + }, + "Vertical Production Well Cell ID": { + "description": "", + "type": "string", + "units": null, + "category": "Reservoir", + "default": "A3Q28", + "minimum": null, + "maximum": null + }, + "Number of Horizontal Injection Well Cells": { + "description": "Number of cells assumed as horizontal injection wells", + "type": "integer", + "units": null, + "category": "Reservoir", + "default": 0, + "minimum": 0, + "maximum": 5 + }, + "Horizontal Injection Well Cell ID 1": { + "description": "", + "type": "string", + "units": null, + "category": "Reservoir", + "default": "A3R23", + "minimum": null, + "maximum": null + }, + "Horizontal Injection Well Cell ID 2": { + "description": "", + "type": "string", + "units": null, + "category": "Reservoir", + "default": "A3S23", + "minimum": null, + "maximum": null + }, + "Horizontal Injection Well Cell ID 3": { + "description": "", + "type": "string", + "units": null, + "category": "Reservoir", + "default": "A3T23", + "minimum": null, + "maximum": null + }, + "Horizontal Injection Well Cell ID 4": { + "description": "", + "type": "string", + "units": null, + "category": "Reservoir", + "default": "A3U23", + "minimum": null, + "maximum": null + }, + "Horizontal Injection Well Cell ID 5": { + "description": "", + "type": "string", + "units": null, + "category": "Reservoir", + "default": "A3V23", + "minimum": null, + "maximum": null + }, + "Number of Horizontal Production Well Cells": { + "description": "Number of cells assumed as horizontal production wells", + "type": "integer", + "units": null, + "category": "Reservoir", + "default": 0, + "minimum": 0, + "maximum": 5 + }, + "Horizontal Production Well Cell ID 1": { + "description": "", + "type": "string", + "units": null, + "category": "Reservoir", + "default": "A3R28", + "minimum": null, + "maximum": null + }, + "Horizontal Production Well Cell ID 2": { + "description": "", + "type": "string", + "units": null, + "category": "Reservoir", + "default": "A3S28", + "minimum": null, + "maximum": null + }, + "Horizontal Production Well Cell ID 3": { + "description": "", + "type": "string", + "units": null, + "category": "Reservoir", + "default": "A3T28", + "minimum": null, + "maximum": null + }, + "Horizontal Production Well Cell ID 4": { + "description": "", + "type": "string", + "units": null, + "category": "Reservoir", + "default": "A3U28", + "minimum": null, + "maximum": null + }, + "Horizontal Production Well Cell ID 5": { + "description": "", + "type": "string", + "units": null, + "category": "Reservoir", + "default": "A3V28", "minimum": null, "maximum": null }, @@ -616,7 +744,7 @@ "units": "meter", "category": "Reservoir", "default": 0.0, - "minimum": 10, + "minimum": 5, "maximum": 10000 }, "Reservoir Width": { diff --git a/tests/examples/example_overpressure.txt b/tests/examples/example_overpressure.txt index 56914025..dc4119ef 100644 --- a/tests/examples/example_overpressure.txt +++ b/tests/examples/example_overpressure.txt @@ -15,7 +15,10 @@ Electricity Escalation Rate Per Year,0.012 Reservoir Model,1, ---Multiple Fractures reservoir model Reservoir Depth,3, ---[km] Number of Segments,1, ---[-] + +# FIXME: Overpressure implementation does correctly convert gradients - see https://github.com/softwareengineerprogrammer/GEOPHIRES/pull/111/files#r2492064756 Gradient 1,47, ---[deg.C/km] + Maximum Temperature,400, ---[deg.C] Number of Production Wells,2, ---[-] Number of Injection Wells,2, ---[-] diff --git a/tests/examples/example_overpressure2.txt b/tests/examples/example_overpressure2.txt index 6f97da40..c65af80b 100644 --- a/tests/examples/example_overpressure2.txt +++ b/tests/examples/example_overpressure2.txt @@ -15,7 +15,10 @@ Electricity Escalation Rate Per Year,0.012 Reservoir Model,1, ---Multiple Fractures reservoir model Reservoir Depth,3, ---[km] Number of Segments,1, ---[-] + +# FIXME: Overpressure implementation does correctly convert gradients - see https://github.com/softwareengineerprogrammer/GEOPHIRES/pull/111/files#r2492064756 Gradient 1,47, ---[deg.C/km] + Maximum Temperature,400, ---[deg.C] Number of Production Wells,2, ---[-] Number of Injection Wells,2, ---[-]