From 30f8e11cc3eaaa88acf49f973dc6c7291b68e984 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:43:35 -0700 Subject: [PATCH 1/7] fix comment typo 'capital' instead of 'capitol' --- src/geophires_x/Economics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index dbfd45083..70c9d3a05 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -2687,7 +2687,7 @@ 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 + # update the capital costs, assuming the entire ITC is used to reduce the capital costs if self.RITC.Provided: self.RITCValue.value = self.RITC.value * self.CCap.value self.CCap.value = self.CCap.value - self.RITCValue.value From 55a52ba438c6547e31282f1a51817fb8f67366cc Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Wed, 2 Apr 2025 12:30:53 -0700 Subject: [PATCH 2/7] Apply ITC credit as revenue after construction rather than deduction of capital costs --- src/geophires_x/Economics.py | 14 ++++++++++---- tests/examples/Fervo_Project_Cape-3.out | 24 ++++++++++++------------ tests/examples/example_ITC.out | 24 ++++++++++++------------ 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 70c9d3a05..5e78d8a6d 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -2687,10 +2687,10 @@ def Calculate(self, model: Model) -> None: else: self.CCap.value = self.totalcapcost.value - # update the capital costs, assuming the entire ITC is used to reduce the capital costs - if self.RITC.Provided: - self.RITCValue.value = self.RITC.value * self.CCap.value - self.CCap.value = self.CCap.value - self.RITCValue.value + # # update the capital costs, assuming the entire ITC is used to reduce the capital 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 @@ -2928,6 +2928,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] diff --git a/tests/examples/Fervo_Project_Cape-3.out b/tests/examples/Fervo_Project_Cape-3.out index 2d30aaaf7..679e3a434 100644 --- a/tests/examples/Fervo_Project_Cape-3.out +++ b/tests/examples/Fervo_Project_Cape-3.out @@ -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:28 + Calculation Time: 0.853 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.59 cents/kWh Number of production wells: 39 Number of injection wells: 39 Flowrate per production well: 120.0 kg/sec @@ -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 @@ -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)*** @@ -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 diff --git a/tests/examples/example_ITC.out b/tests/examples/example_ITC.out index 7c2e338e9..ee3cbe06a 100644 --- a/tests/examples/example_ITC.out +++ b/tests/examples/example_ITC.out @@ -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:28 + Calculation Time: 0.783 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.44 cents/kWh Number of production wells: 2 Number of injection wells: 2 Flowrate per production well: 55.0 kg/sec @@ -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 @@ -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)*** @@ -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 From 03bd4774d7ce357de20eac403b45480e15a9b0f8 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Wed, 2 Apr 2025 12:42:10 -0700 Subject: [PATCH 3/7] apply discount vector to ITC when calculating LCOE/H/C --- src/geophires_x/Economics.py | 11 ++++++++--- tests/examples/Fervo_Project_Cape-3.out | 6 +++--- tests/examples/example_ITC.out | 6 +++--- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 5e78d8a6d..93cb1d289 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -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) @@ -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) @@ -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) diff --git a/tests/examples/Fervo_Project_Cape-3.out b/tests/examples/Fervo_Project_Cape-3.out index 679e3a434..b2e9faf4a 100644 --- a/tests/examples/Fervo_Project_Cape-3.out +++ b/tests/examples/Fervo_Project_Cape-3.out @@ -6,14 +6,14 @@ Simulation Metadata ---------------------- GEOPHIRES Version: 3.8.9 Simulation Date: 2025-04-02 - Simulation Time: 12:28 - Calculation Time: 0.853 sec + 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: 3.59 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 diff --git a/tests/examples/example_ITC.out b/tests/examples/example_ITC.out index ee3cbe06a..65bb8a792 100644 --- a/tests/examples/example_ITC.out +++ b/tests/examples/example_ITC.out @@ -6,14 +6,14 @@ Simulation Metadata ---------------------- GEOPHIRES Version: 3.8.9 Simulation Date: 2025-04-02 - Simulation Time: 12:28 - Calculation Time: 0.783 sec + 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: 4.44 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 From 079bb6bb4bd45e4442d456dbf2515f0139490214 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Wed, 2 Apr 2025 12:43:11 -0700 Subject: [PATCH 4/7] remove commented code --- src/geophires_x/Economics.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 93cb1d289..8257b12a1 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -2692,11 +2692,6 @@ def Calculate(self, model: Model) -> None: else: self.CCap.value = self.totalcapcost.value - # # update the capital costs, assuming the entire ITC is used to reduce the capital 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 From ab97004d16d1c6fd07755d1b7afdbc2dc9ebed8b Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Wed, 2 Apr 2025 12:56:57 -0700 Subject: [PATCH 5/7] Fix LCOH calculation bug for Heat Pump + BICYCLE --- src/geophires_x/Economics.py | 2 +- tests/test_geophires_x.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 8257b12a1..d7e258255 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -540,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 diff --git a/tests/test_geophires_x.py b/tests/test_geophires_x.py index d26dd6408..2d4dda1b7 100644 --- a/tests/test_geophires_x.py +++ b/tests/test_geophires_x.py @@ -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. From 1467a88b46fd940097679bdec7ad16ddecdce140 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Wed, 2 Apr 2025 13:03:15 -0700 Subject: [PATCH 6/7] update discount initial year cashflow test, since ITC period fix affects NPV --- tests/test_geophires_x.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_geophires_x.py b/tests/test_geophires_x.py index 2d4dda1b7..9e81cbb51 100644 --- a/tests/test_geophires_x.py +++ b/tests/test_geophires_x.py @@ -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'] From 47bea34719433851414208e7a47cd21484dd2bcf Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 3 Apr 2025 09:59:40 -0700 Subject: [PATCH 7/7] ITC tooltip link to https://programs.dsireusa.org/system/program/detail/658 --- src/geophires_x/Economics.py | 3 ++- src/geophires_x_schema_generator/geophires-request.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index d7e258255..ad42a0322 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -979,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", diff --git a/src/geophires_x_schema_generator/geophires-request.json b/src/geophires_x_schema_generator/geophires-request.json index 04338a7ef..8bd77f3c2 100644 --- a/src/geophires_x_schema_generator/geophires-request.json +++ b/src/geophires_x_schema_generator/geophires-request.json @@ -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",