From b74859f6c943a24bf578017c478b3d88436f8eb0 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 7 Mar 2025 15:22:41 -0800 Subject: [PATCH 01/16] numpy-financial NPV test in test_economics --- tests/geophires_x_tests/test_economics.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/geophires_x_tests/test_economics.py b/tests/geophires_x_tests/test_economics.py index 7fdc95862..a09984bdc 100644 --- a/tests/geophires_x_tests/test_economics.py +++ b/tests/geophires_x_tests/test_economics.py @@ -2,8 +2,11 @@ import sys from pathlib import Path -from geophires_x.Economics import CalculateFinancialPerformance +import numpy_financial as npf + +# ruff: noqa: I001 # Successful module initialization is dependent on this specific import order. from geophires_x.Model import Model +from geophires_x.Economics import CalculateFinancialPerformance from tests.base_test_case import BaseTestCase @@ -33,6 +36,19 @@ def calc_irr(total_revenue): self.assertAlmostEqual(6.21, calc_irr([-100, 100, 0, 7]), places=2) self.assertAlmostEqual(8.86, calc_irr([-5, 10.5, 1, -8, 1]), places=2) + def test_numpy_financial_npv(self): + # https://www.nrel.gov/docs/legosti/old/5173.pdf, p. 41 + rate = 0.12 + cashflow_series = [-10000, 7274, 6558, 6223, 6087, 6259] + npv = npf.npv(rate, cashflow_series) + self.assertEqual(13572, round(npv)) + + # https://support.microsoft.com/en-us/office/npv-function-8672cb67-2576-4d07-b67b-ac28acf2a568 + rate = 0.1 + cashflow_series = [-10000, 3000, 4200, 6800] + excel_npv = npf.npv(rate, [0, *cashflow_series]) + self.assertEqual(1188.44, round(excel_npv, 2)) + def test_well_drilling_cost_correlation_tooltiptext(self): ec = self._new_model().economics self.assertEqual( From c3fd03d77f928062ecbdfd24ae87e60f4cc62cae Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 7 Mar 2025 15:39:55 -0800 Subject: [PATCH 02/16] Add Cashflow Series Start Year parameter to enable NPV calculation parity with Excel/Google Sheets/etc. --- src/geophires_x/Economics.py | 46 +++++++++++++++++--- tests/geophires_x_tests/test_economics.py | 51 +++++++++++++++++------ tests/test_geophires_x.py | 19 +++++++++ 3 files changed, 98 insertions(+), 18 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 5085a38d2..3479adfff 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -309,7 +309,8 @@ def CalculateFinancialPerformance(plantlifetime: int, TotalRevenue: list, TotalCummRevenue: list, CAPEX: float, - OPEX: float): + OPEX: float, + cashflow_series_start_year: float = 0.): """ CalculateFinancialPerformance calculates the financial performance of the project. It is used to calculate the financial performance of the project. It is used to calculate the revenue stream for the project. @@ -325,6 +326,9 @@ def CalculateFinancialPerformance(plantlifetime: int, :type CAPEX: float :param OPEX: The total annual operating cost of the project in MUSD :type OPEX: float + :param cashflow_series_start_year: The starting year of the cashflow series used to calculate NPV + :type cashflow_series_start_year: float + :return: NPV: The net present value of the project in MUSD :rtype: float :return: IRR: The internal rate of return of the project in % @@ -336,8 +340,19 @@ def CalculateFinancialPerformance(plantlifetime: int, :rtype: tuple """ # Calculate financial performance values using numpy financials - NPV = npf.npv(FixedInternalRate / 100, TotalRevenue) - IRR = npf.irr(TotalRevenue) + + cashflow_series = TotalRevenue.copy() + + if cashflow_series_start_year not in [0., 1.]: + param_name = 'Cashflow Series Start Year' # TODO reference name defined in parameter dict + raise NotImplementedError(f'Unsupported value for {param_name}: {cashflow_series_start_year}') + + + if cashflow_series_start_year == 1.: + cashflow_series = [0, *cashflow_series] + + NPV = npf.npv(FixedInternalRate / 100, cashflow_series) + IRR = npf.irr(cashflow_series) if math.isnan(IRR): IRR = 0.0 else: @@ -859,6 +874,19 @@ def __init__(self, model: Model): "Discount Rate is synonymous with Fixed Internal Rate. If one is provided, the other's value " "will be automatically set to the same value." ) + + # TODO add support for float values + self.cashflow_series_start_year = self.ParameterDict[self.discountrate.Name] = intParameter( + "Cashflow Series Start Year", + DefaultValue=0, + AllowableRange=[0,1], + UnitType=Units.NONE, + ErrMessage=f'assume default Cashflow Series Start Year ({0})', + ToolTipText="Cashflow Series Start Year used to calculate NPV" + # "in the Standard Levelized Cost Model." # (?) + # TODO documentation re: values/conventions + ) + self.FIB = self.ParameterDict[self.FIB.Name] = floatParameter( "Fraction of Investment in Bonds", DefaultValue=0.5, @@ -2881,9 +2909,15 @@ def Calculate(self, model: Model) -> None: # Calculate more financial values using numpy financials self.ProjectNPV.value, self.ProjectIRR.value, self.ProjectVIR.value, self.ProjectMOIC.value = \ - CalculateFinancialPerformance(model.surfaceplant.plant_lifetime.value, self.FixedInternalRate.value, - self.TotalRevenue.value, self.TotalCummRevenue.value, self.CCap.value, - self.Coam.value) + CalculateFinancialPerformance( + model.surfaceplant.plant_lifetime.value, + self.FixedInternalRate.value, + self.TotalRevenue.value, + self.TotalCummRevenue.value, + self.CCap.value, + self.Coam.value, + self.cashflow_series_start_year.value + ) # Calculate the project payback period self.ProjectPaybackPeriod.value = 0.0 # start by assuming the project never pays back diff --git a/tests/geophires_x_tests/test_economics.py b/tests/geophires_x_tests/test_economics.py index a09984bdc..2757ad408 100644 --- a/tests/geophires_x_tests/test_economics.py +++ b/tests/geophires_x_tests/test_economics.py @@ -11,21 +11,22 @@ class EconomicsTestCase(BaseTestCase): + @staticmethod + def cumm_revenue(total_revenue): + cumm_revenue = [total_revenue[0]] * len(total_revenue) + cumm_revenue[1] = total_revenue[1] + for i in range(2, len(total_revenue)): + cumm_revenue[i] = cumm_revenue[i - 1] + total_revenue[i] + return cumm_revenue + def test_irr(self): """ Test cases adapted from https://numpy.org/numpy-financial/latest/irr.html """ - def cumm_revenue(total_revenue): - cumm_revenue = [total_revenue[0]] * len(total_revenue) - cumm_revenue[1] = total_revenue[1] - for i in range(2, len(total_revenue)): - cumm_revenue[i] = cumm_revenue[i - 1] + total_revenue[i] - return cumm_revenue - def calc_irr(total_revenue): NPV, IRR, VIR, MOIC = CalculateFinancialPerformance( - 30, 5, total_revenue, cumm_revenue(total_revenue), 1000, 10 + 30, 5, total_revenue, EconomicsTestCase.cumm_revenue(total_revenue), 1000, 10 ) return IRR @@ -36,12 +37,35 @@ def calc_irr(total_revenue): self.assertAlmostEqual(6.21, calc_irr([-100, 100, 0, 7]), places=2) self.assertAlmostEqual(8.86, calc_irr([-5, 10.5, 1, -8, 1]), places=2) - def test_numpy_financial_npv(self): - # https://www.nrel.gov/docs/legosti/old/5173.pdf, p. 41 + def test_npv(self): + """ + Includes sanity checks that numpy-financial.npv used by CalculateFinancialPerformance + matches reference calculations + """ + rate = 0.12 + + def calc_npv(total_revenue, cashflow_series_start_year=0): + NPV, IRR, VIR, MOIC = CalculateFinancialPerformance( + len(total_revenue) + 1, + rate * 100, + total_revenue, + EconomicsTestCase.cumm_revenue(total_revenue), + total_revenue[0], + 10, + cashflow_series_start_year=cashflow_series_start_year, + ) + + return NPV + + # https://www.nrel.gov/docs/legosti/old/5173.pdf, p. 41 cashflow_series = [-10000, 7274, 6558, 6223, 6087, 6259] - npv = npf.npv(rate, cashflow_series) - self.assertEqual(13572, round(npv)) + + npf_npv = npf.npv(rate, cashflow_series) + self.assertEqual(13572, round(npf_npv)) + + geophires_npv = calc_npv(cashflow_series) + self.assertEqual(13572, round(geophires_npv)) # https://support.microsoft.com/en-us/office/npv-function-8672cb67-2576-4d07-b67b-ac28acf2a568 rate = 0.1 @@ -49,6 +73,9 @@ def test_numpy_financial_npv(self): excel_npv = npf.npv(rate, [0, *cashflow_series]) self.assertEqual(1188.44, round(excel_npv, 2)) + geophires_npv = calc_npv(cashflow_series, cashflow_series_start_year=1) + self.assertEqual(1188.44, round(geophires_npv, 2)) + def test_well_drilling_cost_correlation_tooltiptext(self): ec = self._new_model().economics self.assertEqual( diff --git a/tests/test_geophires_x.py b/tests/test_geophires_x.py index bc9c50730..c455c36f1 100644 --- a/tests/test_geophires_x.py +++ b/tests/test_geophires_x.py @@ -590,6 +590,25 @@ def assertHasLogRecordWithMessage(logs_, message): logs2, 'Set Discount Rate to 0.042 because Fixed Internal Rate was provided (4.2 percent)' ) + def test_cashflow_series_start_year(self): + def _get_result(series_start_year: int) -> GeophiresXResult: + return GeophiresXClient().get_geophires_result( + GeophiresInputParameters( + # TODO switch over to generic EGS case to avoid thrash from example updates + # from_file_path=self._get_test_file_path('geophires_x_tests/generic-egs-case.txt'), + from_file_path=self._get_test_file_path('examples/Fervo_Project_Cape-3.txt'), + params={ + 'Cashflow Series Start Year': series_start_year, + }, + ) + ) + + def _npv(r: GeophiresXResult) -> dict: + return r.result['ECONOMIC PARAMETERS']['Project NPV']['value'] + + self.assertEqual(4561.96, _npv(_get_result(0))) + self.assertEqual(4263.51, _npv(_get_result(1))) + def test_transmission_pipeline_cost(self): result = GeophiresXClient().get_geophires_result( GeophiresInputParameters( From ae7a8f0a62d017f5023919fa796045200f045f9f Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 7 Mar 2025 15:58:31 -0800 Subject: [PATCH 03/16] fix incorrect parameter dict declaration --- src/geophires_x/Economics.py | 4 ++-- tests/test_geophires_x.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 3479adfff..3eec70e6f 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -876,10 +876,10 @@ def __init__(self, model: Model): ) # TODO add support for float values - self.cashflow_series_start_year = self.ParameterDict[self.discountrate.Name] = intParameter( + self.cashflow_series_start_year = self.ParameterDict[self.cashflow_series_start_year.Name] = intParameter( "Cashflow Series Start Year", DefaultValue=0, - AllowableRange=[0,1], + AllowableRange=[0, 1], UnitType=Units.NONE, ErrMessage=f'assume default Cashflow Series Start Year ({0})', ToolTipText="Cashflow Series Start Year used to calculate NPV" diff --git a/tests/test_geophires_x.py b/tests/test_geophires_x.py index c455c36f1..b53b7409d 100644 --- a/tests/test_geophires_x.py +++ b/tests/test_geophires_x.py @@ -572,9 +572,9 @@ def assertHasLogRecordWithMessage(logs_, message): with self.assertLogs(level='INFO') as logs: result = client.get_geophires_result(input_params(discount_rate='0.042')) - assert result is not None - assert result.result['ECONOMIC PARAMETERS']['Interest Rate']['value'] == 4.2 - assert result.result['ECONOMIC PARAMETERS']['Interest Rate']['unit'] == '%' + self.assertIsNotNone(result) + self.assertEqual(4.2, result.result['ECONOMIC PARAMETERS']['Interest Rate']['value']) + self.assertEqual('%', result.result['ECONOMIC PARAMETERS']['Interest Rate']['unit']) assertHasLogRecordWithMessage( logs, 'Set Fixed Internal Rate to 4.2 percent because Discount Rate was provided (0.042)' ) @@ -582,9 +582,9 @@ def assertHasLogRecordWithMessage(logs_, message): with self.assertLogs(level='INFO') as logs2: result2 = client.get_geophires_result(input_params(fixed_internal_rate='4.2')) - assert result2 is not None - assert result2.result['ECONOMIC PARAMETERS']['Interest Rate']['value'] == 4.2 - assert result2.result['ECONOMIC PARAMETERS']['Interest Rate']['unit'] == '%' + self.assertIsNotNone(result2) + self.assertEqual(4.2, result2.result['ECONOMIC PARAMETERS']['Interest Rate']['value']) + self.assertEqual('%', result2.result['ECONOMIC PARAMETERS']['Interest Rate']['unit']) assertHasLogRecordWithMessage( logs2, 'Set Discount Rate to 0.042 because Fixed Internal Rate was provided (4.2 percent)' From 7213230b719d2e738788c01a0dadf5e66bed265f Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Wed, 12 Mar 2025 11:10:50 -0700 Subject: [PATCH 04/16] sync cashflow series start year test npv test values --- 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 b53b7409d..7790543ce 100644 --- a/tests/test_geophires_x.py +++ b/tests/test_geophires_x.py @@ -606,8 +606,8 @@ def _get_result(series_start_year: int) -> GeophiresXResult: def _npv(r: GeophiresXResult) -> dict: return r.result['ECONOMIC PARAMETERS']['Project NPV']['value'] - self.assertEqual(4561.96, _npv(_get_result(0))) - self.assertEqual(4263.51, _npv(_get_result(1))) + self.assertEqual(4580.36, _npv(_get_result(0))) + self.assertEqual(4280.71, _npv(_get_result(1))) def test_transmission_pipeline_cost(self): result = GeophiresXClient().get_geophires_result( From 467ef077dbe13d6b42da029fd377c9cf3bbb40db Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Wed, 12 Mar 2025 11:52:18 -0700 Subject: [PATCH 05/16] Switch from integer 'Cashflow Series Start Year' to boolean 'Discount Initial Year Cashflow' to match Excel's (simpler) convention, which does not treat cash outlays spread over multiple initial years differently. --- src/geophires_x/Economics.py | 39 ++++++++++++----------- src/geophires_x/Parameter.py | 2 +- tests/geophires_x_tests/test_economics.py | 6 ++-- tests/test_geophires_x.py | 10 +++--- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 3eec70e6f..fe8db10e7 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -310,7 +310,7 @@ def CalculateFinancialPerformance(plantlifetime: int, TotalCummRevenue: list, CAPEX: float, OPEX: float, - cashflow_series_start_year: float = 0.): + discount_initial_year_cashflow: bool = False): """ CalculateFinancialPerformance calculates the financial performance of the project. It is used to calculate the financial performance of the project. It is used to calculate the revenue stream for the project. @@ -326,8 +326,8 @@ def CalculateFinancialPerformance(plantlifetime: int, :type CAPEX: float :param OPEX: The total annual operating cost of the project in MUSD :type OPEX: float - :param cashflow_series_start_year: The starting year of the cashflow series used to calculate NPV - :type cashflow_series_start_year: float + :param discount_initial_year_cashflow: Whether to discount the initial year of cashflow used to calculate NPV + :type discount_initial_year_cashflow: bool :return: NPV: The net present value of the project in MUSD :rtype: float @@ -343,12 +343,7 @@ def CalculateFinancialPerformance(plantlifetime: int, cashflow_series = TotalRevenue.copy() - if cashflow_series_start_year not in [0., 1.]: - param_name = 'Cashflow Series Start Year' # TODO reference name defined in parameter dict - raise NotImplementedError(f'Unsupported value for {param_name}: {cashflow_series_start_year}') - - - if cashflow_series_start_year == 1.: + if discount_initial_year_cashflow: cashflow_series = [0, *cashflow_series] NPV = npf.npv(FixedInternalRate / 100, cashflow_series) @@ -875,16 +870,22 @@ def __init__(self, model: Model): "will be automatically set to the same value." ) - # TODO add support for float values - self.cashflow_series_start_year = self.ParameterDict[self.cashflow_series_start_year.Name] = intParameter( - "Cashflow Series Start Year", - DefaultValue=0, - AllowableRange=[0, 1], + + self.discount_initial_year_cashflow = self.ParameterDict[self.discount_initial_year_cashflow.Name] = boolParameter( + 'Discount Initial Year Cashflow', + DefaultValue=False, UnitType=Units.NONE, - ErrMessage=f'assume default Cashflow Series Start Year ({0})', - ToolTipText="Cashflow Series Start Year used to calculate NPV" - # "in the Standard Levelized Cost Model." # (?) - # TODO documentation re: values/conventions + ToolTipText='Whether to discount cashflow in the initial project year when calculating NPV. ' + 'The default value of False conforms to NREL\'s standard convention for NPV calculation ' + '(Short W et al, 1995. https://www.nrel.gov/docs/legosti/old/5173.pdf). ' + 'A value of True will, by contrast, cause NPV calculation to follow the convention used by ' + 'Excel, Google Sheets, and other common spreadsheet software. ' + 'Although NREL\'s NPV convention may typically be considered more technically correct ' + 'from a financial mathematics perspective, ' + 'Excel-style NPV calculation may be preferred by some for familiarity, ' + 'compliance with de-facto organizational standards, or comparison of ' + 'GEOPHIRES with independent financial calculations. ' + 'See https://github.com/NREL/GEOPHIRES-X/discussions/344 for discussion and further details.' ) self.FIB = self.ParameterDict[self.FIB.Name] = floatParameter( @@ -2916,7 +2917,7 @@ def Calculate(self, model: Model) -> None: self.TotalCummRevenue.value, self.CCap.value, self.Coam.value, - self.cashflow_series_start_year.value + self.discount_initial_year_cashflow.value ) # Calculate the project payback period diff --git a/src/geophires_x/Parameter.py b/src/geophires_x/Parameter.py index 086f2f429..3e43dbba2 100644 --- a/src/geophires_x/Parameter.py +++ b/src/geophires_x/Parameter.py @@ -144,7 +144,7 @@ class boolParameter(Parameter): def __post_init__(self): if self.value is None: - self.value:bool = self.DefaultValue + self.value: bool = self.DefaultValue value: bool = None DefaultValue: bool = value diff --git a/tests/geophires_x_tests/test_economics.py b/tests/geophires_x_tests/test_economics.py index 2757ad408..4e0dc6a15 100644 --- a/tests/geophires_x_tests/test_economics.py +++ b/tests/geophires_x_tests/test_economics.py @@ -45,7 +45,7 @@ def test_npv(self): rate = 0.12 - def calc_npv(total_revenue, cashflow_series_start_year=0): + def calc_npv(total_revenue, discount_initial_year_cashflow=False): NPV, IRR, VIR, MOIC = CalculateFinancialPerformance( len(total_revenue) + 1, rate * 100, @@ -53,7 +53,7 @@ def calc_npv(total_revenue, cashflow_series_start_year=0): EconomicsTestCase.cumm_revenue(total_revenue), total_revenue[0], 10, - cashflow_series_start_year=cashflow_series_start_year, + discount_initial_year_cashflow=discount_initial_year_cashflow, ) return NPV @@ -73,7 +73,7 @@ def calc_npv(total_revenue, cashflow_series_start_year=0): excel_npv = npf.npv(rate, [0, *cashflow_series]) self.assertEqual(1188.44, round(excel_npv, 2)) - geophires_npv = calc_npv(cashflow_series, cashflow_series_start_year=1) + geophires_npv = calc_npv(cashflow_series, discount_initial_year_cashflow=True) self.assertEqual(1188.44, round(geophires_npv, 2)) def test_well_drilling_cost_correlation_tooltiptext(self): diff --git a/tests/test_geophires_x.py b/tests/test_geophires_x.py index 7790543ce..a452950ad 100644 --- a/tests/test_geophires_x.py +++ b/tests/test_geophires_x.py @@ -590,15 +590,15 @@ def assertHasLogRecordWithMessage(logs_, message): logs2, 'Set Discount Rate to 0.042 because Fixed Internal Rate was provided (4.2 percent)' ) - def test_cashflow_series_start_year(self): - def _get_result(series_start_year: int) -> GeophiresXResult: + def test_discount_initial_year_cashflow(self): + def _get_result(do_discount: bool) -> GeophiresXResult: return GeophiresXClient().get_geophires_result( GeophiresInputParameters( # TODO switch over to generic EGS case to avoid thrash from example updates # from_file_path=self._get_test_file_path('geophires_x_tests/generic-egs-case.txt'), from_file_path=self._get_test_file_path('examples/Fervo_Project_Cape-3.txt'), params={ - 'Cashflow Series Start Year': series_start_year, + 'Discount Initial Year Cashflow': do_discount, }, ) ) @@ -606,8 +606,8 @@ def _get_result(series_start_year: int) -> GeophiresXResult: def _npv(r: GeophiresXResult) -> dict: return r.result['ECONOMIC PARAMETERS']['Project NPV']['value'] - self.assertEqual(4580.36, _npv(_get_result(0))) - self.assertEqual(4280.71, _npv(_get_result(1))) + self.assertEqual(4580.36, _npv(_get_result(False))) + self.assertEqual(4280.71, _npv(_get_result(True))) def test_transmission_pipeline_cost(self): result = GeophiresXClient().get_geophires_result( From 37a59c20044c63f9ae843694b77f33c975172453 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Wed, 12 Mar 2025 12:02:49 -0700 Subject: [PATCH 06/16] regenerate schema with Discount Initial Year Cashflow --- src/geophires_x_schema_generator/geophires-request.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/geophires_x_schema_generator/geophires-request.json b/src/geophires_x_schema_generator/geophires-request.json index 128984c8b..67c563083 100644 --- a/src/geophires_x_schema_generator/geophires-request.json +++ b/src/geophires_x_schema_generator/geophires-request.json @@ -1580,6 +1580,15 @@ "minimum": 0.0, "maximum": 1.0 }, + "Discount Initial Year Cashflow": { + "description": "Whether to discount cashflow in the initial project year when calculating NPV. The default value of False conforms to NREL's standard convention for NPV calculation (Short W et al, 1995. https://www.nrel.gov/docs/legosti/old/5173.pdf). A value of True will, by contrast, cause NPV calculation to follow the convention used by Excel, Google Sheets, and other common spreadsheet software. Although NREL's NPV convention may typically be considered more technically correct from a financial mathematics perspective, Excel-style NPV calculation may be preferred by some for familiarity, compliance with de-facto organizational standards, or comparison of GEOPHIRES with independent financial calculations. See https://github.com/NREL/GEOPHIRES-X/discussions/344 for discussion and further details.", + "type": "boolean", + "units": null, + "category": "Economics", + "default": false, + "minimum": null, + "maximum": null + }, "Fraction of Investment in Bonds": { "description": "Fraction of geothermal project financing through bonds (see docs)", "type": "number", From cb1e0c6788bce29a72e62da575d5f68c0f698225 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Wed, 12 Mar 2025 12:03:14 -0700 Subject: [PATCH 07/16] =?UTF-8?q?Bump=20version:=203.8.0=20=E2=86=92=203.8?= =?UTF-8?q?.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- .cookiecutterrc | 2 +- README.rst | 4 ++-- docs/conf.py | 2 +- setup.py | 2 +- src/geophires_x/__init__.py | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index da8012bee..8e622db46 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.8.0 +current_version = 3.8.1 commit = True tag = True diff --git a/.cookiecutterrc b/.cookiecutterrc index a84486cfa..f8d460e2f 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -54,7 +54,7 @@ default_context: sphinx_doctest: "no" sphinx_theme: "sphinx-py3doc-enhanced-theme" test_matrix_separate_coverage: "no" - version: 3.8.0 + version: 3.8.1 version_manager: "bump2version" website: "https://github.com/NREL" year_from: "2023" diff --git a/README.rst b/README.rst index a92972b73..c96582226 100644 --- a/README.rst +++ b/README.rst @@ -56,9 +56,9 @@ Free software: `MIT license `__ :alt: Supported implementations :target: https://pypi.org/project/geophires-x -.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.8.0.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.8.1.svg :alt: Commits since latest release - :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.8.0...main + :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.8.1...main .. |docs| image:: https://readthedocs.org/projects/GEOPHIRES-X/badge/?style=flat :target: https://nrel.github.io/GEOPHIRES-X diff --git a/docs/conf.py b/docs/conf.py index f3e0c0d94..88a2a840e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,7 +18,7 @@ year = '2025' author = 'NREL' copyright = f'{year}, {author}' -version = release = '3.8.0' +version = release = '3.8.1' pygments_style = 'trac' templates_path = ['./templates'] diff --git a/setup.py b/setup.py index 2e3bf8218..f1a55c01b 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def read(*names, **kwargs): setup( name='geophires-x', - version='3.8.0', + version='3.8.1', license='MIT', description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.', long_description='{}\n{}'.format( diff --git a/src/geophires_x/__init__.py b/src/geophires_x/__init__.py index 99d6aecee..64e0fe00b 100644 --- a/src/geophires_x/__init__.py +++ b/src/geophires_x/__init__.py @@ -1 +1 @@ -__version__ = '3.8.0' +__version__ = '3.8.1' From b206bb5f45cca77661ae65e0694b0c029132e5e1 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 13 Mar 2025 08:18:39 -0700 Subject: [PATCH 08/16] tweak Discount Initial Year Cashflow tooltip --- src/geophires_x/Economics.py | 11 +++++------ .../geophires-request.json | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index fe8db10e7..accaa7c31 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -875,16 +875,15 @@ def __init__(self, model: Model): 'Discount Initial Year Cashflow', DefaultValue=False, UnitType=Units.NONE, - ToolTipText='Whether to discount cashflow in the initial project year when calculating NPV. ' + ToolTipText='Whether to discount cashflow in the initial project year when calculating NPV ' + '(Net Present Value). ' 'The default value of False conforms to NREL\'s standard convention for NPV calculation ' '(Short W et al, 1995. https://www.nrel.gov/docs/legosti/old/5173.pdf). ' 'A value of True will, by contrast, cause NPV calculation to follow the convention used by ' 'Excel, Google Sheets, and other common spreadsheet software. ' - 'Although NREL\'s NPV convention may typically be considered more technically correct ' - 'from a financial mathematics perspective, ' - 'Excel-style NPV calculation may be preferred by some for familiarity, ' - 'compliance with de-facto organizational standards, or comparison of ' - 'GEOPHIRES with independent financial calculations. ' + 'Although NREL\'s NPV convention may typically be considered more technically correct, ' + 'Excel-style NPV calculation may be preferred by some for familiarity ' + 'or compatibility with existing business processes.' 'See https://github.com/NREL/GEOPHIRES-X/discussions/344 for discussion and further details.' ) diff --git a/src/geophires_x_schema_generator/geophires-request.json b/src/geophires_x_schema_generator/geophires-request.json index 67c563083..8b9c4acf3 100644 --- a/src/geophires_x_schema_generator/geophires-request.json +++ b/src/geophires_x_schema_generator/geophires-request.json @@ -1581,7 +1581,7 @@ "maximum": 1.0 }, "Discount Initial Year Cashflow": { - "description": "Whether to discount cashflow in the initial project year when calculating NPV. The default value of False conforms to NREL's standard convention for NPV calculation (Short W et al, 1995. https://www.nrel.gov/docs/legosti/old/5173.pdf). A value of True will, by contrast, cause NPV calculation to follow the convention used by Excel, Google Sheets, and other common spreadsheet software. Although NREL's NPV convention may typically be considered more technically correct from a financial mathematics perspective, Excel-style NPV calculation may be preferred by some for familiarity, compliance with de-facto organizational standards, or comparison of GEOPHIRES with independent financial calculations. See https://github.com/NREL/GEOPHIRES-X/discussions/344 for discussion and further details.", + "description": "Whether to discount cashflow in the initial project year when calculating NPV (Net Present Value). The default value of False conforms to NREL's standard convention for NPV calculation (Short W et al, 1995. https://www.nrel.gov/docs/legosti/old/5173.pdf). A value of True will, by contrast, cause NPV calculation to follow the convention used by Excel, Google Sheets, and other common spreadsheet software. Although NREL's NPV convention may typically be considered more technically correct, Excel-style NPV calculation may be preferred by some for familiarity or compatibility with existing business processes.See https://github.com/NREL/GEOPHIRES-X/discussions/344 for discussion and further details.", "type": "boolean", "units": null, "category": "Economics", From 00f937cf559a63d45e59781490d0391f10cb1676 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 13 Mar 2025 08:39:38 -0700 Subject: [PATCH 09/16] honor discount initial year cashflow in add-ons economics --- src/geophires_x/Economics.py | 25 ++++++++++++++++++------- src/geophires_x/EconomicsAddOns.py | 7 ++++++- src/geophires_x/OutputsAddOns.py | 2 +- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index accaa7c31..c15774f7c 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -304,6 +304,22 @@ def CalculateCarbonRevenue(model, plant_lifetime: int, construction_years: int, return cash_flow_musd, cumm_cash_flow_musd, carbon_that_would_have_been_produced_annually_lbs, carbon_that_would_have_been_produced_total_lbs +def calculate_npv( + discount_rate_tenths: float, + cashflow_series: list[float], + discount_initial_year_cashflow: bool +) -> float: + # TODO warn/raise exception if discount rate > 1 (i.e. it's probably not converted from percent to tenths) + + npv_cashflow_series = cashflow_series.copy() # Copy to guard against unintentional mutation of consumer field + + if discount_initial_year_cashflow: + # Enable Excel-style NPV calculation - see https://github.com/NREL/GEOPHIRES-X/discussions/344 + npv_cashflow_series = [0, *npv_cashflow_series] + + return npf.npv(discount_rate_tenths, npv_cashflow_series) + + def CalculateFinancialPerformance(plantlifetime: int, FixedInternalRate: float, TotalRevenue: list, @@ -341,13 +357,8 @@ def CalculateFinancialPerformance(plantlifetime: int, """ # Calculate financial performance values using numpy financials - cashflow_series = TotalRevenue.copy() - - if discount_initial_year_cashflow: - cashflow_series = [0, *cashflow_series] - - NPV = npf.npv(FixedInternalRate / 100, cashflow_series) - IRR = npf.irr(cashflow_series) + NPV = calculate_npv(FixedInternalRate / 100, TotalRevenue.copy(), discount_initial_year_cashflow) + IRR = npf.irr(TotalRevenue) if math.isnan(IRR): IRR = 0.0 else: diff --git a/src/geophires_x/EconomicsAddOns.py b/src/geophires_x/EconomicsAddOns.py index 2fc7df89f..6a047f557 100644 --- a/src/geophires_x/EconomicsAddOns.py +++ b/src/geophires_x/EconomicsAddOns.py @@ -347,7 +347,12 @@ def Calculate(self, model: Model) -> None: # Now calculate a new "NPV", "IRR", "VIR", "Payback Period", and "MOIC" # Calculate more financial values using numpy financials - self.ProjectNPV.value = npf.npv(self.FixedInternalRate.value / 100, self.ProjectCashFlow.value) + self.ProjectNPV.value = Economics.calculate_npv( + self.FixedInternalRate.value / 100, + self.ProjectCashFlow.value.copy(), + self.discount_initial_year_cashflow.value + ) + self.ProjectIRR.value = npf.irr(self.ProjectCashFlow.value) if math.isnan(self.ProjectIRR.value): self.ProjectIRR.value = 0.0 diff --git a/src/geophires_x/OutputsAddOns.py b/src/geophires_x/OutputsAddOns.py index bab710f89..cf9e9f234 100644 --- a/src/geophires_x/OutputsAddOns.py +++ b/src/geophires_x/OutputsAddOns.py @@ -41,7 +41,7 @@ def PrintOutputs(self, model) -> tuple: addon_results.append(OutputTableItem('Adjusted Project CAPEX (after incentives, grants, AddOns, etc)', '{0:10.2f}'.format(model.addeconomics.AdjustedProjectCAPEX.value), model.addeconomics.AdjustedProjectCAPEX.PreferredUnits.value)) f.write(f" Adjusted Project OPEX (after incentives, grants, AddOns, etc): {model.addeconomics.AdjustedProjectOPEX.value:10.2f} " + model.addeconomics.AdjustedProjectOPEX.PreferredUnits.value + NL) addon_results.append(OutputTableItem('Adjusted Project OPEX (after incentives, grants, AddOns, etc)', '{0:10.2f}'.format(model.addeconomics.AdjustedProjectOPEX.value), model.addeconomics.AdjustedProjectOPEX.PreferredUnits.value)) - f.write(f" Project NPV (including AddOns): {model.addeconomics.ProjectNPV.value:10.2f} " + model.addeconomics.ProjectNPV.PreferredUnits.value + NL) + f.write(f' Project NPV (including AddOns): {model.addeconomics.ProjectNPV.value:10.2f} {model.addeconomics.ProjectNPV.PreferredUnits.value}\n') addon_results.append(OutputTableItem('Project NPV (including AddOns)', '{0:10.2f}'.format(model.addeconomics.ProjectNPV.value), model.addeconomics.ProjectNPV.PreferredUnits.value)) f.write(f" Project IRR (including AddOns): {model.addeconomics.ProjectIRR.value:10.2f} " + model.addeconomics.ProjectIRR.PreferredUnits.value + NL) addon_results.append(OutputTableItem('Project IRR (including AddOns)', '{0:10.2f}'.format(model.addeconomics.ProjectIRR.value), model.addeconomics.ProjectIRR.PreferredUnits.value)) From ba94b45cd1938b7d9a8444c6d0e0a8df2a5cac0c Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 13 Mar 2025 09:21:47 -0700 Subject: [PATCH 10/16] unit test add-ons/extended economics npv with/without discount initial year --- src/geophires_x/Outputs.py | 7 ++++++- src/geophires_x/WellBores.py | 2 +- tests/test_geophires_x.py | 28 ++++++++++++++++++++++++---- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index e286dc8ea..a70bfd497 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -1643,7 +1643,12 @@ def PrintOutputs(self, model: Model): f.write(f' Accrued financing during construction: {model.economics.inflrateconstruction.value*100:10.2f} ' + model.economics.inflrateconstruction.CurrentUnits.value + NL) f.write(f' Project lifetime: {model.surfaceplant.plant_lifetime.value:10.0f} ' + model.surfaceplant.plant_lifetime.CurrentUnits.value + NL) f.write(f' Capacity factor: {model.surfaceplant.utilization_factor.value * 100:10.1f} %' + NL) - f.write(f' Project NPV: {model.economics.ProjectNPV.value:10.2f} ' + model.economics.ProjectNPV.PreferredUnits.value + NL) + + e_npv = model.economics.ProjectNPV + npv_field_label = Outputs._field_label('Project NPV', 49) + # TODO should use CurrentUnits instead of PreferredUnits + f.write(f' {npv_field_label}{e_npv.value:10.2f} {e_npv.PreferredUnits.value}\n') + f.write(f' Project IRR: {model.economics.ProjectIRR.value:10.2f} ' + model.economics.ProjectIRR.PreferredUnits.value + NL) f.write(f' Project VIR=PI=PIR: {model.economics.ProjectVIR.value:10.2f}' + NL) f.write(f' {model.economics.ProjectMOIC.Name}: {model.economics.ProjectMOIC.value:10.2f}' + NL) diff --git a/src/geophires_x/WellBores.py b/src/geophires_x/WellBores.py index 7c8fbd87a..7317ecf4f 100644 --- a/src/geophires_x/WellBores.py +++ b/src/geophires_x/WellBores.py @@ -537,7 +537,7 @@ def ProdPressureDropAndPumpingPowerUsingIndexes( pumpdepthfinal_m = np.max(pumpdepth_m) if pumpdepthfinal_m < 0.0: pumpdepthfinal_m = 0.0 - msg = (f'GEOPHIRES calculates negative production well pumping depth. ({pumpdepthfinal_m:.2f}m)' + msg = (f'GEOPHIRES calculates negative production well pumping depth. ({pumpdepthfinal_m:.2f}m). ' f'No production well pumps will be assumed') print(f'Warning: {msg}') model.logger.warning(msg) diff --git a/tests/test_geophires_x.py b/tests/test_geophires_x.py index a452950ad..964cae392 100644 --- a/tests/test_geophires_x.py +++ b/tests/test_geophires_x.py @@ -591,12 +591,12 @@ def assertHasLogRecordWithMessage(logs_, message): ) def test_discount_initial_year_cashflow(self): - def _get_result(do_discount: bool) -> GeophiresXResult: + def _get_result(base_example: str, do_discount: bool) -> GeophiresXResult: return GeophiresXClient().get_geophires_result( GeophiresInputParameters( # TODO switch over to generic EGS case to avoid thrash from example updates # from_file_path=self._get_test_file_path('geophires_x_tests/generic-egs-case.txt'), - from_file_path=self._get_test_file_path('examples/Fervo_Project_Cape-3.txt'), + from_file_path=self._get_test_file_path(f'examples/{base_example}.txt'), params={ 'Discount Initial Year Cashflow': do_discount, }, @@ -606,8 +606,28 @@ def _get_result(do_discount: bool) -> GeophiresXResult: def _npv(r: GeophiresXResult) -> dict: return r.result['ECONOMIC PARAMETERS']['Project NPV']['value'] - self.assertEqual(4580.36, _npv(_get_result(False))) - self.assertEqual(4280.71, _npv(_get_result(True))) + self.assertEqual(4580.36, _npv(_get_result('Fervo_Project_Cape-3', False))) + self.assertEqual(4280.71, _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'] + + add_ons_result_without_discount = _get_result('example1_addons', False) + add_ons_result_with_discount = _get_result('example1_addons', True) + + self.assertGreater(_npv(add_ons_result_without_discount), _npv(add_ons_result_with_discount)) + + ee_npv_without_discount = _extended_economics_npv(add_ons_result_without_discount) + assert ee_npv_without_discount < 0, ( + 'Test is expecting example1_addons extended economics NPV to be negative ' + 'as a precondition - if this error is encountered, ' + 'create a test-only copy of the previous version of example1_addons and ' + 'use it in this test (like geophires_x_tests/generic-egs-case.txt).' + ) + + # Discounting first year causes negative NPVs to be less negative (according to Google Sheets, + # which was used to manually validate the expected NPVs here). + self.assertLess(ee_npv_without_discount, _extended_economics_npv(add_ons_result_with_discount)) def test_transmission_pipeline_cost(self): result = GeophiresXClient().get_geophires_result( From 5571a2859c091933ef914cd2fcf3e367a3c1f238 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 13 Mar 2025 09:27:06 -0700 Subject: [PATCH 11/16] remove python 3.8-incompatible type annotation --- 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 c15774f7c..057302ca6 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -306,7 +306,7 @@ def CalculateCarbonRevenue(model, plant_lifetime: int, construction_years: int, def calculate_npv( discount_rate_tenths: float, - cashflow_series: list[float], + cashflow_series: list, discount_initial_year_cashflow: bool ) -> float: # TODO warn/raise exception if discount rate > 1 (i.e. it's probably not converted from percent to tenths) From bb325588d536d63456dd53d7cbb34cabeb15dbbf Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 13 Mar 2025 09:46:12 -0700 Subject: [PATCH 12/16] Missing space typo --- src/geophires_x/Economics.py | 2 +- src/geophires_x_schema_generator/geophires-request.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 057302ca6..901ff59e2 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -894,7 +894,7 @@ def __init__(self, model: Model): 'Excel, Google Sheets, and other common spreadsheet software. ' 'Although NREL\'s NPV convention may typically be considered more technically correct, ' 'Excel-style NPV calculation may be preferred by some for familiarity ' - 'or compatibility with existing business processes.' + 'or compatibility with existing business processes. ' 'See https://github.com/NREL/GEOPHIRES-X/discussions/344 for discussion and further details.' ) diff --git a/src/geophires_x_schema_generator/geophires-request.json b/src/geophires_x_schema_generator/geophires-request.json index 8b9c4acf3..571623232 100644 --- a/src/geophires_x_schema_generator/geophires-request.json +++ b/src/geophires_x_schema_generator/geophires-request.json @@ -1581,7 +1581,7 @@ "maximum": 1.0 }, "Discount Initial Year Cashflow": { - "description": "Whether to discount cashflow in the initial project year when calculating NPV (Net Present Value). The default value of False conforms to NREL's standard convention for NPV calculation (Short W et al, 1995. https://www.nrel.gov/docs/legosti/old/5173.pdf). A value of True will, by contrast, cause NPV calculation to follow the convention used by Excel, Google Sheets, and other common spreadsheet software. Although NREL's NPV convention may typically be considered more technically correct, Excel-style NPV calculation may be preferred by some for familiarity or compatibility with existing business processes.See https://github.com/NREL/GEOPHIRES-X/discussions/344 for discussion and further details.", + "description": "Whether to discount cashflow in the initial project year when calculating NPV (Net Present Value). The default value of False conforms to NREL's standard convention for NPV calculation (Short W et al, 1995. https://www.nrel.gov/docs/legosti/old/5173.pdf). A value of True will, by contrast, cause NPV calculation to follow the convention used by Excel, Google Sheets, and other common spreadsheet software. Although NREL's NPV convention may typically be considered more technically correct, Excel-style NPV calculation may be preferred by some for familiarity or compatibility with existing business processes. See https://github.com/NREL/GEOPHIRES-X/discussions/344 for discussion and further details.", "type": "boolean", "units": null, "category": "Economics", From 6f088ba0db4fae114cf525f2b0364007d96393f9 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Thu, 13 Mar 2025 10:20:23 -0700 Subject: [PATCH 13/16] =?UTF-8?q?Bump=20version:=203.8.1=20=E2=86=92=203.8?= =?UTF-8?q?.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- .cookiecutterrc | 2 +- README.rst | 4 ++-- docs/conf.py | 2 +- setup.py | 2 +- src/geophires_x/__init__.py | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 8e622db46..084cbefc8 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.8.1 +current_version = 3.8.2 commit = True tag = True diff --git a/.cookiecutterrc b/.cookiecutterrc index f8d460e2f..5ff3be294 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -54,7 +54,7 @@ default_context: sphinx_doctest: "no" sphinx_theme: "sphinx-py3doc-enhanced-theme" test_matrix_separate_coverage: "no" - version: 3.8.1 + version: 3.8.2 version_manager: "bump2version" website: "https://github.com/NREL" year_from: "2023" diff --git a/README.rst b/README.rst index c96582226..b515cf933 100644 --- a/README.rst +++ b/README.rst @@ -56,9 +56,9 @@ Free software: `MIT license `__ :alt: Supported implementations :target: https://pypi.org/project/geophires-x -.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.8.1.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.8.2.svg :alt: Commits since latest release - :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.8.1...main + :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.8.2...main .. |docs| image:: https://readthedocs.org/projects/GEOPHIRES-X/badge/?style=flat :target: https://nrel.github.io/GEOPHIRES-X diff --git a/docs/conf.py b/docs/conf.py index 88a2a840e..ea84ac730 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,7 +18,7 @@ year = '2025' author = 'NREL' copyright = f'{year}, {author}' -version = release = '3.8.1' +version = release = '3.8.2' pygments_style = 'trac' templates_path = ['./templates'] diff --git a/setup.py b/setup.py index f1a55c01b..37e63532a 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def read(*names, **kwargs): setup( name='geophires-x', - version='3.8.1', + version='3.8.2', license='MIT', description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.', long_description='{}\n{}'.format( diff --git a/src/geophires_x/__init__.py b/src/geophires_x/__init__.py index 64e0fe00b..5b2ac9951 100644 --- a/src/geophires_x/__init__.py +++ b/src/geophires_x/__init__.py @@ -1 +1 @@ -__version__ = '3.8.1' +__version__ = '3.8.2' From fa958f3572509350a067135dda8ef30e18e4c39b Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 14 Mar 2025 08:36:40 -0700 Subject: [PATCH 14/16] Tweak discount initial year cashflow documentation --- src/geophires_x/Economics.py | 4 ++-- src/geophires_x_schema_generator/geophires-request.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 901ff59e2..f8b92f0a3 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -893,9 +893,9 @@ def __init__(self, model: Model): 'A value of True will, by contrast, cause NPV calculation to follow the convention used by ' 'Excel, Google Sheets, and other common spreadsheet software. ' 'Although NREL\'s NPV convention may typically be considered more technically correct, ' - 'Excel-style NPV calculation may be preferred by some for familiarity ' + 'Excel-style NPV calculation might be preferred for familiarity ' 'or compatibility with existing business processes. ' - 'See https://github.com/NREL/GEOPHIRES-X/discussions/344 for discussion and further details.' + 'See https://github.com/NREL/GEOPHIRES-X/discussions/344 for further details.' ) self.FIB = self.ParameterDict[self.FIB.Name] = floatParameter( diff --git a/src/geophires_x_schema_generator/geophires-request.json b/src/geophires_x_schema_generator/geophires-request.json index 571623232..cb14fbb07 100644 --- a/src/geophires_x_schema_generator/geophires-request.json +++ b/src/geophires_x_schema_generator/geophires-request.json @@ -1581,7 +1581,7 @@ "maximum": 1.0 }, "Discount Initial Year Cashflow": { - "description": "Whether to discount cashflow in the initial project year when calculating NPV (Net Present Value). The default value of False conforms to NREL's standard convention for NPV calculation (Short W et al, 1995. https://www.nrel.gov/docs/legosti/old/5173.pdf). A value of True will, by contrast, cause NPV calculation to follow the convention used by Excel, Google Sheets, and other common spreadsheet software. Although NREL's NPV convention may typically be considered more technically correct, Excel-style NPV calculation may be preferred by some for familiarity or compatibility with existing business processes. See https://github.com/NREL/GEOPHIRES-X/discussions/344 for discussion and further details.", + "description": "Whether to discount cashflow in the initial project year when calculating NPV (Net Present Value). The default value of False conforms to NREL's standard convention for NPV calculation (Short W et al, 1995. https://www.nrel.gov/docs/legosti/old/5173.pdf). A value of True will, by contrast, cause NPV calculation to follow the convention used by Excel, Google Sheets, and other common spreadsheet software. Although NREL's NPV convention may typically be considered more technically correct, Excel-style NPV calculation might be preferred for familiarity or compatibility with existing business processes. See https://github.com/NREL/GEOPHIRES-X/discussions/344 for further details.", "type": "boolean", "units": null, "category": "Economics", From 3796088d17a3c52b2c13cebc618397de6d31e747 Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 14 Mar 2025 08:45:33 -0700 Subject: [PATCH 15/16] =?UTF-8?q?Bump=20version:=203.8.2=20=E2=86=92=203.8?= =?UTF-8?q?.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- .cookiecutterrc | 2 +- README.rst | 4 ++-- docs/conf.py | 2 +- setup.py | 2 +- src/geophires_x/__init__.py | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 084cbefc8..d87ee2d77 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.8.2 +current_version = 3.8.3 commit = True tag = True diff --git a/.cookiecutterrc b/.cookiecutterrc index 5ff3be294..74b3d046f 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -54,7 +54,7 @@ default_context: sphinx_doctest: "no" sphinx_theme: "sphinx-py3doc-enhanced-theme" test_matrix_separate_coverage: "no" - version: 3.8.2 + version: 3.8.3 version_manager: "bump2version" website: "https://github.com/NREL" year_from: "2023" diff --git a/README.rst b/README.rst index b515cf933..5d3e7b58b 100644 --- a/README.rst +++ b/README.rst @@ -56,9 +56,9 @@ Free software: `MIT license `__ :alt: Supported implementations :target: https://pypi.org/project/geophires-x -.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.8.2.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.8.3.svg :alt: Commits since latest release - :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.8.2...main + :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.8.3...main .. |docs| image:: https://readthedocs.org/projects/GEOPHIRES-X/badge/?style=flat :target: https://nrel.github.io/GEOPHIRES-X diff --git a/docs/conf.py b/docs/conf.py index ea84ac730..97db0e071 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,7 +18,7 @@ year = '2025' author = 'NREL' copyright = f'{year}, {author}' -version = release = '3.8.2' +version = release = '3.8.3' pygments_style = 'trac' templates_path = ['./templates'] diff --git a/setup.py b/setup.py index 37e63532a..9068cdf3d 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def read(*names, **kwargs): setup( name='geophires-x', - version='3.8.2', + version='3.8.3', license='MIT', description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.', long_description='{}\n{}'.format( diff --git a/src/geophires_x/__init__.py b/src/geophires_x/__init__.py index 5b2ac9951..20d4c1026 100644 --- a/src/geophires_x/__init__.py +++ b/src/geophires_x/__init__.py @@ -1 +1 @@ -__version__ = '3.8.2' +__version__ = '3.8.3' From 62af12e58c0121d7b2af2c217a3dae02d12a031d Mon Sep 17 00:00:00 2001 From: softwareengineerprogrammer <4056124+softwareengineerprogrammer@users.noreply.github.com> Date: Fri, 14 Mar 2025 08:45:57 -0700 Subject: [PATCH 16/16] =?UTF-8?q?Bump=20version:=203.8.3=20=E2=86=92=203.8?= =?UTF-8?q?.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- .cookiecutterrc | 2 +- README.rst | 4 ++-- docs/conf.py | 2 +- setup.py | 2 +- src/geophires_x/__init__.py | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index d87ee2d77..17cdc0cd5 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.8.3 +current_version = 3.8.4 commit = True tag = True diff --git a/.cookiecutterrc b/.cookiecutterrc index 74b3d046f..e71bc7bc2 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -54,7 +54,7 @@ default_context: sphinx_doctest: "no" sphinx_theme: "sphinx-py3doc-enhanced-theme" test_matrix_separate_coverage: "no" - version: 3.8.3 + version: 3.8.4 version_manager: "bump2version" website: "https://github.com/NREL" year_from: "2023" diff --git a/README.rst b/README.rst index 5d3e7b58b..5139b845f 100644 --- a/README.rst +++ b/README.rst @@ -56,9 +56,9 @@ Free software: `MIT license `__ :alt: Supported implementations :target: https://pypi.org/project/geophires-x -.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.8.3.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.8.4.svg :alt: Commits since latest release - :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.8.3...main + :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.8.4...main .. |docs| image:: https://readthedocs.org/projects/GEOPHIRES-X/badge/?style=flat :target: https://nrel.github.io/GEOPHIRES-X diff --git a/docs/conf.py b/docs/conf.py index 97db0e071..4df535946 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,7 +18,7 @@ year = '2025' author = 'NREL' copyright = f'{year}, {author}' -version = release = '3.8.3' +version = release = '3.8.4' pygments_style = 'trac' templates_path = ['./templates'] diff --git a/setup.py b/setup.py index 9068cdf3d..c6a49f541 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def read(*names, **kwargs): setup( name='geophires-x', - version='3.8.3', + version='3.8.4', license='MIT', description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.', long_description='{}\n{}'.format( diff --git a/src/geophires_x/__init__.py b/src/geophires_x/__init__.py index 20d4c1026..4f3bde541 100644 --- a/src/geophires_x/__init__.py +++ b/src/geophires_x/__init__.py @@ -1 +1 @@ -__version__ = '3.8.3' +__version__ = '3.8.4'