Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions src/geophires_x/Economics.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,10 @@ def CalculateLCOELCOHLCOC(self, model: Model) -> tuple:
NPVcap = np.sum((1 + self.inflrateconstruction.value) * self.CCap.value * CRF * discountvector)
NPVfc = np.sum((1 + self.inflrateconstruction.value) * self.CCap.value * self.PTR.value * inflationvector * discountvector)
NPVit = np.sum(self.CTR.value / (1 - self.CTR.value) * ((1 + self.inflrateconstruction.value) * self.CCap.value * CRF - self.CCap.value / model.surfaceplant.plant_lifetime.value) * discountvector)
NPVitc = (1 + self.inflrateconstruction.value) * self.CCap.value * self.RITC.value / (1 - self.CTR.value)

npv_itc_discount_factor = discountvector[model.surfaceplant.construction_years.value]
NPVitc = ((1 + self.inflrateconstruction.value) * self.CCap.value * self.RITC.value / (1 - self.CTR.value)
* npv_itc_discount_factor)

if model.surfaceplant.enduse_option.value == EndUseOptions.ELECTRICITY:
NPVoandm = np.sum(self.Coam.value * inflationvector * discountvector)
Expand All @@ -502,7 +505,8 @@ def CalculateLCOELCOHLCOC(self, model: Model) -> tuple:
NPVcap_elec = np.sum((1 + self.inflrateconstruction.value) * CCap_elec * CRF * discountvector)
NPVfc_elec = np.sum((1 + self.inflrateconstruction.value) * CCap_elec * self.PTR.value * inflationvector * discountvector)
NPVit_elec = np.sum(self.CTR.value / (1 - self.CTR.value) * ((1 + self.inflrateconstruction.value) * CCap_elec * CRF - CCap_elec / model.surfaceplant.plant_lifetime.value) * discountvector)
NPVitc_elec = (1 + self.inflrateconstruction.value) * CCap_elec * self.RITC.value / (1 - self.CTR.value)
NPVitc_elec = ((1 + self.inflrateconstruction.value) * CCap_elec * self.RITC.value / (1 - self.CTR.value)
* npv_itc_discount_factor)
NPVoandm_elec = np.sum(Coam_elec * inflationvector * discountvector)
NPVgrt_elec = self.GTR.value / (1 - self.GTR.value) * (NPVcap_elec + NPVoandm_elec + NPVfc_elec + NPVit_elec - NPVitc_elec)

Expand All @@ -512,7 +516,8 @@ def CalculateLCOELCOHLCOC(self, model: Model) -> tuple:
NPVcap_heat = np.sum((1 + self.inflrateconstruction.value) * CCap_heat * CRF * discountvector)
NPVfc_heat = np.sum((1 + self.inflrateconstruction.value) * (self.CCap.value * (1.0 - self.CAPEX_heat_electricity_plant_ratio.value)) * self.PTR.value * inflationvector * discountvector)
NPVit_heat = np.sum(self.CTR.value / (1 - self.CTR.value) * ((1 + self.inflrateconstruction.value) * CCap_heat * CRF - CCap_heat / model.surfaceplant.plant_lifetime.value) * discountvector)
NPVitc_heat = (1 + self.inflrateconstruction.value) * CCap_heat * self.RITC.value / (1 - self.CTR.value)
NPVitc_heat = ((1 + self.inflrateconstruction.value) * CCap_heat * self.RITC.value / (1 - self.CTR.value)
* npv_itc_discount_factor)
NPVoandm_heat = np.sum((self.Coam.value * (1.0 - self.CAPEX_heat_electricity_plant_ratio.value)) * inflationvector * discountvector)
NPVgrt_heat = self.GTR.value / (1 - self.GTR.value) * (NPVcap_heat + NPVoandm_heat + NPVfc_heat + NPVit_heat - NPVitc_heat)

Expand All @@ -535,7 +540,7 @@ def CalculateLCOELCOHLCOC(self, model: Model) -> tuple:
NPVgrt = self.GTR.value / (1 - self.GTR.value) * (NPVcap + NPVoandm + NPVfc + NPVit - NPVitc)
LCOH = (NPVcap + NPVoandm + NPVfc + NPVit + NPVgrt - NPVitc) / np.sum(
model.surfaceplant.HeatkWhProduced.value * inflationvector * discountvector) * 1E8
LCOH = self.LCOH.value * 2.931 # $/MMBTU
LCOH = LCOH * 2.931 # $/MMBTU

