Skip to content

Commit 2029282

Browse files
Apply drilling cost adjustment to both production and injection wells if injection well factor is not specified NREL#332
1 parent 4f4cad5 commit 2029282

File tree

5 files changed

+154
-27
lines changed

5 files changed

+154
-27
lines changed

src/geophires_x/Economics.py

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,7 +1753,8 @@ def __init__(self, model: Model):
17531753
CurrentUnits=PercentUnit.TENTH
17541754
)
17551755
self.ProjectMOIC = self.OutputParameterDict[self.ProjectMOIC.Name] = OutputParameter(
1756-
"Project Multiple of Invested Capital",
1756+
"Project MOIC",
1757+
ToolTipText="Project Multiple of Invested Capital",
17571758
UnitType=Units.PERCENT,
17581759
PreferredUnits=PercentUnit.TENTH,
17591760
CurrentUnits=PercentUnit.TENTH
@@ -1910,35 +1911,30 @@ def read_parameters(self, model: Model) -> None:
19101911
ParameterToModify.value = 1.0
19111912
elif ParameterToModify.Name == "Well Drilling and Completion Capital Cost Adjustment Factor":
19121913
if self.per_production_well_cost.Valid and ParameterToModify.Valid:
1913-
print("Warning: Provided well drilling and completion cost adjustment factor not" +
1914-
" considered because valid total well drilling and completion cost provided.")
1915-
model.logger.warning("Provided well drilling and completion cost adjustment factor not" +
1916-
" considered because valid total well drilling and completion cost provided.")
1914+
msg = ('Provided well drilling and completion cost adjustment factor not considered '
1915+
'because valid total well drilling and completion cost provided.')
1916+
print(f'Warning: {msg}')
1917+
model.logger.warning(msg)
19171918
elif not self.per_production_well_cost.Provided and not self.production_well_cost_adjustment_factor.Provided:
19181919
ParameterToModify.value = 1.0
1919-
print("Warning: No valid well drilling and completion total cost or adjustment" +
1920-
" factor provided. GEOPHIRES will assume default built-in well drilling and" +
1921-
" completion cost correlation with adjustment factor = 1.")
1922-
model.logger.warning(
1923-
"No valid well drilling and completion total cost or adjustment factor" +
1924-
" provided. GEOPHIRES will assume default built-in well drilling and completion cost" +
1925-
" correlation with adjustment factor = 1.")
1920+
msg = ("No valid well drilling and completion total cost or adjustment factor provided. "
1921+
"GEOPHIRES will assume default built-in well drilling and completion cost "
1922+
"correlation with adjustment factor = 1.")
1923+
print(f'Warning: {msg}')
1924+
model.logger.warning(msg)
19261925
elif self.per_production_well_cost.Provided and not self.per_production_well_cost.Valid:
1927-
print("Warning: Provided well drilling and completion cost outside of range 0-1000." +
1928-
" GEOPHIRES will assume default built-in well drilling and completion cost correlation" +
1929-
" with adjustment factor = 1.")
1930-
model.logger.warning("Provided well drilling and completion cost outside of range 0-1000." +
1931-
" GEOPHIRES will assume default built-in well drilling and completion cost correlation with" +
1932-
" adjustment factor = 1.")
1926+
msg = ("Provided well drilling and completion cost outside of range 0-1000. GEOPHIRES "
1927+
"will assume default built-in well drilling and completion cost correlation "
1928+
"with adjustment factor = 1.")
1929+
print(f'Warning: {msg}')
1930+
model.logger.warning(msg)
19331931
self.production_well_cost_adjustment_factor.value = 1.0
19341932
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:
1935-
print("Warning: Provided well drilling and completion cost adjustment factor outside" +
1936-
" of range 0-10. GEOPHIRES will assume default built-in well drilling and completion" +
1937-
" cost correlation with adjustment factor = 1.")
1938-
model.logger.warning(
1939-
"Provided well drilling and completion cost adjustment factor outside" +
1940-
" of range 0-10. GEOPHIRES will assume default built-in well drilling and completion" +
1941-
" cost correlation with adjustment factor = 1.")
1933+
msg = ("Provided well drilling and completion cost adjustment factor outside of range "
1934+
"0-10. GEOPHIRES will assume default built-in well drilling and completion cost "
1935+
"correlation with adjustment factor = 1.")
1936+
print(f'Warning: {msg}')
1937+
model.logger.warning(msg)
19421938
self.production_well_cost_adjustment_factor.value = 1.0
19431939
elif ParameterToModify.Name == "Wellfield O&M Cost Adjustment Factor":
19441940
if self.oamtotalfixed.Valid:
@@ -2169,6 +2165,7 @@ def read_parameters(self, model: Model) -> None:
21692165

21702166
coerce_int_params_to_enum_values(self.ParameterDict)
21712167
self.sync_interest_rate(model)
2168+
self.sync_well_drilling_and_completion_capital_cost_adjustment_factor(model)
21722169

21732170
model.logger.info(f'complete {__class__!s}: {sys._getframe().f_code.co_name}')
21742171

@@ -2198,6 +2195,16 @@ def discount_rate_display() -> str:
21982195

21992196
self.interest_rate.value = self.discountrate.quantity().to(convertible_unit(self.interest_rate.CurrentUnits)).magnitude
22002197

2198+
def sync_well_drilling_and_completion_capital_cost_adjustment_factor(self, model):
2199+
if (self.production_well_cost_adjustment_factor.Provided
2200+
and not self.injection_well_cost_adjustment_factor.Provided):
2201+
factor = self.production_well_cost_adjustment_factor.value
2202+
self.injection_well_cost_adjustment_factor.value = factor
2203+
model.logger.info(
2204+
f'Set {self.injection_well_cost_adjustment_factor.Name} to {factor} because '
2205+
f'{self.production_well_cost_adjustment_factor.Name} was provided.')
2206+
2207+
22012208
def Calculate(self, model: Model) -> None:
22022209
"""
22032210
The Calculate function is where all the calculations are done.

src/geophires_x/Outputs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1646,7 +1646,7 @@ def PrintOutputs(self, model: Model):
16461646
f.write(f' Project NPV: {model.economics.ProjectNPV.value:10.2f} ' + model.economics.ProjectNPV.PreferredUnits.value + NL)
16471647
f.write(f' Project IRR: {model.economics.ProjectIRR.value:10.2f} ' + model.economics.ProjectIRR.PreferredUnits.value + NL)
16481648
f.write(f' Project VIR=PI=PIR: {model.economics.ProjectVIR.value:10.2f}' + NL)
1649-
f.write(f' Project MOIC: {model.economics.ProjectMOIC.value:10.2f}' + NL)
1649+
f.write(f' {model.economics.ProjectMOIC.Name}: {model.economics.ProjectMOIC.value:10.2f}' + NL)
16501650

16511651
payback_period_val = model.economics.ProjectPaybackPeriod.value
16521652
project_payback_period_display = f'{payback_period_val:10.2f} {model.economics.ProjectPaybackPeriod.PreferredUnits.value}' \

src/geophires_x/OutputsCCUS.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def PrintOutputs(self, model):
3636
f.write(f" Project NPV (including carbon credit): {model.ccuseconomics.ProjectNPV.value:10.2f} " + model.ccuseconomics.ProjectNPV.PreferredUnits.value + NL)
3737
f.write(f" Project IRR (including carbon credit): {model.ccuseconomics.ProjectIRR.value:10.2f} " + model.ccuseconomics.ProjectIRR.PreferredUnits.value + NL)
3838
f.write(f" Project VIR=IR=PIR (including carbon credit): {model.ccuseconomics.ProjectVIR.value:10.2f}" + NL)
39-
f.write(f" Project MOIC (including carbon credit): {model.ccuseconomics.ProjectMOIC.value:10.2f}" + NL)
39+
f.write(f" {model.economics.ProjectMOIC.Name} (including carbon credit): {model.ccuseconomics.ProjectMOIC.value:10.2f}" + NL)
4040
f.write(NL)
4141
f.write(NL)
4242
f.write(" ******************" + NL)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
Reservoir Model, 1
2+
Reservoir Volume Option, 1
3+
Reservoir Density, 2800
4+
#Reservoir Depth, 8500 feet, -- https://pangea.stanford.edu/ERE/db/GeoConf/papers/SGW/2024/Fercho.pdf
5+
Reservoir Heat Capacity, 790
6+
Reservoir Thermal Conductivity, 3.05
7+
Reservoir Porosity, 0.0118
8+
Reservoir Impedance, 0.001
9+
10+
Number of Fractures, 149
11+
Fracture Shape, 4
12+
Fracture Height, 2000
13+
Fracture Width, 10000
14+
Fracture Separation, 30
15+
16+
Number of Segments, 1
17+
18+
Production Well Diameter, 7
19+
Injection Well Diameter, 7
20+
Well Separation, 365 feet
21+
Injection Temperature, 60 degC
22+
Injection Wellbore Temperature Gain, 3
23+
Plant Outlet Pressure, 1000 psi
24+
#Production Wellhead Pressure, 325 psi
25+
Ramey Production Wellbore Model, 1
26+
Utilization Factor, .9
27+
Water Loss Fraction, 0.05
28+
Maximum Drawdown, 1
29+
Ambient Temperature, 10 degC
30+
#Surface Temperature, 10 degC
31+
End-Use Option, 1
32+
33+
Plant Lifetime, 25
34+
35+
#Power Plant Type, 3
36+
Circulation Pump Efficiency, 0.80
37+
38+
#Exploration Capital Cost, 30
39+
#Reservoir Stimulation Capital Cost, 234, -- 78 wells @ $3M/well
40+
Economic Model, 3
41+
Starting Electricity Sale Price, 0.15
42+
Ending Electricity Sale Price, 1.00
43+
Electricity Escalation Rate Per Year, 0.004053223
44+
Electricity Escalation Start Year, 1
45+
Fraction of Investment in Bonds, .5
46+
Combined Income Tax Rate, .3
47+
Gross Revenue Tax Rate, 0
48+
Inflated Bond Interest Rate, .05
49+
Inflated Equity Interest Rate, .08
50+
Inflation Rate, .02
51+
Investment Tax Credit Rate, .3, -- https://programs.dsireusa.org/system/program/detail/658
52+
Production Tax Credit Electricity, 0.0275, -- https://programs.dsireusa.org/system/program/detail/734
53+
Inflation Rate During Construction, 0.05
54+
Property Tax Rate, 0
55+
Time steps per year, 10
56+
Maximum Temperature, 500
57+
58+
59+
Print Output to Console, 0
60+
Surface Temperature, 12
61+
Reservoir Depth, 5.4
62+
Gradient 1, 36.7
63+
Power Plant Type, 4
64+
65+
# Well Drilling and Completion Capital Cost Adjustment Factor, 1.175
66+
Number of Injection Wells, 73
67+
Number of Production Wells, 73
68+
Production Flow Rate per Well, 80

tests/test_geophires_x.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,3 +595,55 @@ def test_transmission_pipeline_cost(self):
595595
self.assertAlmostEqual(
596596
result.result['CAPITAL COSTS (M$)']['Transmission pipeline cost']['value'], 3.75, delta=0.5
597597
)
598+
599+
def test_well_drilling_and_completion_capital_cost_adjustment_factor(self):
600+
base_file = self._get_test_file_path('drilling-adjustment-factor.txt')
601+
r_no_adj = GeophiresXClient().get_geophires_result(GeophiresInputParameters(from_file_path=base_file))
602+
603+
r_noop_adj = GeophiresXClient().get_geophires_result(
604+
GeophiresInputParameters(
605+
from_file_path=base_file,
606+
params={'Well Drilling and Completion Capital Cost Adjustment Factor': 1.0},
607+
)
608+
)
609+
610+
r_adj = GeophiresXClient().get_geophires_result(
611+
GeophiresInputParameters(
612+
from_file_path=base_file,
613+
params={'Well Drilling and Completion Capital Cost Adjustment Factor': 1.175},
614+
)
615+
)
616+
617+
def c_well(r, prod: bool = False, inj: bool = False):
618+
well_type = 'production ' if prod else 'injection ' if inj else ''
619+
try:
620+
c = r.result['CAPITAL COSTS (M$)'][f'Drilling and completion costs per {well_type}well']['value']
621+
622+
if not prod and not inj:
623+
# indirect cost is not applied to prod/inj-specific per-well cost
624+
default_indirect_cost_factor = 1.05
625+
c = c / default_indirect_cost_factor
626+
627+
return c
628+
except TypeError:
629+
return None
630+
631+
self.assertEqual(c_well(r_no_adj), c_well(r_noop_adj))
632+
633+
self.assertAlmostEqual(1.175 * c_well(r_no_adj), c_well(r_adj), delta=0.1)
634+
635+
r_adj_diff_prod_inj = GeophiresXClient().get_geophires_result(
636+
GeophiresInputParameters(
637+
from_file_path=base_file,
638+
params={
639+
'Well Drilling and Completion Capital Cost Adjustment Factor': 1.175,
640+
'Injection Well Drilling and Completion Capital Cost Adjustment Factor': 3,
641+
},
642+
)
643+
)
644+
645+
c_well_no_adj = c_well(r_no_adj)
646+
c_prod_well_adj = c_well(r_adj_diff_prod_inj, prod=True)
647+
c_inj_well_adj = c_well(r_adj_diff_prod_inj, inj=True)
648+
self.assertAlmostEqual(1.175 * c_well_no_adj, c_prod_well_adj, delta=0.1)
649+
self.assertAlmostEqual(3 * c_well_no_adj, c_inj_well_adj, delta=0.1)

0 commit comments

Comments
 (0)