Skip to content

Commit 8419423

Browse files
cleaned up model instatiation so the correct version of class gets instatiated; moved total_drilling_length witin wellbores
1 parent 73f9224 commit 8419423

File tree

3 files changed

+146
-90
lines changed

3 files changed

+146
-90
lines changed

src/geophires_x/Economics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2186,7 +2186,7 @@ def Calculate(self, model: Model) -> None:
21862186
output_vert_depth_km = model.reserv.OutputDepth.quantity().to('km').magnitude
21872187
model.wellbores.injection_reservoir_depth.value = input_vert_depth_km
21882188

2189-
tot_m, tot_vert_m, tot_horiz_m = calculate_total_drilling_lengths_m(model.wellbores.Configuration.value,
2189+
tot_m, tot_vert_m, tot_horiz_m, _ = calculate_total_drilling_lengths_m(model.wellbores.Configuration.value,
21902190
model.wellbores.numnonverticalsections.value,
21912191
model.wellbores.Nonvertical_length.value / 1000.0,
21922192
input_vert_depth_km,

src/geophires_x/Model.py

Lines changed: 80 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -75,74 +75,107 @@ def __init__(self, enable_geophires_logging_config=True, input_file=None):
7575
if input_file is None and len(sys.argv) > 1:
7676
input_file = sys.argv[1]
7777

78+
# Key step - read the entire provided input file
7879
read_input_file(self.InputParameters, logger=self.logger, input_file_name=input_file)
7980

81+
# initiate the outputs object
82+
output_file = 'HDR.out'
83+
if len(sys.argv) > 2:
84+
output_file = sys.argv[2]
85+
self.outputs = Outputs(self, output_file=output_file)
86+
87+
# Initiate the elements of the Model object
88+
# this is where you can change what class get initiated - the superclass, or one of the subclasses
89+
self.logger.info("Initiate the elements of the Model")
90+
91+
# Assume that SDAC and add-ons are not used
8092
self.sdacgtoutputs = None
8193
self.sdacgteconomics = None
8294
self.addoutputs = None
8395
self.addeconomics = None
8496

85-
# Initiate the elements of the Model
86-
# this is where you can change what class get initiated - the superclass, or one of the subclasses
87-
self.logger.info("Initiate the elements of the Model")
88-
# we need to decide which reservoir to instantiate based on the user input (InputParameters),
89-
# which we just read above for the first time
90-
# Default is Thermal drawdown percentage model (GETEM)
91-
self.reserv: TDPReservoir = TDPReservoir(self)
92-
if 'Reservoir Model' in self.InputParameters:
93-
if self.InputParameters['Reservoir Model'].sValue == '0':
94-
self.reserv: CylindricalReservoir = CylindricalReservoir(self) # Simple Cylindrical Reservoir
95-
elif self.InputParameters['Reservoir Model'].sValue == '1':
96-
self.reserv: MPFReservoir = MPFReservoir(self) # Multiple parallel fractures model (LANL)
97-
elif self.InputParameters['Reservoir Model'].sValue == '2':
98-
self.reserv: LHSReservoir = LHSReservoir(self) # Multiple parallel fractures model (LANL)
99-
elif self.InputParameters['Reservoir Model'].sValue == '3':
100-
self.reserv: SFReservoir = SFReservoir(self) # Drawdown parameter model (Tester)
101-
elif self.InputParameters['Reservoir Model'].sValue == '5':
102-
self.reserv: UPPReservoir = UPPReservoir(self) # Generic user-provided temperature profile
103-
elif self.InputParameters['Reservoir Model'].sValue == '6':
104-
self.reserv: TOUGH2Reservoir = TOUGH2Reservoir(self) # Tough2 is called
105-
elif self.InputParameters['Reservoir Model'].sValue == '7':
106-
self.reserv: SUTRAReservoir = SUTRAReservoir(self) # SUTRA output is created
107-
elif self.InputParameters['Reservoir Model'].sValue == '8':
108-
self.logger.info('Setup the SBT elements of the Model and instantiate new attributes as needed')
109-
self.reserv: SBTReservoir = SBTReservoir(self)
110-
11197
# initialize the default objects
98+
self.reserv: TDPReservoir = TDPReservoir(self)
11299
self.wellbores: WellBores = WellBores(self)
113-
self.surfaceplant: SurfacePlant = SurfacePlant(self)
100+
self.surfaceplant = SurfacePlantIndustrialHeat(self) # default is Industrial Heat
114101
self.economics: Economics = Economics(self)
115102

116-
output_file = 'HDR.out'
117-
if len(sys.argv) > 2:
118-
output_file = sys.argv[2]
119-
120-
self.outputs = Outputs(self, output_file=output_file)
103+
# Now we need to handle the creation of all the special case objects based on the user settings
104+
# We have to access the user setting from the overall master table because the read_parameters functions
105+
# have not been called on the specific objects yet, so the instantiated objects only contain default values
106+
# not user set values. The user set value can only come from the master dictionary "InputParameters"
107+
# based on the user input, which we just read above for the first time
121108

109+
# First, we need to decide which reservoir to instantiate
110+
# For reservoirs, the default is Thermal drawdown percentage model (GETEM); see above where it is initialized.
111+
# The user can change this in the input file, so check the values and initiate the appropriate reservoir object
122112
if 'Reservoir Model' in self.InputParameters:
123-
if self.InputParameters['Reservoir Model'].sValue == '7':
124-
# if we use SUTRA output for simulating reservoir thermal energy storage, we use a special wellbore object that can handle SUTRA data
113+
if self.InputParameters['Reservoir Model'].sValue in ['0', 'Simple cylindrical']:
114+
self.reserv: CylindricalReservoir = CylindricalReservoir(self)
115+
elif self.InputParameters['Reservoir Model'].sValue in ['1', 'Multiple Parallel Fractures']:
116+
self.reserv: MPFReservoir = MPFReservoir(self)
117+
elif self.InputParameters['Reservoir Model'].sValue in ['2', '1-D Linear Heat Sweep']:
118+
self.reserv: LHSReservoir = LHSReservoir(self)
119+
elif self.InputParameters['Reservoir Model'].sValue in ['3', 'Single Fracture m/A Thermal Drawdown']:
120+
self.reserv: SFReservoir = SFReservoir(self)
121+
elif self.InputParameters['Reservoir Model'].sValue in ['5', 'User-Provided Temperature Profile']:
122+
self.reserv: UPPReservoir = UPPReservoir(self)
123+
elif self.InputParameters['Reservoir Model'].sValue in ['6', 'TOUGH2 Simulator']:
124+
self.reserv: TOUGH2Reservoir = TOUGH2Reservoir(self)
125+
elif self.InputParameters['Reservoir Model'].sValue in ['7', 'SUTRA']:
126+
# if we use SUTRA output for simulating reservoir thermal energy storage,
127+
# we use a special wellbore object that handles SUTRA data, and special Economics and Outputs objects
128+
self.logger.info('Setup the SUTRA elements of the Model and instantiate new attributes as needed')
129+
self.reserv: SUTRAReservoir = SUTRAReservoir(self)
125130
self.wellbores: WellBores = SUTRAWellBores(self)
126131
self.surfaceplant: SurfacePlantSUTRA = SurfacePlantSUTRA(self)
127132
self.economics: SUTRAEconomics = SUTRAEconomics(self)
128133
self.outputs: SUTRAOutputs = SUTRAOutputs(self, output_file=output_file)
129-
if self.InputParameters['Reservoir Model'].sValue == '8':
130-
self.wellbores: SBTWellbores = SBTWellbores(self)
134+
elif self.InputParameters['Reservoir Model'].sValue in ['8', 'SBT']:
135+
self.logger.info('Setup the SBT elements of the Model and instantiate new attributes as needed')
136+
self.reserv: SBTReservoir = SBTReservoir(self)
137+
self.wellbores: SBTWellBores = SBTWellbores(self)
131138
self.economics: SBTEconomics = SBTEconomics(self)
132139

140+
# Now handle the special cases for all AGS cases (CLGS, SBT, or CLGS)
133141
if 'Is AGS' in self.InputParameters:
134142
if self.InputParameters['Is AGS'].sValue in ['True', 'true', 'TRUE', 'T', '1']:
135-
self.logger.info("Setup the AGS elements of the Model and instantiate new attributes as needed")
136-
# If we are doing AGS, we need to replace the various objects we with versions of the objects
137-
# that have AGS functionality.
138-
# that means importing them, initializing them, then reading their parameters
139-
# use the simple cylindrical reservoir for all AGS systems.
140-
self.reserv: CylindricalReservoir = CylindricalReservoir(self)
141-
self.wellbores: WellBores = AGSWellBores(self)
142-
self.surfaceplant: SurfacePlantAGS = SurfacePlantAGS(self)
143-
self.economics: AGSEconomics = AGSEconomics(self)
144-
self.outputs: AGSOutputs = AGSOutputs(self, output_file=output_file)
143+
self.logger.info('Setup the AGS elements of the Model and instantiate new attributes as needed')
145144
self.wellbores.IsAGS.value = True
145+
if not isinstance(self.reserv, SBTReservoir):
146+
if self.InputParameters['Economic Model'].sValue not in ['4', 'Simple (CLGS)']:
147+
# must be doing wangju approach, # so go back to using default objects
148+
self.surfaceplant = SurfacePlant(self)
149+
self.economics = Economics(self)
150+
# Must be doing CLGS, so we need to instantiate the right objects
151+
self.reserv: CylindricalReservoir = CylindricalReservoir(self)
152+
self.wellbores: WellBores = AGSWellBores(self)
153+
self.surfaceplant: SurfacePlantAGS = SurfacePlantAGS(self)
154+
self.economics: AGSEconomics = AGSEconomics(self)
155+
self.outputs: AGSOutputs = AGSOutputs(self, output_file=output_file)
156+
157+
# initialize the right Power Plant Type
158+
if 'Power Plant Type' in self.InputParameters:
159+
# electricity
160+
if self.InputParameters['Power Plant Type'].sValue in ['1', 'Subcritical ORC']:
161+
self.surfaceplant = SurfacePlantSubcriticalOrc(self)
162+
elif self.InputParameters['Power Plant Type'].sValue in ['2', 'Supercritical ORC']:
163+
self.surfaceplant = SurfacePlantSupercriticalOrc(self)
164+
elif self.InputParameters['Power Plant Type'].sValue in ['3', 'Single-Flash']:
165+
self.surfaceplant = SurfacePlantSingleFlash(self)
166+
elif self.InputParameters['Power Plant Type'].sValue in ['4', 'Double-Flash']:
167+
self.surfaceplant = SurfacePlantDoubleFlash(self)
168+
# Heat applications
169+
elif self.InputParameters['Power Plant Type'].sValue in ['5', 'Absorption Chiller']:
170+
self.surfaceplant = SurfacePlantAbsorptionChiller(self)
171+
elif self.InputParameters['Power Plant Type'].sValue in ['6', 'Heat Pump']:
172+
self.surfaceplant = SurfacePlantHeatPump(self)
173+
elif self.InputParameters['Power Plant Type'].sValue in ['7', 'District Heating']:
174+
self.surfaceplant = SurfacePlantDistrictHeating(self)
175+
elif self.InputParameters['Power Plant Type'].sValue in ['8', 'Reservoir Thermal Energy Storage']:
176+
self.surfaceplant = SurfacePlantSUTRA(self)
177+
elif self.InputParameters['Power Plant Type'].sValue in ['9', 'Industrial']:
178+
self.surfaceplant = SurfacePlantIndustrialHeat(self)
146179

147180
# if we find out we have an add-ons, we need to instantiate it, then read for the parameters
148181
if 'AddOn Nickname 1' in self.InputParameters:
@@ -159,6 +192,7 @@ def __init__(self, enable_geophires_logging_config=True, input_file=None):
159192

160193
self.logger.info(f'Complete {__class__}: {__name__}')
161194

195+
162196
def __str__(self):
163197
return "Model"
164198

src/geophires_x/WellBores.py

Lines changed: 65 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,71 @@
1515
# code from Koenraad
1616

1717

18+
def calculate_total_drilling_lengths_m(Configuration, numnonverticalsections: int, nonvertical_length_km: float,
19+
InputDepth_km: float, OutputDepth_km: float, nprod:int, ninj:int,
20+
junction_depth_km: float = 0.0, angle_rad: float = 0.0) -> tuple:
21+
"""
22+
returns the total length, vertical length, and non-vertical lengths, depending on the configuration
23+
:param Configuration: configuration of the well
24+
:type Configuration: :class:`~geophires
25+
:param numnonverticalsections: number of non-vertical sections
26+
:type numnonverticalsections: int
27+
:param nonvertical_length_km: length of non-vertical sections in km
28+
:type nonvertical_length_km: float
29+
:param InputDepth_km: depth of the well in km
30+
:type InputDepth_km: float
31+
:param OutputDepth_km: depth of the output end of the well in km, if U shaped, and not horizontal
32+
:type OutputDepth_km: float
33+
:param nprod: number of production wells
34+
:type nprod: int
35+
:param ninj: number of injection wells
36+
:param junction_depth_km: depth of the junction in km
37+
:type junction_depth_km: float
38+
:param angle_rad: angle of the well in radians, from horizontal
39+
:type angle_rad: float
40+
:return: total length, vertical length, lateral, and junction lengths in meters
41+
:rtype: tuple
42+
"""
43+
tot_pipe_length_m = vertical_pipe_length_m = lateral_pipe_length_m = tot_to_junction_m = 0.0
44+
if Configuration is Configuration.ULOOP:
45+
# Total drilling depth of both wells and laterals in U-loop [m]
46+
vertical_pipe_length_m = (nprod * InputDepth_km * 1000.0) + (ninj * OutputDepth_km * 1000.0)
47+
lateral_pipe_length_m = numnonverticalsections * nonvertical_length_km * 1000.0
48+
49+
elif Configuration is Configuration.COAXIAL:
50+
# Total drilling depth of well and lateral in co-axial case [m] - is not necessarily only vertical
51+
vertical_pipe_length_m = (nprod + ninj) * InputDepth_km * 1000.0
52+
lateral_pipe_length_m = numnonverticalsections * nonvertical_length_km * 1000.0
53+
54+
elif Configuration is Configuration.VERTICAL:
55+
# Total drilling depth of well in vertical case [m]
56+
vertical_pipe_length_m = (nprod + ninj) * InputDepth_km * 1000.0
57+
lateral_pipe_length_m = 0.0
58+
59+
elif Configuration is Configuration.L:
60+
# Total drilling depth of well in L case [m]
61+
vertical_pipe_length_m = (nprod + ninj) * InputDepth_km * 1000.0
62+
lateral_pipe_length_m = numnonverticalsections * nonvertical_length_km * 1000.0
63+
64+
elif Configuration is Configuration.EAVORLOOP:
65+
# Total drilling length of well in EavorLoop [m]
66+
vertical_pipe_length_m = (nprod + ninj) * InputDepth_km * 1000.0
67+
68+
# now calculate the distance from the bottom of the vertical to the junction of the laterals [m]
69+
O1 = (junction_depth_km - InputDepth_km) * 1000.0 # in meters
70+
tot_to_junction_m = (O1 / math.sin(angle_rad)) * 2 # there are two of these of each EavorLoop
71+
72+
# now calculate the distance from the junction of the laterals to the end of the laterals [m]
73+
O2 = (OutputDepth_km - junction_depth_km) * 1000.0 # in meters
74+
lateral_pipe_length_m = (O2 / math.sin(angle_rad)) * 2 # there are two of these of each lateral of an EavorLoop
75+
lateral_pipe_length_m = lateral_pipe_length_m * numnonverticalsections # there are numnonverticalsections of these
76+
else:
77+
raise ValueError(f'Invalid Configuration: {Configuration}')
78+
79+
tot_pipe_length_m = vertical_pipe_length_m + lateral_pipe_length_m + tot_to_junction_m
80+
return tot_pipe_length_m, vertical_pipe_length_m, lateral_pipe_length_m, tot_to_junction_m
81+
82+
1883
def InjectionReservoirPressurePredictor(project_lifetime_yr: int, timesteps_per_year: int, initial_pressure_kPa: float,
1984
inflation_rate: float) -> list:
2085
"""
@@ -1363,46 +1428,3 @@ def Calculate(self, model: Model) -> None:
13631428
self.PumpingPower.value = [0. if x < 0. else x for x in self.PumpingPower.value]
13641429

13651430
model.logger.info(f'complete {self.__class__.__name__}: {__name__}')
1366-
1367-
1368-
def calculate_total_drilling_lengths_m(Configuration, numnonverticalsections: int, nonvertical_length_km: float,
1369-
InputDepth_km: float, OutputDepth_km: float, nprod:int, ninj:int) -> tuple:
1370-
"""
1371-
returns the total length, vertical length, and non-vertical lengths, depending on the configuration
1372-
:param Configuration: Configuration of the well
1373-
:type Configuration: :class:`~geophires
1374-
:param numnonverticalsections: number of non-vertical sections
1375-
:type numnonverticalsections: int
1376-
:param nonvertical_length_km: length of non-vertical sections in km
1377-
:type nonvertical_length_km: float
1378-
:param InputDepth_km: depth of the well in km
1379-
:type InputDepth_km: float
1380-
:param OutputDepth_km: depth of the output end of the well in km, if U shaped, and not horizontal
1381-
:type OutputDepth_km: float
1382-
:param nprod: number of production wells
1383-
:type nprod: int
1384-
:param ninj: number of injection wells
1385-
:return: total length, vertical length, and horizontal lengths in meters
1386-
:rtype: tuple
1387-
"""
1388-
if Configuration == Configuration.ULOOP:
1389-
# Total drilling depth of both wells and laterals in U-loop [m]
1390-
vertical_pipe_length_m = (nprod * InputDepth_km * 1000.0) + (ninj * OutputDepth_km * 1000.0)
1391-
nonvertical_pipe_length_m = numnonverticalsections * nonvertical_length_km * 1000.0
1392-
elif Configuration == Configuration.COAXIAL:
1393-
# Total drilling depth of well and lateral in co-axial case [m]
1394-
vertical_pipe_length_m = (nprod + ninj) * InputDepth_km * 1000.0
1395-
nonvertical_pipe_length_m = numnonverticalsections * nonvertical_length_km * 1000.0
1396-
elif Configuration == Configuration.VERTICAL:
1397-
# Total drilling depth of well in vertical case [m]
1398-
vertical_pipe_length_m = (nprod + ninj) * InputDepth_km * 1000.0
1399-
nonvertical_pipe_length_m = 0.0
1400-
elif Configuration == Configuration.L:
1401-
# Total drilling depth of well in L case [m]
1402-
vertical_pipe_length_m = (nprod + ninj) * InputDepth_km * 1000.0
1403-
nonvertical_pipe_length_m = numnonverticalsections * nonvertical_length_km * 1000.0
1404-
else:
1405-
raise ValueError(f'Invalid Configuration: {Configuration}')
1406-
1407-
tot_pipe_length_m = vertical_pipe_length_m + nonvertical_pipe_length_m
1408-
return tot_pipe_length_m, vertical_pipe_length_m, nonvertical_pipe_length_m

0 commit comments

Comments
 (0)