elif model.surfaceplant.enduse_option.value == EndUseOptions.HEAT and model.surfaceplant.plant_type.value == PlantType.DISTRICT_HEATING:
PumpingCosts = model.surfaceplant.PumpingkWh.value * model.surfaceplant.electricity_cost_to_buy.value / 1E6
Expand Down Expand Up @@ -974,7 +979,8 @@ def __init__(self, model: Model):
PreferredUnits=PercentUnit.TENTH,
CurrentUnits=PercentUnit.TENTH,
ErrMessage="assume default investment tax credit rate (0)",
ToolTipText="Investment tax credit rate (see docs)"
ToolTipText="Investment tax credit rate "
"(see https://programs.dsireusa.org/system/program/detail/658)"
)
self.PTR = self.ParameterDict[self.PTR.Name] = floatParameter(
"Property Tax Rate",
Expand Down Expand Up @@ -2687,11 +2693,6 @@ def Calculate(self, model: Model) -> None:
else:
self.CCap.value = self.totalcapcost.value

# update the capitol costs, assuming the entire ITC is used to reduce the capitol costs
if self.RITC.Provided:
self.RITCValue.value = self.RITC.value * self.CCap.value
self.CCap.value = self.CCap.value - self.RITCValue.value

# Add in the FlatLicenseEtc, OtherIncentives, & TotalGrant
self.CCap.value = self.CCap.value + self.FlatLicenseEtc.value - self.OtherIncentives.value - self.TotalGrant.value

Expand Down Expand Up @@ -2928,6 +2929,12 @@ def Calculate(self, model: Model) -> None:
model.surfaceplant.plant_lifetime.value + model.surfaceplant.construction_years.value, 1):
self.TotalRevenue.value[i] = self.TotalRevenue.value[i] - self.Coam.value


if self.RITC.Provided:
self.RITCValue.value = self.RITC.value * self.CCap.value
# ITC is credited year after construction
self.TotalRevenue.value[model.surfaceplant.construction_years.value] += self.RITCValue.value

# Now do a one-time calculation that calculates the cumulative cash flow after everything else has been accounted for
for i in range(1, model.surfaceplant.plant_lifetime.value + model.surfaceplant.construction_years.value, 1):
self.TotalCummRevenue.value[i] = self.TotalCummRevenue.value[i-1] + self.TotalRevenue.value[i]
Expand Down
2 changes: 1 addition & 1 deletion src/geophires_x_schema_generator/geophires-request.json
Original file line number Diff line number Diff line change
Expand Up @@ -1644,7 +1644,7 @@
"maximum": 1.0
},
"Investment Tax Credit Rate": {
"description": "Investment tax credit rate (see docs)",
"description": "Investment tax credit rate (see https://programs.dsireusa.org/system/program/detail/658)",
"type": "number",
"units": "",
"category": "Economics",
Expand Down
24 changes: 12 additions & 12 deletions tests/examples/Fervo_Project_Cape-3.out
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@

Simulation Metadata
----------------------
GEOPHIRES Version: 3.7.23
Simulation Date: 2025-03-10
Simulation Time: 10:42
Calculation Time: 0.871 sec
GEOPHIRES Version: 3.8.9
Simulation Date: 2025-04-02
Simulation Time: 12:41
Calculation Time: 0.860 sec

***SUMMARY OF RESULTS***

End-Use Option: Electricity
Average Net Electricity Production: 404.31 MW
Electricity breakeven price: 2.77 cents/kWh
Electricity breakeven price: 3.76 cents/kWh
Number of production wells: 39
Number of injection wells: 39
Flowrate per production well: 120.0 kg/sec
Expand All @@ -27,10 +27,10 @@ Simulation Metadata
Accrued financing during construction: 5.00
Project lifetime: 20 yr
Capacity factor: 90.0 %
Project NPV: 4580.36 MUSD
Project IRR: 43.75 %
Project VIR=PI=PIR: 5.27
Project MOIC: 6.30
Project NPV: 4550.28 MUSD
Project IRR: 39.26 %
Project VIR=PI=PIR: 3.97
Project MOIC: 4.91
Project Payback Period: 3.38 yr
Estimated Jobs Created: 976

Expand Down Expand Up @@ -102,7 +102,7 @@ Simulation Metadata
Total surface equipment costs: 969.26 MUSD
Exploration costs: 30.00 MUSD
Investment Tax Credit: -459.83 MUSD
Total capital costs: 1072.95 MUSD
Total capital costs: 1532.78 MUSD


***OPERATING AND MAINTENANCE COSTS (M$/yr)***
Expand Down Expand Up @@ -193,8 +193,8 @@ 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/lb) (MUSD/yr) (MUSD) |(MUSD/yr) (MUSD/yr) (MUSD)
________________________________________________________________________________________________________________________________________________________________________________________
0 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 -1072.95 -1072.95
1 15.00 474.16 474.16 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 26.96 447.20 -625.75
0 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 -1532.78 -1532.78
1 15.00 474.16 474.16 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 26.96 907.04 -625.75
2 15.00 476.35 950.51 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 26.96 449.39 -176.36
3 15.41 489.93 1440.44 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 26.96 462.97 286.61
4 15.81 503.25 1943.69 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 26.96 476.29 762.90
Expand Down
24 changes: 12 additions & 12 deletions tests/examples/example_ITC.out
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@

Simulation Metadata
----------------------
GEOPHIRES Version: 3.8.4
Simulation Date: 2025-03-19
Simulation Time: 10:30
Calculation Time: 0.808 sec
GEOPHIRES Version: 3.8.9
Simulation Date: 2025-04-02
Simulation Time: 12:41
Calculation Time: 0.777 sec

***SUMMARY OF RESULTS***

End-Use Option: Electricity
Average Net Electricity Production: 18.84 MW
Electricity breakeven price: 3.21 cents/kWh
Electricity breakeven price: 4.89 cents/kWh
Number of production wells: 2
Number of injection wells: 2
Flowrate per production well: 55.0 kg/sec
Expand All @@ -27,10 +27,10 @@ Simulation Metadata
Accrued financing during construction: 0.00
Project lifetime: 30 yr
Capacity factor: 90.0 %
Project NPV: 10.31 MUSD
Project IRR: 8.82 %
Project VIR=PI=PIR: 1.19
Project MOIC: 0.72
Project NPV: 6.77 MUSD
Project IRR: 8.05 %
Project VIR=PI=PIR: 1.06
Project MOIC: 0.52
Project Payback Period: 11.46 yr
Estimated Jobs Created: 41

Expand Down Expand Up @@ -99,7 +99,7 @@ Simulation Metadata
Total surface equipment costs: 62.40 MUSD
Exploration costs: 8.24 MUSD
Investment Tax Credit: -54.20 MUSD
Total capital costs: 54.20 MUSD
Total capital costs: 108.39 MUSD


***OPERATING AND MAINTENANCE COSTS (M$/yr)***
Expand Down Expand Up @@ -210,8 +210,8 @@ 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/lb) (MUSD/yr) (MUSD) |(MUSD/yr) (MUSD/yr) (MUSD)
________________________________________________________________________________________________________________________________________________________________________________________
0 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 -54.20 -54.20
1 5.50 8.02 8.02 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 2.95 5.07 -49.13
0 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 0.00 0.00 | 0.00 -108.39 -108.39
1 5.50 8.02 8.02 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 2.95 59.26 -49.13
2 5.50 8.09 16.11 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 2.95 5.14 -43.99
3 5.50 8.12 24.23 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 2.95 5.17 -38.82
4 5.50 8.13 32.36 | 2.50 0.00 0.00 | 2.50 0.00 0.00 | 0.00 0.00 0.00 | 2.95 5.18 -33.64
Expand Down
17 changes: 15 additions & 2 deletions tests/test_geophires_x.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,8 +610,8 @@ def _get_result(base_example: str, do_discount: bool) -> GeophiresXResult:
def _npv(r: GeophiresXResult) -> dict:
return r.result['ECONOMIC PARAMETERS']['Project NPV']['value']

self.assertEqual(4580.36, _npv(_get_result('Fervo_Project_Cape-3', False)))
self.assertEqual(4280.71, _npv(_get_result('Fervo_Project_Cape-3', True)))
self.assertEqual(4550.28, _npv(_get_result('Fervo_Project_Cape-3', False)))
self.assertEqual(4252.6, _npv(_get_result('Fervo_Project_Cape-3', True)))

def _extended_economics_npv(r: GeophiresXResult) -> dict:
return r.result['EXTENDED ECONOMICS']['Project NPV (including AddOns)']['value']
Expand Down Expand Up @@ -854,3 +854,16 @@ def test_field_gathering_cost(self):
)

self.assertEqual(fg_cost, result.result['CAPITAL COSTS (M$)']['Field gathering system costs']['value'])

def test_heat_pump_lcoh_bicycle(self):
result = GeophiresXClient().get_geophires_result(
GeophiresInputParameters(
from_file_path=self._get_test_file_path('examples/example10_HP.txt'),
params={
'Economic Model': 3,
},
)
)

lcoh = result.result['SUMMARY OF RESULTS']['Direct-Use heat breakeven price (LCOH)']['value']
self.assertTrue(10 < lcoh < 20) # Sanity-check that value is non-zero and broadly within the expected range.
Loading