diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 55713fc5..8eed370d 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.9.24 +current_version = 3.9.25 commit = True tag = True diff --git a/.cookiecutterrc b/.cookiecutterrc index 27651a88..1c5f9e71 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.9.24 + version: 3.9.25 version_manager: "bump2version" website: "https://github.com/NREL" year_from: "2023" diff --git a/README.rst b/README.rst index cf85f9a3..e7c688d8 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.9.24.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.9.25.svg :alt: Commits since latest release - :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.9.24...main + :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.9.25...main .. |docs| image:: https://readthedocs.org/projects/GEOPHIRES-X/badge/?style=flat :target: https://nrel.github.io/GEOPHIRES-X diff --git a/docs/Fervo_Project_Cape-4.md b/docs/Fervo_Project_Cape-4.md index b6d0d5a2..06354385 100644 --- a/docs/Fervo_Project_Cape-4.md +++ b/docs/Fervo_Project_Cape-4.md @@ -8,7 +8,7 @@ Financial results are calculated using the [SAM Single Owner PPA Economic Model](https://softwareengineerprogrammer.github.io/GEOPHIRES/SAM-Economic-Models.html#sam-single-owner-ppa). -Key case study results include LCOE = $76.5/MWh and CAPEX = $4350/kW. +Key case study results include LCOE = $75.5/MWh and CAPEX = $4290/kW. [Click here](https://gtp.scientificwebservices.com/geophires/?geophires-example-id=Fervo_Project_Cape-4) to interactively explore the case study in the GEOPHIRES web interface. @@ -41,20 +41,20 @@ in source code for the full set of inputs. ### Economic Parameters -| Parameter | Input Value(s) | Source | -|-----------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Economic Model | SAM Single Owner PPA | The SAM Single Owner PPA economic model is used to calculate financial results including LCOE, NPV, IRR, and pro-forma cash flow analysis. See [GEOPHIRES documentation of SAM Economic Models](https://softwareengineerprogrammer.github.io/GEOPHIRES/SAM-Economic-Models.html) for details on how System Advisor Model financial models are integrated into GEOPHIRES. | -| Inflation Rate | 2.3% | US inflation rate as of April 2025 | -| PPA Price | Starting at 9.5 cents/kWh, escalating to 10 cents/kWh by project year 11 | Upper end of ranges given in 2024 NREL ATB (NREL, 2024). Both PPAs 'firm for 10 years at less than $100/MWh' estimate given in a podcast. | -| Well Drilling Cost Correlation & Adjustment Factor | Vertical large baseline correlation + adjustment factor = 0.84 to align with Fervo claimed drilling costs of <$4M/well | Akindipe & Witter, 2025; Latimer, 2025. | -| Reservoir Stimulation Capital Cost Adjustment Factor | 2.66 | Estimated cost of ~$2M per well. Typical range for Nth-of-kind projects may be $0.5–2M. | -| Capital Cost for Power Plant for Electricity Generation | $1900/kW | US DOE, 2021. | -| Discount Rate | 12% | Typical discount rates for high-risk projects may be 12–15% | -| Inflated Bond Interest Rate | 5.6% | Typical debt annual interest rate | -| Fraction of Investment in Bonds (percent debt vs. equity) | 60% | Approximate remaining percentage of CAPEX with $1 billion sponsor equity per Matson, 2024. Note that this source says that Fervo ultimately wants to target "15% sponsor equity, 15% bridge loan, and 70% construction to term loans", but this case study does not attempt to model that capital structure. | -| Exploration Capital Cost | $30M | Estimate significantly higher exploration costs than default correlation in consideration of potential risks associated with second/third/fourth-of-a-kind EGS projects | -| Investment Tax Credit Rate (ITC) | 30% | Same as 400 MWe case study (Fervo_Project_Cape-3) | -| Inflation Rate During Construction (additional indirect capital cost) | 15% | Estimate high indirect capital costs in consideration of potential risks associated with unforeseen engineering challenges or construction delays | +| Parameter | Input Value(s) | Source | +|-----------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Economic Model | SAM Single Owner PPA | The SAM Single Owner PPA economic model is used to calculate financial results including LCOE, NPV, IRR, and pro-forma cash flow analysis. See [GEOPHIRES documentation of SAM Economic Models](https://softwareengineerprogrammer.github.io/GEOPHIRES/SAM-Economic-Models.html) for details on how System Advisor Model financial models are integrated into GEOPHIRES. | +| Inflation Rate | 2.3% | US inflation rate as of April 2025 | +| PPA Price | Starting at 9.5 cents/kWh, escalating to 10 cents/kWh by project year 11 | Upper end of ranges given in 2024 NREL ATB (NREL, 2024). Both PPAs 'firm for 10 years at less than $100/MWh' estimate given in a podcast. | +| Well Drilling Cost Correlation & Adjustment Factor | Vertical large baseline correlation + adjustment factor = 0.8 to align with Fervo claimed drilling costs of <$4M/well | Akindipe & Witter, 2025; Latimer, 2025. | +| Reservoir Stimulation Capital Cost Adjustment Factor | 2.66 | Estimated cost of ~$2M per well. Typical range for Nth-of-kind projects may be $0.5–2M. | +| Capital Cost for Power Plant for Electricity Generation | $1900/kW | US DOE, 2021. | +| Discount Rate | 12% | Typical discount rates for high-risk projects may be 12–15% | +| Inflated Bond Interest Rate | 5.6% | Typical debt annual interest rate | +| Fraction of Investment in Bonds (percent debt vs. equity) | 60% | Approximate remaining percentage of CAPEX with $1 billion sponsor equity per Matson, 2024. Note that this source says that Fervo ultimately wants to target "15% sponsor equity, 15% bridge loan, and 70% construction to term loans", but this case study does not attempt to model that capital structure. | +| Exploration Capital Cost | $30M | Estimate significantly higher exploration costs than default correlation in consideration of potential risks associated with second/third/fourth-of-a-kind EGS projects | +| Investment Tax Credit Rate (ITC) | 30% | Same as 400 MWe case study (Fervo_Project_Cape-3) | +| Inflation Rate During Construction (additional indirect capital cost) | 15% | Estimate high indirect capital costs in consideration of potential risks associated with unforeseen engineering challenges or construction delays | ### Technical & Engineering Parameters @@ -67,7 +67,7 @@ in source code for the full set of inputs. | Number of Fractures per well | 102 | Estimate. (Note this is not a direct GEOPHIRES input parameter but was used to calculate other case study GEOPHIRES input parameters such as reservoir volume.) | | Fracture Separation | 18 m | Per Norbeck et al, 2024: lateral length is 4700 ft = 1432 m. Dividing 1432 by 80 = ~18 m fracture spacing. | | Fracture Geometry | 165.3 m × 165.3 m (Square) | Extrapolated from 30 million ft² fracture surface area per well per Fercho et al, 2025. | -| Reservoir Volume | 5,418,039,158 m³ | Calculated from fracture area (27,324.09 m²) × fracture separation (18 m) × targeted number of fractures per well (102) | +| Reservoir Volume | 5,919,217,617 m³ | Calculated from fracture area (27,324.09 m²) × fracture separation (18 m) × targeted number of fractures per well (102) | | Water Loss Rate | 15% | Water loss rate is conservatively estimated to be between 10 and 20%. Other estimates and some simulations may suggest a significantly lower water loss rate than this conservative estimate. See [Geothermal Mythbusting: Water Use and Impacts](https://fervoenergy.com/geothermal-mythbusting-water-use-and-impacts/) (Fervo Energy, 2025). | | Maximum Drawdown | 0.0153 | Tuned to keep minimum net electricity generation ≥ 500 MWe and thermal breakthrough requiring redrilling occurring every 5–10 years | | Reservoir Impedance | 0.001565 GPa.s/m³ | Yields ~15% initial pumping power/net installed power | @@ -82,18 +82,18 @@ in source code for the complete results. | Metric | Result Value | Reference Value(s) | Reference Source | |------------------------------------|----------------------------------------------------------|--------------------------|---------------------------------------------| -| LCOE | $76.5/MWh | $80/MWh | Horne et al, 2025 | -| Project capital costs: Total CAPEX | $2.67B | | | -| Project capital costs: $/kW | $4350/kW (based on maximum total electricity generation) | $4500/kW, $3000–$6000/kW | Horne et al, 2025; Latimer, 2025. | -| Well Drilling and Completion Cost | $3.96M/well | $<4M/well | Latimer, 2025. | +| LCOE | $75.5/MWh | $80/MWh | Horne et al, 2025 | +| Project capital costs: Total CAPEX | $2.64B | | | +| Project capital costs: $/kW | $4290/kW (based on maximum total electricity generation) | $4500/kW, $3000–$6000/kW | Horne et al, 2025; Latimer, 2025. | +| Well Drilling and Completion Cost | $3.96M/well (including 5% indirect costs) | $<4M/well | Latimer, 2025. | | WACC | 8.3% | 8.3% | Matson, 2024. | -| After-tax IRR | 30.7% | 15–25% | Typical levered returns for energy projects | +| After-tax IRR | 31.5% | 15–25% | Typical levered returns for energy projects | ### Technical & Engineering Results | Metric | Result Value | Reference Value(s) | Reference Source | |-------------------------------------------------|--------------|----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Minimum Net Electricity Generation | 503 MW | 500 MW | Fervo Energy, 2025. The 500 MW PPA is interpreted to mean that Cape Station's net electricity generation must never fall below 500 MWe. | +| Minimum Net Electricity Generation | 504 MW | 500 MW | Fervo Energy, 2025. The 500 MW PPA is interpreted to mean that Cape Station's net electricity generation must never fall below 500 MWe. | | Maximum Total Electricity Generation | 615 MW | | Actual maximum total generation may be bounded or constrained by modular power plant design not modeled in this case study. For example, a modular design with 50MW units may constrain maximum total generation to 600 MW. | | Number of times redrilling | 3 | 3–6 | Redrilling expected to be required within 5–10 years of project start | | Average Production Temperature | 199℃ | 204℃, 190.6–198.6℃ (optimal plant operating range) | Trent, 2024; Norbeck et al, 2024. | diff --git a/docs/conf.py b/docs/conf.py index 4aee1af7..4c0cad18 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,7 +18,7 @@ year = '2025' author = 'NREL' copyright = f'{year}, {author}' -version = release = '3.9.24' +version = release = '3.9.25' pygments_style = 'trac' templates_path = ['./templates'] diff --git a/setup.py b/setup.py index d8fcb8bf..1df97d72 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def read(*names, **kwargs): setup( name='geophires-x', - version='3.9.24', + version='3.9.25', license='MIT', description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.', long_description='{}\n{}'.format( diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index 660ff1ea..dc716d72 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -1633,6 +1633,7 @@ def __init__(self, model: Model): f'Provide {self.ccexplfixed.Name} to override the default correlation and set your own cost.' ) + # noinspection SpellCheckingInspection self.Cwell = self.OutputParameterDict[self.Cwell.Name] = OutputParameter( Name="Wellfield cost", display_name='Drilling and completion costs', @@ -1640,11 +1641,21 @@ def __init__(self, model: Model): PreferredUnits=CurrencyUnit.MDOLLARS, CurrentUnits=CurrencyUnit.MDOLLARS, - # See TODO re:parameterizing indirect costs at src/geophires_x/Economics.py:652 - # (https://github.com/NREL/GEOPHIRES-X/issues/383) + # TODO https://github.com/NREL/GEOPHIRES-X/issues/383?title=Parameterize+indirect+cost+factor ToolTipText="Includes total drilling and completion cost of all injection and production wells and " "laterals, plus 5% indirect costs." ) + self.drilling_and_completion_costs_per_well = self.OutputParameterDict[ + self.drilling_and_completion_costs_per_well.Name] = OutputParameter( + Name='Drilling and completion costs per well', + UnitType=Units.CURRENCY, + PreferredUnits=CurrencyUnit.MDOLLARS, + CurrentUnits=CurrencyUnit.MDOLLARS, + + # TODO https://github.com/NREL/GEOPHIRES-X/issues/383?title=Parameterize+indirect+cost+factor + ToolTipText='Includes total drilling and completion cost per well, ' + 'including injection and production wells and laterals, plus 5% indirect costs.' + ) self.Coamwell = self.OutputParameterDict[self.Coamwell.Name] = OutputParameter( Name="O&M Wellfield cost", display_name='Wellfield maintenance costs', @@ -2313,7 +2324,9 @@ def Calculate(self, model: Model) -> None: else: self.cost_lateral_section.value = 0.0 # cost of the well field - # 1.05 for 5% indirect costs - see TODO re:parameterizing at src/geophires_x/Economics.py:652 + + # 1.05 for 5% indirect costs + # TODO https://github.com/NREL/GEOPHIRES-X/issues/383?title=Parameterize+indirect+cost+factor self.Cwell.value = 1.05 * ((self.cost_one_production_well.value * model.wellbores.nprod.value) + (self.cost_one_injection_well.value * model.wellbores.ninj.value) + self.cost_lateral_section.value) @@ -2972,6 +2985,7 @@ def calculate_cashflow(self, model: Model) -> None: 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] + # noinspection SpellCheckingInspection def _calculate_derived_outputs(self, model: Model) -> None: """ Subclasses should call _calculate_derived_outputs at the end of their Calculate methods to populate output @@ -2988,5 +3002,11 @@ def _calculate_derived_outputs(self, model: Model) -> None: self.real_discount_rate.value = self.discountrate.quantity().to(convertible_unit( self.real_discount_rate.CurrentUnits)).magnitude + if hasattr(self, 'Cwell') and hasattr(model.wellbores, 'nprod') and hasattr(model.wellbores, 'ninj'): + self.drilling_and_completion_costs_per_well.value = ( + self.Cwell.value / + (model.wellbores.nprod.value + model.wellbores.ninj.value) + ) + def __str__(self): return "Economics" diff --git a/src/geophires_x/EconomicsSam.py b/src/geophires_x/EconomicsSam.py index 4b93b98d..eedff5a8 100644 --- a/src/geophires_x/EconomicsSam.py +++ b/src/geophires_x/EconomicsSam.py @@ -38,10 +38,10 @@ project_vir_parameter, project_payback_period_parameter, ) -from geophires_x.GeoPHIRESUtils import is_float, is_int +from geophires_x.GeoPHIRESUtils import is_float, is_int, sig_figs from geophires_x.OptionList import EconomicModel, EndUseOptions from geophires_x.Parameter import Parameter, OutputParameter, floatParameter -from geophires_x.Units import convertible_unit, EnergyCostUnit, CurrencyUnit, Units, PercentUnit +from geophires_x.Units import convertible_unit, EnergyCostUnit, CurrencyUnit, Units @dataclass @@ -162,7 +162,7 @@ def calculate_sam_economics(model: Model) -> SamEconomicsCalculations: cash_flow = _calculate_sam_economics_cash_flow(model, single_owner) def sf(_v: float, num_sig_figs: int = 5) -> float: - return _sig_figs(_v, num_sig_figs) + return sig_figs(_v, num_sig_figs) sam_economics: SamEconomicsCalculations = SamEconomicsCalculations(sam_cash_flow_profile=cash_flow) sam_economics.lcoe_nominal.value = sf(single_owner.Outputs.lcoe_nom) @@ -435,21 +435,3 @@ def _ppa_pricing_model( def _get_max_total_generation_kW(model: Model) -> float: return np.max(model.surfaceplant.ElectricityProduced.quantity().to(convertible_unit('kW')).magnitude) - - -def _sig_figs(val: float | list | tuple, num_sig_figs: int) -> float: - """ - TODO move to utilities, probably - """ - - if val is None: - return None - - if isinstance(val, list) or isinstance(val, tuple): - return [_sig_figs(v, num_sig_figs) for v in val] - - try: - return float('%s' % float(f'%.{num_sig_figs}g' % val)) # pylint: disable=consider-using-f-string - except TypeError: - # TODO warn - return val diff --git a/src/geophires_x/GeoPHIRESUtils.py b/src/geophires_x/GeoPHIRESUtils.py index e8328d8a..1a7594aa 100644 --- a/src/geophires_x/GeoPHIRESUtils.py +++ b/src/geophires_x/GeoPHIRESUtils.py @@ -642,3 +642,16 @@ def is_float(o: Any) -> bool: else: return True + +def sig_figs(val: float | list | tuple, num_sig_figs: int) -> float: + if val is None: + return None + + if isinstance(val, list) or isinstance(val, tuple): + return [sig_figs(v, num_sig_figs) for v in val] + + try: + return float('%s' % float(f'%.{num_sig_figs}g' % val)) # pylint: disable=consider-using-f-string + except TypeError: + # TODO warn + return val diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index ac9f7a2c..6ae0fb7a 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -458,7 +458,8 @@ def PrintOutputs(self, model: Model): f.write(f' Drilling and completion costs per production well: {econ.cost_one_production_well.value:10.2f} ' + econ.cost_one_production_well.CurrentUnits.value + NL) f.write(f' Drilling and completion costs per injection well: {econ.cost_one_injection_well.value:10.2f} ' + econ.cost_one_injection_well.CurrentUnits.value + NL) else: - f.write(f' Drilling and completion costs per well: {model.economics.Cwell.value/(model.wellbores.nprod.value+model.wellbores.ninj.value):10.2f} ' + model.economics.Cwell.CurrentUnits.value + NL) + cpw_label = Outputs._field_label(econ.drilling_and_completion_costs_per_well.display_name, 47) + f.write(f' {cpw_label}{econ.drilling_and_completion_costs_per_well.value:10.2f} {econ.Cwell.CurrentUnits.value}\n') f.write(f' {econ.Cstim.display_name}: {econ.Cstim.value:10.2f} {econ.Cstim.CurrentUnits.value}\n') f.write(f' Surface power plant costs: {model.economics.Cplant.value:10.2f} ' + model.economics.Cplant.CurrentUnits.value + NL) if model.surfaceplant.plant_type.value == PlantType.ABSORPTION_CHILLER: diff --git a/src/geophires_x/WellBores.py b/src/geophires_x/WellBores.py index d4f6e51d..ec25bf92 100644 --- a/src/geophires_x/WellBores.py +++ b/src/geophires_x/WellBores.py @@ -1035,14 +1035,24 @@ def __init__(self, model: Model): ErrMessage="assume default for Non-vertical Wellbore Diameter (0.156 m)", ToolTipText="Non-vertical Wellbore Diameter" ) + + max_allowed_total_wells = max(self.nprod.AllowableRange) + max(self.ninj.AllowableRange) + max_allowed_laterals_per_well_when_max_wells = 3 + """Arbitrary upper limit, could be increased in future if needed""" + + # noinspection SpellCheckingInspection self.numnonverticalsections = self.ParameterDict[self.numnonverticalsections.Name] = intParameter( "Number of Multilateral Sections", DefaultValue=0, - AllowableRange=list(range(0, 101, 1)), + AllowableRange=list(range(0, max_allowed_total_wells * max_allowed_laterals_per_well_when_max_wells, 1)), UnitType=Units.NONE, ErrMessage="assume default for Number of Nonvertical Wellbore Sections (0)", - ToolTipText="Number of Nonvertical Wellbore Sections" + ToolTipText='Number of Nonvertical Wellbore Sections, aka laterals or horizontals. ' + 'Note that this is the total number of sections for the entire project and not the number of ' + 'sections per well. For example, a project with 2 injectors and 2 producers with 3 laterals ' + 'per well should set Number of Multilateral Sections = 2 * 2 * 3 = 12.' ) + self.NonverticalsCased = self.ParameterDict[self.NonverticalsCased.Name] = boolParameter( "Multilaterals Cased", DefaultValue=False, diff --git a/src/geophires_x/__init__.py b/src/geophires_x/__init__.py index 84fb14cc..ae9c5753 100644 --- a/src/geophires_x/__init__.py +++ b/src/geophires_x/__init__.py @@ -1 +1 @@ -__version__ = '3.9.24' +__version__ = '3.9.25' diff --git a/src/geophires_x_client/__init__.py b/src/geophires_x_client/__init__.py index c2b30358..954e1fac 100644 --- a/src/geophires_x_client/__init__.py +++ b/src/geophires_x_client/__init__.py @@ -1,9 +1,8 @@ import atexit -import os import sys import threading from multiprocessing import Manager -from pathlib import Path +from multiprocessing import current_process # noinspection PyPep8Naming from geophires_x import GEOPHIRESv3 as geophires @@ -29,31 +28,37 @@ class GeophiresXClient: _init_lock = threading.Lock() """A standard threading lock to make the one-time initialization thread-safe.""" - def __init__(self, enable_caching=True, logger_name=None): + def __init__(self, enable_caching=False, logger_name=None): if logger_name is None: logger_name = __name__ self._logger = _get_logger(logger_name=logger_name) self._enable_caching = enable_caching - # Lazy-initialize shared resources if they haven't been already. if enable_caching and GeophiresXClient._manager is None: + # Lazy-initialize shared resources if they haven't been already. self._initialize_shared_resources() @classmethod def _initialize_shared_resources(cls): """ Initializes the multiprocessing Manager and shared resources in a - thread-safe manner. It also registers the shutdown hook to ensure - automatic cleanup on application exit. + thread-safe and now process-safe manner. It also registers the + shutdown hook to ensure automatic cleanup on application exit. """ - with cls._init_lock: - if cls._manager is None: - cls._manager = Manager() - cls._cache = cls._manager.dict() - cls._lock = cls._manager.RLock() - # Register the shutdown method to be called automatically on exit. - atexit.register(cls.shutdown) + # Ensure that only the top-level user process can create the manager. + # A spawned child process, which re-imports this script, will have a different name + # (e.g., 'Spawn-1') and will skip this entire block, preventing a recursive crash. + if current_process().name == 'MainProcess': + with cls._init_lock: + if cls._manager is None: + cls._logger = _get_logger(__name__) # Add a logger for this class method + cls._logger.debug('MainProcess is creating the shared multiprocessing manager...') + cls._manager = Manager() + cls._cache = cls._manager.dict() + cls._lock = cls._manager.RLock() + # Register the shutdown method to be called automatically on exit. + atexit.register(cls.shutdown) @classmethod def shutdown(cls): @@ -65,9 +70,17 @@ def shutdown(cls): """ with cls._init_lock: if cls._manager is not None: + cls._logger = _get_logger(__name__) + cls._logger.debug('Shutting down the shared multiprocessing manager...') cls._manager.shutdown() # De-register the hook to avoid trying to shut down twice. - atexit.unregister(cls.shutdown) + try: + atexit.unregister(cls.shutdown) + except Exception as e: + # Fails in some environments (e.g. pytest), but is not critical + cls._logger.debug( + f'Encountered exception shutting down the shared multiprocessing manager (OK): ' f'{e!s}' + ) cls._manager = None cls._cache = None cls._lock = None @@ -80,22 +93,23 @@ def get_geophires_result(self, input_params: GeophiresInputParameters) -> Geophi """ is_immutable = isinstance(input_params, ImmutableGeophiresInputParameters) - if not (self._enable_caching and is_immutable): + if not (self._enable_caching and is_immutable and GeophiresXClient._manager is not None): return self._run_simulation(input_params) cache_key = hash(input_params) with GeophiresXClient._lock: if cache_key in GeophiresXClient._cache: + # self._logger.debug(f'Cache hit for inputs: {input_params}') return GeophiresXClient._cache[cache_key] + # Cache miss result = self._run_simulation(input_params) GeophiresXClient._cache[cache_key] = result return result def _run_simulation(self, input_params: GeophiresInputParameters) -> GeophiresXResult: """Helper method to encapsulate the actual GEOPHIRES run.""" - stash_cwd = Path.cwd() stash_sys_argv = sys.argv sys.argv = ['', input_params.as_file_path(), input_params.get_output_file_path()] @@ -107,7 +121,6 @@ def _run_simulation(self, input_params: GeophiresInputParameters) -> GeophiresXR raise RuntimeError('GEOPHIRES exited without giving a reason') from None finally: sys.argv = stash_sys_argv - os.chdir(stash_cwd) self._logger.info(f'GEOPHIRES-X output file: {input_params.get_output_file_path()}') result = GeophiresXResult(input_params.get_output_file_path()) diff --git a/src/geophires_x_schema_generator/geophires-request.json b/src/geophires_x_schema_generator/geophires-request.json index 34ebf881..a7e62abd 100644 --- a/src/geophires_x_schema_generator/geophires-request.json +++ b/src/geophires_x_schema_generator/geophires-request.json @@ -947,13 +947,13 @@ "maximum": 100.0 }, "Number of Multilateral Sections": { - "description": "Number of Nonvertical Wellbore Sections", + "description": "Number of Nonvertical Wellbore Sections, aka laterals or horizontals. Note that this is the total number of sections for the entire project and not the number of sections per well. For example, a project with 2 injectors and 2 producers with 3 laterals per well should set Number of Multilateral Sections = 2 * 2 * 3 = 12.", "type": "integer", "units": null, "category": "Well Bores", "default": 0, "minimum": 0, - "maximum": 100 + "maximum": 1199 }, "Multilaterals Cased": { "description": "If set to True, casing & cementing are assumed to comprise 50% of drilling costs (doubling cost compared to uncased).", diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index 5294e9b9..8fadbe38 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -333,7 +333,11 @@ "description": "Wellfield cost. Includes total drilling and completion cost of all injection and production wells and laterals, plus 5% indirect costs.", "units": "MUSD" }, - "Drilling and completion costs per well": {}, + "Drilling and completion costs per well": { + "type": "number", + "description": "Includes total drilling and completion cost per well, including injection and production wells and laterals, plus 5% indirect costs.", + "units": "MUSD" + }, "Drilling and completion costs per production well": {}, "Drilling and completion costs per injection well": {}, "Drilling and completion costs per vertical production well": {}, diff --git a/tests/base_test_case.py b/tests/base_test_case.py index 1e36812e..36bf53ba 100644 --- a/tests/base_test_case.py +++ b/tests/base_test_case.py @@ -15,8 +15,8 @@ class BaseTestCase(unittest.TestCase): def _get_test_file_path(self, test_file_name) -> str: return os.path.join(os.path.abspath(os.path.dirname(inspect.getfile(self.__class__))), test_file_name) - def _get_test_file_content(self, test_file_name): - with open(self._get_test_file_path(test_file_name)) as f: + def _get_test_file_content(self, test_file_name, **open_kw_args) -> str: + with open(self._get_test_file_path(test_file_name), **open_kw_args) as f: return f.readlines() def _list_test_files_dir(self, test_files_dir: str): diff --git a/tests/examples/Fervo_Project_Cape-4.out b/tests/examples/Fervo_Project_Cape-4.out index dcdb16f0..55113f51 100644 --- a/tests/examples/Fervo_Project_Cape-4.out +++ b/tests/examples/Fervo_Project_Cape-4.out @@ -4,17 +4,17 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.9.18 - Simulation Date: 2025-06-18 - Simulation Time: 13:23 - Calculation Time: 1.554 sec + GEOPHIRES Version: 3.9.24 + Simulation Date: 2025-06-20 + Simulation Time: 14:04 + Calculation Time: 1.575 sec ***SUMMARY OF RESULTS*** End-Use Option: Electricity - Average Net Electricity Production: 531.71 MW - Electricity breakeven price: 7.65 cents/kWh - Total CAPEX: 2673.11 MUSD + Average Net Electricity Production: 532.53 MW + Electricity breakeven price: 7.55 cents/kWh + Total CAPEX: 2639.39 MUSD Number of production wells: 59 Number of injection wells: 59 Flowrate per production well: 107.0 kg/sec @@ -31,12 +31,12 @@ Simulation Metadata Accrued financing during construction: 15.00 % Project lifetime: 30 yr Capacity factor: 90.0 % - Project NPV: 612.72 MUSD - After-tax IRR: 30.66 % - Project VIR=PI=PIR: 1.57 - Project MOIC: 5.55 - Project Payback Period: 2.01 yr - Estimated Jobs Created: 1298 + Project NPV: 641.24 MUSD + After-tax IRR: 31.51 % + Project VIR=PI=PIR: 1.61 + Project MOIC: 5.77 + Project Payback Period: 1.95 yr + Estimated Jobs Created: 1300 ***ENGINEERING PARAMETERS*** @@ -47,7 +47,7 @@ Simulation Metadata Pump efficiency: 80.0 % Injection temperature: 56.6 degC Production Wellbore heat transmission calculated with Ramey's model - Average production well temperature drop: 0.5 degC + Average production well temperature drop: 0.6 degC Flowrate per production well: 107.0 kg/sec Injection well casing ID: 9.625 in Production well casing ID: 9.625 in @@ -69,10 +69,10 @@ Simulation Metadata Fracture model = Square Well separation: fracture height: 165.30 meter Fracture area: 27324.09 m**2 - Number of fractures calculated with reservoir volume and fracture separation as input - Number of fractures: 11018 + Reservoir volume calculated with fracture separation and number of fractures as input + Number of fractures: 12036 Fracture separation: 18.00 meter - Reservoir volume: 5418039158 m**3 + Reservoir volume: 5919217617 m**3 Reservoir impedance: 0.0016 GPa.s/m**3 Reservoir density: 2800.00 kg/m**3 Reservoir thermal conductivity: 3.05 W/m/K @@ -81,59 +81,57 @@ Simulation Metadata ***RESERVOIR SIMULATION RESULTS*** - Maximum Production Temperature: 199.5 degC - Average Production Temperature: 198.9 degC - Minimum Production Temperature: 195.2 degC + Maximum Production Temperature: 199.6 degC + Average Production Temperature: 199.0 degC + Minimum Production Temperature: 195.4 degC Initial Production Temperature: 198.2 degC - Average Reservoir Heat Extraction: 3758.70 MW + Average Reservoir Heat Extraction: 3761.51 MW Production Wellbore Heat Transmission Model = Ramey Model - Average Production Well Temperature Drop: 0.5 degC - Total Average Pressure Drop: 8523.8 kPa + Average Production Well Temperature Drop: 0.6 degC + Total Average Pressure Drop: 8521.8 kPa Average Injection Well Pressure Drop: 600.9 kPa Average Reservoir Pressure Drop: 10344.9 kPa Average Production Well Pressure Drop: 504.2 kPa - Average Buoyancy Pressure Drop: -2926.2 kPa + Average Buoyancy Pressure Drop: -2928.2 kPa ***CAPITAL COSTS (M$)*** - Drilling and completion costs: 497.69 MUSD - Drilling and completion costs per vertical production well: 3.96 MUSD - Drilling and completion costs per vertical injection well: 3.96 MUSD - Drilling and completion costs per non-vertical section: 2.08 MUSD + Drilling and completion costs: 467.75 MUSD + Drilling and completion costs per well: 3.96 MUSD Stimulation costs: 236.88 MUSD - Surface power plant costs: 1503.42 MUSD - Field gathering system costs: 56.45 MUSD - Total surface equipment costs: 1559.87 MUSD + Surface power plant costs: 1504.05 MUSD + Field gathering system costs: 56.44 MUSD + Total surface equipment costs: 1560.49 MUSD Exploration costs: 30.00 MUSD - Investment Tax Credit: -697.33 MUSD - Total CAPEX: 2673.11 MUSD + Investment Tax Credit: -688.54 MUSD + Total CAPEX: 2639.39 MUSD ***OPERATING AND MAINTENANCE COSTS (M$/yr)*** - Wellfield maintenance costs: 6.50 MUSD/yr - Power plant maintenance costs: 25.42 MUSD/yr + Wellfield maintenance costs: 6.20 MUSD/yr + Power plant maintenance costs: 25.43 MUSD/yr Water costs: 24.86 MUSD/yr - Total operating and maintenance costs: 130.24 MUSD/yr + Total operating and maintenance costs: 126.95 MUSD/yr ***SURFACE EQUIPMENT SIMULATION RESULTS*** Initial geofluid availability: 0.19 MW/(kg/s) - Maximum Total Electricity Generation: 614.34 MW - Average Total Electricity Generation: 609.40 MW - Minimum Total Electricity Generation: 581.66 MW + Maximum Total Electricity Generation: 614.60 MW + Average Total Electricity Generation: 610.21 MW + Minimum Total Electricity Generation: 583.15 MW Initial Total Electricity Generation: 604.35 MW - Maximum Net Electricity Generation: 536.88 MW - Average Net Electricity Generation: 531.71 MW - Minimum Net Electricity Generation: 502.90 MW + Maximum Net Electricity Generation: 537.14 MW + Average Net Electricity Generation: 532.53 MW + Minimum Net Electricity Generation: 504.44 MW Initial Net Electricity Generation: 526.63 MW - Average Annual Total Electricity Generation: 4804.56 GWh - Average Annual Net Electricity Generation: 4192.05 GWh + Average Annual Total Electricity Generation: 4810.97 GWh + Average Annual Net Electricity Generation: 4198.60 GWh Initial pumping power/net installed power: 14.76 % - Average Pumping Power: 77.69 MW - Heat to Power Conversion Efficiency: 14.15 % + Average Pumping Power: 77.67 MW + Heat to Power Conversion Efficiency: 14.16 % ************************************************************ * HEATING, COOLING AND/OR ELECTRICITY PRODUCTION PROFILE * @@ -143,34 +141,34 @@ Simulation Metadata (degC) (MW) (MW) (%) 1 1.0000 198.22 77.7286 526.6263 14.0769 2 1.0051 199.24 77.6662 534.3539 14.1818 - 3 1.0061 199.42 77.6548 535.7720 14.2009 - 4 1.0065 199.52 77.6495 536.4851 14.2105 - 5 1.0066 199.54 77.6560 536.6451 14.2126 - 6 1.0057 199.35 77.7115 535.1783 14.1922 - 7 1.0019 198.61 77.9043 529.3559 14.1115 - 8 0.9926 196.75 78.3717 514.8879 13.9080 - 9 1.0037 198.95 77.6277 532.2546 14.1541 - 10 1.0056 199.33 77.6188 535.1354 14.1929 - 11 1.0063 199.47 77.6071 536.1662 14.2068 - 12 1.0066 199.54 77.5953 536.7025 14.2141 - 13 1.0065 199.50 77.6023 536.4142 14.2102 - 14 1.0046 199.14 77.6869 533.5811 14.1712 - 15 0.9989 198.00 77.9623 524.7524 14.0484 - 16 0.9862 195.49 78.5792 505.2627 13.7716 - 17 1.0048 199.18 77.5103 534.1012 14.1805 - 18 1.0059 199.40 77.4976 535.7642 14.2029 - 19 1.0065 199.50 77.4875 536.5580 14.2136 - 20 1.0067 199.54 77.4864 536.8507 14.2175 - 21 1.0060 199.41 77.5261 535.7989 14.2030 - 22 1.0029 198.79 77.6864 530.9677 14.1361 - 23 0.9947 197.16 78.0990 518.2759 13.9582 - 24 1.0027 198.76 77.4615 530.9336 14.1386 - 25 1.0054 199.30 77.4599 535.0372 14.1937 - 26 1.0062 199.45 77.4589 536.1857 14.2090 - 27 1.0066 199.53 77.4598 536.7844 14.2170 - 28 1.0066 199.52 77.4767 536.6927 14.2156 - 29 1.0051 199.24 77.5592 534.4598 14.1846 - 30 1.0003 198.27 77.8106 526.9180 14.0797 + 3 1.0061 199.42 77.6547 535.7721 14.2009 + 4 1.0065 199.52 77.6489 536.5044 14.2108 + 5 1.0068 199.57 77.6474 536.9145 14.2163 + 6 1.0067 199.55 77.6626 536.7091 14.2133 + 7 1.0054 199.29 77.7347 534.6638 14.1850 + 8 1.0012 198.47 77.9449 528.2545 14.0960 + 9 0.9919 196.61 78.4108 513.7990 13.8924 + 10 1.0040 199.02 77.6265 532.7400 14.1607 + 11 1.0057 199.35 77.6184 535.2509 14.1944 + 12 1.0063 199.48 77.6077 536.2282 14.2076 + 13 1.0067 199.55 77.5951 536.8041 14.2155 + 14 1.0068 199.58 77.5874 537.0016 14.2182 + 15 1.0063 199.46 77.6101 536.1193 14.2061 + 16 1.0038 198.97 77.7249 532.3053 14.1535 + 17 0.9974 197.70 78.0349 522.3915 14.0153 + 18 1.0000 198.22 77.5160 526.8389 14.0826 + 19 1.0051 199.24 77.5030 534.5170 14.1861 + 20 1.0061 199.42 77.4920 535.9348 14.2052 + 21 1.0065 199.52 77.4831 536.6702 14.2152 + 22 1.0068 199.57 77.4784 537.0835 14.2208 + 23 1.0067 199.55 77.4911 536.8806 14.2179 + 24 1.0054 199.29 77.5620 534.8365 14.1896 + 25 1.0012 198.47 77.7729 528.4265 14.1006 + 26 0.9919 196.61 78.2420 513.9678 13.8970 + 27 1.0040 199.02 77.4599 532.9067 14.1651 + 28 1.0057 199.35 77.4589 535.4103 14.1987 + 29 1.0063 199.48 77.4584 536.3775 14.2116 + 30 1.0067 199.55 77.4587 536.9405 14.2191 ******************************************************************* @@ -179,36 +177,36 @@ Simulation Metadata YEAR ELECTRICITY HEAT RESERVOIR PERCENTAGE OF PROVIDED EXTRACTED HEAT CONTENT TOTAL HEAT MINED (GWh/year) (GWh/year) (10^15 J) (%) - 1 4192.9 29636.9 1632.52 6.13 - 2 4219.2 29728.1 1525.50 12.29 - 3 4227.1 29755.4 1418.38 18.45 - 4 4230.8 29768.3 1311.21 24.61 - 5 4226.9 29755.2 1204.09 30.77 - 6 4200.5 29666.3 1097.29 36.91 - 7 4123.6 29405.9 991.43 43.00 - 8 4085.2 29271.2 886.06 49.05 - 9 4210.4 29696.4 779.15 55.20 - 10 4223.5 29741.8 672.08 61.36 - 11 4229.5 29762.5 564.93 67.52 - 12 4231.1 29767.9 457.77 73.68 - 13 4220.5 29731.9 350.73 79.83 - 14 4177.3 29585.5 244.23 85.96 - 15 4068.9 29216.8 139.05 92.01 - 16 4161.7 29526.4 32.75 98.12 - 17 4218.4 29721.1 -74.24 104.27 - 18 4227.4 29752.0 -181.35 110.43 - 19 4231.8 29767.3 -288.51 116.59 - 20 4229.8 29760.8 -395.65 122.75 - 21 4208.8 29689.9 -502.54 128.89 - 22 4142.8 29466.4 -608.62 134.99 - 23 4066.8 29205.0 -713.75 141.04 - 24 4207.2 29680.6 -820.60 147.18 - 25 4223.3 29736.8 -927.66 153.34 - 26 4229.9 29759.9 -1034.79 159.50 - 27 4232.4 29768.7 -1141.96 165.66 - 28 4224.7 29743.1 -1249.03 171.82 - 29 4188.8 29621.9 -1355.67 177.95 - 30 4099.9 29320.6 -1461.23 184.02 + 1 4192.9 29636.9 1793.40 5.62 + 2 4219.2 29728.1 1686.38 11.25 + 3 4227.2 29755.6 1579.26 16.89 + 4 4231.6 29771.1 1472.08 22.53 + 5 4232.9 29775.6 1364.89 28.17 + 6 4225.2 29749.9 1257.79 33.80 + 7 4194.0 29644.4 1151.07 39.42 + 8 4114.4 29374.5 1045.32 44.99 + 9 4101.4 29325.8 939.75 50.54 + 10 4212.2 29702.5 832.82 56.17 + 11 4224.2 29744.1 725.74 61.80 + 12 4230.1 29764.4 618.59 67.44 + 13 4233.3 29775.3 511.40 73.09 + 14 4231.4 29768.8 404.23 78.73 + 15 4214.5 29711.6 297.27 84.36 + 16 4162.9 29536.5 190.94 89.95 + 17 4055.6 29171.2 85.92 95.48 + 18 4194.3 29636.9 -20.77 101.09 + 19 4220.5 29728.1 -127.79 106.73 + 20 4228.5 29755.6 -234.91 112.36 + 21 4233.0 29771.1 -342.09 118.00 + 22 4234.2 29775.6 -449.28 123.65 + 23 4226.6 29749.9 -556.38 129.28 + 24 4195.3 29644.4 -663.10 134.90 + 25 4115.7 29374.5 -768.85 140.46 + 26 4102.7 29325.8 -874.42 146.02 + 27 4213.5 29702.5 -981.35 151.65 + 28 4225.4 29744.1 -1088.43 157.28 + 29 4231.2 29764.4 -1195.58 162.92 + 30 4234.3 29775.2 -1302.77 168.56 *************************** * SAM CASH FLOW PROFILE * @@ -216,52 +214,52 @@ Simulation Metadata ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Year 0 Year 1 Year 2 Year 3 Year 4 Year 5 Year 6 Year 7 Year 8 Year 9 Year 10 Year 11 Year 12 Year 13 Year 14 Year 15 Year 16 Year 17 Year 18 Year 19 Year 20 Year 21 Year 22 Year 23 Year 24 Year 25 Year 26 Year 27 Year 28 Year 29 Year 30 ENERGY -Electricity to grid (kWh) 0.0 4,193,226,013 4,219,526,008 4,227,429,703 4,231,130,578 4,227,174,470 4,200,800,078 4,123,944,075 4,085,524,292 4,210,727,110 4,223,816,595 4,229,858,515 4,231,437,288 4,220,815,353 4,177,617,023 4,069,152,556 4,162,009,516 4,218,752,453 4,227,707,328 4,232,132,093 4,230,152,834 4,209,135,409 4,143,119,490 4,067,069,431 4,207,503,902 4,223,608,553 4,230,238,414 4,232,692,624 4,225,013,608 4,189,101,096 4,100,244,505 +Electricity to grid (kWh) 0.0 4,193,273,525 4,219,573,970 4,227,516,388 4,232,001,035 4,233,245,126 4,225,570,913 4,194,325,606 4,114,710,394 4,101,737,992 4,212,547,398 4,224,519,385 4,230,441,060 4,233,667,186 4,231,750,368 4,214,888,525 4,163,207,043 4,055,985,743 4,194,671,056 4,220,853,770 4,228,811,003 4,233,321,393 4,234,588,146 4,226,928,684 4,195,686,141 4,116,056,232 4,103,061,353 4,213,834,812 4,225,738,401 4,231,569,184 4,234,649,495 Electricity from grid (kWh) 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -Electricity to grid net (kWh) 0.0 4,193,226,013 4,219,526,008 4,227,429,703 4,231,130,578 4,227,174,470 4,200,800,078 4,123,944,075 4,085,524,292 4,210,727,110 4,223,816,595 4,229,858,515 4,231,437,288 4,220,815,353 4,177,617,023 4,069,152,556 4,162,009,516 4,218,752,453 4,227,707,328 4,232,132,093 4,230,152,834 4,209,135,409 4,143,119,490 4,067,069,431 4,207,503,902 4,223,608,553 4,230,238,414 4,232,692,624 4,225,013,608 4,189,101,096 4,100,244,505 +Electricity to grid net (kWh) 0.0 4,193,273,525 4,219,573,970 4,227,516,388 4,232,001,035 4,233,245,126 4,225,570,913 4,194,325,606 4,114,710,394 4,101,737,992 4,212,547,398 4,224,519,385 4,230,441,060 4,233,667,186 4,231,750,368 4,214,888,525 4,163,207,043 4,055,985,743 4,194,671,056 4,220,853,770 4,228,811,003 4,233,321,393 4,234,588,146 4,226,928,684 4,195,686,141 4,116,056,232 4,103,061,353 4,213,834,812 4,225,738,401 4,231,569,184 4,234,649,495 REVENUE PPA price (cents/kWh) 0.0 9.50 9.50 9.56 9.61 9.67 9.73 9.79 9.84 9.90 9.96 10.01 10.07 10.13 10.18 10.24 10.30 10.36 10.41 10.47 10.53 10.58 10.64 10.70 10.75 10.81 10.87 10.93 10.98 11.04 11.10 -PPA revenue ($) 0 398,356,471 400,854,971 404,015,457 406,780,894 408,810,043 408,653,832 403,527,928 402,097,301 416,819,877 420,523,180 423,535,733 426,105,735 427,441,971 425,448,518 416,721,913 428,603,740 436,851,817 440,188,887 443,061,909 445,265,887 445,452,800 440,827,914 435,054,417 452,474,970 456,614,321 459,742,311 462,421,669 463,990,994 462,434,870 454,963,130 +PPA revenue ($) 0 398,360,985 400,859,527 404,023,741 406,864,580 409,397,136 411,063,538 410,414,761 404,969,797 406,031,044 419,401,219 423,001,126 426,005,415 428,743,476 430,961,457 431,646,734 428,727,061 419,997,324 436,749,150 441,881,181 445,124,646 448,012,403 450,560,179 452,154,561 451,204,088 444,986,839 445,920,708 460,361,453 464,070,591 467,122,922 469,876,708 Curtailment payment revenue ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Capacity payment revenue ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -Salvage value ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1,336,555,047 -Total revenue ($) 0 398,356,471 400,854,971 404,015,457 406,780,894 408,810,043 408,653,832 403,527,928 402,097,301 416,819,877 420,523,180 423,535,733 426,105,735 427,441,971 425,448,518 416,721,913 428,603,740 436,851,817 440,188,887 443,061,909 445,265,887 445,452,800 440,827,914 435,054,417 452,474,970 456,614,321 459,742,311 462,421,669 463,990,994 462,434,870 1,791,518,178 +Salvage value ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1,319,696,135 +Total revenue ($) 0 398,360,985 400,859,527 404,023,741 406,864,580 409,397,136 411,063,538 410,414,761 404,969,797 406,031,044 419,401,219 423,001,126 426,005,415 428,743,476 430,961,457 431,646,734 428,727,061 419,997,324 436,749,150 441,881,181 445,124,646 448,012,403 450,560,179 452,154,561 451,204,088 444,986,839 445,920,708 460,361,453 464,070,591 467,122,922 1,789,572,843 -Property tax net assessed value ($) 0 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 2,673,110,095 +Property tax net assessed value ($) 0 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 2,639,392,270 OPERATING EXPENSES -O&M fixed expense ($) 0 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 +O&M fixed expense ($) 0 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 O&M production-based expense ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 O&M capacity-based expense ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Fuel expense ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Electricity purchase ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Property tax expense ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Insurance expense ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -Total operating expenses ($) 0 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 130,236,463 +Total operating expenses ($) 0 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 126,952,474 -EBITDA ($) 0 268,120,008 270,618,508 273,778,994 276,544,431 278,573,580 278,417,368 273,291,465 271,860,838 286,583,414 290,286,717 293,299,270 295,869,272 297,205,508 295,212,055 286,485,450 298,367,277 306,615,353 309,952,424 312,825,446 315,029,424 315,216,337 310,591,451 304,817,954 322,238,507 326,377,858 329,505,848 332,185,206 333,754,531 332,198,407 1,661,281,714 +EBITDA ($) 0 271,408,511 273,907,053 277,071,267 279,912,106 282,444,662 284,111,065 283,462,287 278,017,323 279,078,570 292,448,745 296,048,652 299,052,941 301,791,002 304,008,984 304,694,260 301,774,587 293,044,850 309,796,677 314,928,707 318,172,172 321,059,929 323,607,705 325,202,088 324,251,614 318,034,365 318,968,234 333,408,979 337,118,117 340,170,448 1,662,620,369 OPERATING ACTIVITIES -EBITDA ($) 0 268,120,008 270,618,508 273,778,994 276,544,431 278,573,580 278,417,368 273,291,465 271,860,838 286,583,414 290,286,717 293,299,270 295,869,272 297,205,508 295,212,055 286,485,450 298,367,277 306,615,353 309,952,424 312,825,446 315,029,424 315,216,337 310,591,451 304,817,954 322,238,507 326,377,858 329,505,848 332,185,206 333,754,531 332,198,407 1,661,281,714 +EBITDA ($) 0 271,408,511 273,907,053 277,071,267 279,912,106 282,444,662 284,111,065 283,462,287 278,017,323 279,078,570 292,448,745 296,048,652 299,052,941 301,791,002 304,008,984 304,694,260 301,774,587 293,044,850 309,796,677 314,928,707 318,172,172 321,059,929 323,607,705 325,202,088 324,251,614 318,034,365 318,968,234 333,408,979 337,118,117 340,170,448 1,662,620,369 Interest earned on reserves ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 plus PBI if not available for debt service: Federal PBI income ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 State PBI income ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Utility PBI income ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Other PBI income ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -Debt interest payment ($) 0 89,816,499 88,597,952 87,311,166 85,952,321 84,517,380 83,002,082 81,401,927 79,712,164 77,927,774 76,043,459 74,053,621 71,952,353 69,733,414 67,390,214 64,915,795 62,302,808 59,543,495 56,629,659 53,552,649 50,303,327 46,872,042 43,248,605 39,422,256 35,381,631 31,114,732 26,608,886 21,850,712 16,826,081 11,520,070 5,916,923 -Cash flow from operating activities ($) 0 178,303,509 182,020,556 186,467,827 190,592,110 194,056,200 195,415,287 191,889,537 192,148,674 208,655,639 214,243,258 219,245,649 223,916,919 227,472,094 227,821,841 221,569,655 236,064,468 247,071,859 253,322,765 259,272,797 264,726,098 268,344,295 267,342,846 265,395,698 286,856,875 295,263,126 302,896,962 310,334,494 316,928,450 320,678,336 1,655,364,791 +Debt interest payment ($) 0 88,683,580 87,480,404 86,209,849 84,868,143 83,451,302 81,955,118 80,375,147 78,706,698 76,944,816 75,084,269 73,119,531 71,044,767 68,853,817 66,540,174 64,096,966 61,516,939 58,792,431 55,915,350 52,877,152 49,668,815 46,280,812 42,703,080 38,924,995 34,935,338 30,722,260 26,273,249 21,575,094 16,613,842 11,374,760 5,842,289 +Cash flow from operating activities ($) 0 182,724,931 186,426,650 190,861,418 195,043,962 198,993,360 202,155,947 203,087,139 199,310,625 202,133,754 217,364,476 222,929,121 228,008,174 232,937,185 237,468,810 240,597,294 240,257,648 234,252,419 253,881,327 262,051,556 268,503,357 274,779,118 280,904,625 286,277,092 289,316,276 287,312,106 292,694,985 311,833,886 320,504,276 328,795,689 1,656,778,080 INVESTING ACTIVITIES -Total installed cost ($) -2,673,110,095 +Total installed cost ($) -2,639,392,270 Debt closing costs ($) 0 Debt up-front fee ($) 0 minus: Total IBI income ($) 0 Total CBI income ($) 0 equals: -Purchase of property ($) -2,673,110,095 +Purchase of property ($) -2,639,392,270 plus: Reserve (increase)/decrease debt service ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Reserve (increase)/decrease working capital ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 @@ -273,86 +271,86 @@ Reserve capital spending major equipment 1 ($) 0 0 0 Reserve capital spending major equipment 2 ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Reserve capital spending major equipment 3 ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 equals: -Cash flow from investing activities ($) -2,673,110,095 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Cash flow from investing activities ($) -2,639,392,270 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 FINANCING ACTIVITIES -Issuance of equity ($) 1,069,244,038 -Size of debt ($) 1,603,866,057 +Issuance of equity ($) 1,055,756,908 +Size of debt ($) 1,583,635,362 minus: -Debt principal payment ($) 0 21,759,769 22,978,316 24,265,102 25,623,948 27,058,889 28,574,187 30,174,341 31,864,104 33,648,494 35,532,810 37,522,647 39,623,915 41,842,855 44,186,054 46,660,474 49,273,460 52,032,774 54,946,609 58,023,619 61,272,942 64,704,227 68,327,663 72,154,013 76,194,637 80,461,537 84,967,383 89,725,556 94,750,188 100,056,198 105,659,345 +Debt principal payment ($) 0 21,485,298 22,688,475 23,959,029 25,300,735 26,717,576 28,213,760 29,793,731 31,462,180 33,224,062 35,084,609 37,049,347 39,124,111 41,315,061 43,628,705 46,071,912 48,651,939 51,376,448 54,253,529 57,291,726 60,500,063 63,888,067 67,465,798 71,243,883 75,233,540 79,446,619 83,895,629 88,593,785 93,555,037 98,794,119 104,326,589 equals: -Cash flow from financing activities ($) 2,673,110,095 -21,759,769 -22,978,316 -24,265,102 -25,623,948 -27,058,889 -28,574,187 -30,174,341 -31,864,104 -33,648,494 -35,532,810 -37,522,647 -39,623,915 -41,842,855 -44,186,054 -46,660,474 -49,273,460 -52,032,774 -54,946,609 -58,023,619 -61,272,942 -64,704,227 -68,327,663 -72,154,013 -76,194,637 -80,461,537 -84,967,383 -89,725,556 -94,750,188 -100,056,198 -105,659,345 +Cash flow from financing activities ($) 2,639,392,270 -21,485,298 -22,688,475 -23,959,029 -25,300,735 -26,717,576 -28,213,760 -29,793,731 -31,462,180 -33,224,062 -35,084,609 -37,049,347 -39,124,111 -41,315,061 -43,628,705 -46,071,912 -48,651,939 -51,376,448 -54,253,529 -57,291,726 -60,500,063 -63,888,067 -67,465,798 -71,243,883 -75,233,540 -79,446,619 -83,895,629 -88,593,785 -93,555,037 -98,794,119 -104,326,589 PROJECT RETURNS Pre-tax Cash Flow: -Cash flow from operating activities ($) 0 178,303,509 182,020,556 186,467,827 190,592,110 194,056,200 195,415,287 191,889,537 192,148,674 208,655,639 214,243,258 219,245,649 223,916,919 227,472,094 227,821,841 221,569,655 236,064,468 247,071,859 253,322,765 259,272,797 264,726,098 268,344,295 267,342,846 265,395,698 286,856,875 295,263,126 302,896,962 310,334,494 316,928,450 320,678,336 1,655,364,791 -Cash flow from investing activities ($) -2,673,110,095 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -Cash flow from financing activities ($) 2,673,110,095 -21,759,769 -22,978,316 -24,265,102 -25,623,948 -27,058,889 -28,574,187 -30,174,341 -31,864,104 -33,648,494 -35,532,810 -37,522,647 -39,623,915 -41,842,855 -44,186,054 -46,660,474 -49,273,460 -52,032,774 -54,946,609 -58,023,619 -61,272,942 -64,704,227 -68,327,663 -72,154,013 -76,194,637 -80,461,537 -84,967,383 -89,725,556 -94,750,188 -100,056,198 -105,659,345 -Total pre-tax cash flow ($) 0 156,543,740 159,042,239 162,202,725 164,968,162 166,997,311 166,841,100 161,715,196 160,284,569 175,007,145 178,710,449 181,723,002 184,293,003 185,629,239 183,635,786 174,909,182 186,791,008 195,039,085 198,376,155 201,249,177 203,453,156 203,640,069 199,015,182 193,241,685 210,662,238 214,801,589 217,929,579 220,608,938 222,178,263 220,622,138 1,549,705,446 +Cash flow from operating activities ($) 0 182,724,931 186,426,650 190,861,418 195,043,962 198,993,360 202,155,947 203,087,139 199,310,625 202,133,754 217,364,476 222,929,121 228,008,174 232,937,185 237,468,810 240,597,294 240,257,648 234,252,419 253,881,327 262,051,556 268,503,357 274,779,118 280,904,625 286,277,092 289,316,276 287,312,106 292,694,985 311,833,886 320,504,276 328,795,689 1,656,778,080 +Cash flow from investing activities ($) -2,639,392,270 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Cash flow from financing activities ($) 2,639,392,270 -21,485,298 -22,688,475 -23,959,029 -25,300,735 -26,717,576 -28,213,760 -29,793,731 -31,462,180 -33,224,062 -35,084,609 -37,049,347 -39,124,111 -41,315,061 -43,628,705 -46,071,912 -48,651,939 -51,376,448 -54,253,529 -57,291,726 -60,500,063 -63,888,067 -67,465,798 -71,243,883 -75,233,540 -79,446,619 -83,895,629 -88,593,785 -93,555,037 -98,794,119 -104,326,589 +Total pre-tax cash flow ($) 0 161,239,633 163,738,175 166,902,389 169,743,227 172,275,784 173,942,186 173,293,409 167,848,445 168,909,692 182,279,867 185,879,774 188,884,063 191,622,124 193,840,105 194,525,382 191,605,709 182,875,972 199,627,798 204,759,829 208,003,294 210,891,051 213,438,827 215,033,209 214,082,736 207,865,487 208,799,356 223,240,101 226,949,239 230,001,570 1,552,451,491 Pre-tax Returns: -Issuance of equity ($) 1,069,244,038 -Total pre-tax cash flow ($) 0 156,543,740 159,042,239 162,202,725 164,968,162 166,997,311 166,841,100 161,715,196 160,284,569 175,007,145 178,710,449 181,723,002 184,293,003 185,629,239 183,635,786 174,909,182 186,791,008 195,039,085 198,376,155 201,249,177 203,453,156 203,640,069 199,015,182 193,241,685 210,662,238 214,801,589 217,929,579 220,608,938 222,178,263 220,622,138 1,549,705,446 -Total pre-tax returns ($) -1,069,244,038 156,543,740 159,042,239 162,202,725 164,968,162 166,997,311 166,841,100 161,715,196 160,284,569 175,007,145 178,710,449 181,723,002 184,293,003 185,629,239 183,635,786 174,909,182 186,791,008 195,039,085 198,376,155 201,249,177 203,453,156 203,640,069 199,015,182 193,241,685 210,662,238 214,801,589 217,929,579 220,608,938 222,178,263 220,622,138 1,549,705,446 +Issuance of equity ($) 1,055,756,908 +Total pre-tax cash flow ($) 0 161,239,633 163,738,175 166,902,389 169,743,227 172,275,784 173,942,186 173,293,409 167,848,445 168,909,692 182,279,867 185,879,774 188,884,063 191,622,124 193,840,105 194,525,382 191,605,709 182,875,972 199,627,798 204,759,829 208,003,294 210,891,051 213,438,827 215,033,209 214,082,736 207,865,487 208,799,356 223,240,101 226,949,239 230,001,570 1,552,451,491 +Total pre-tax returns ($) -1,055,756,908 161,239,633 163,738,175 166,902,389 169,743,227 172,275,784 173,942,186 173,293,409 167,848,445 168,909,692 182,279,867 185,879,774 188,884,063 191,622,124 193,840,105 194,525,382 191,605,709 182,875,972 199,627,798 204,759,829 208,003,294 210,891,051 213,438,827 215,033,209 214,082,736 207,865,487 208,799,356 223,240,101 226,949,239 230,001,570 1,552,451,491 After-tax Returns: -Total pre-tax returns ($) -1,069,244,038 156,543,740 159,042,239 162,202,725 164,968,162 166,997,311 166,841,100 161,715,196 160,284,569 175,007,145 178,710,449 181,723,002 184,293,003 185,629,239 183,635,786 174,909,182 186,791,008 195,039,085 198,376,155 201,249,177 203,453,156 203,640,069 199,015,182 193,241,685 210,662,238 214,801,589 217,929,579 220,608,938 222,178,263 220,622,138 1,549,705,446 -Federal ITC total income ($) 0 801,933,028 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Total pre-tax returns ($) -1,055,756,908 161,239,633 163,738,175 166,902,389 169,743,227 172,275,784 173,942,186 173,293,409 167,848,445 168,909,692 182,279,867 185,879,774 188,884,063 191,622,124 193,840,105 194,525,382 191,605,709 182,875,972 199,627,798 204,759,829 208,003,294 210,891,051 213,438,827 215,033,209 214,082,736 207,865,487 208,799,356 223,240,101 226,949,239 230,001,570 1,552,451,491 +Federal ITC total income ($) 0 791,817,681 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Federal PTC income ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -Federal tax benefit (liability) ($) 0 -23,728,934 -13,361,132 -14,229,685 -15,035,157 -15,711,694 -15,977,123 -15,288,545 -15,339,154 -18,562,964 -19,654,226 -20,631,193 -21,543,492 -22,237,818 -22,306,123 -21,085,072 -23,915,909 -26,065,652 -27,286,454 -28,448,495 -29,513,525 -41,313,900 -52,212,058 -51,831,780 -56,023,148 -57,664,889 -59,155,777 -60,608,327 -61,896,126 -62,628,479 -323,292,744 +Federal tax benefit (liability) ($) 0 -24,732,371 -14,501,509 -15,367,619 -16,184,470 -16,955,788 -17,573,441 -17,755,303 -17,017,749 -17,569,106 -20,543,667 -21,630,442 -22,622,381 -23,585,017 -24,470,043 -25,081,036 -25,014,703 -23,841,882 -27,675,407 -29,271,053 -30,531,090 -42,710,554 -54,860,673 -55,909,916 -56,503,469 -56,112,054 -57,163,331 -60,901,158 -62,594,485 -64,213,798 -323,568,759 State ITC total income ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 State PTC income ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -State tax benefit (liability) ($) 0 -8,504,994 -4,788,936 -5,100,245 -5,388,945 -5,631,431 -5,726,568 -5,479,765 -5,497,905 -6,653,392 -7,044,526 -7,394,693 -7,721,682 -7,970,544 -7,995,026 -7,557,373 -8,572,010 -9,342,528 -9,780,091 -10,196,593 -10,578,324 -14,807,849 -18,713,999 -18,577,699 -20,079,981 -20,668,419 -21,202,787 -21,723,415 -22,184,992 -22,447,484 -115,875,535 -Total after-tax returns ($) -1,069,244,038 926,242,839 140,892,170 142,872,795 144,544,060 145,654,186 145,137,409 140,946,887 139,447,511 149,790,789 152,011,697 153,697,116 155,027,829 155,420,877 153,334,636 146,266,737 154,303,090 159,630,905 161,309,611 162,604,089 163,361,307 147,518,320 128,089,125 122,832,207 134,559,109 136,468,282 137,571,015 138,277,196 138,097,145 135,546,176 1,110,537,167 +State tax benefit (liability) ($) 0 -8,864,649 -5,197,673 -5,508,107 -5,800,885 -6,077,343 -6,298,724 -6,363,908 -6,099,552 -6,297,171 -7,363,321 -7,752,846 -8,108,380 -8,453,411 -8,770,625 -8,989,619 -8,965,843 -8,545,477 -9,919,501 -10,491,417 -10,943,043 -15,308,442 -19,663,324 -20,039,396 -20,252,139 -20,111,847 -20,488,649 -21,828,372 -22,435,299 -23,015,698 -115,974,466 +Total after-tax returns ($) -1,055,756,908 919,460,294 144,038,993 146,026,663 147,757,872 149,242,653 150,070,021 149,174,198 144,731,144 145,043,415 154,372,879 156,496,486 158,153,302 159,583,696 160,599,438 160,454,727 157,625,163 150,488,612 162,032,890 164,997,359 166,529,161 152,872,055 138,914,830 139,083,897 137,327,127 131,641,585 131,147,376 140,510,571 141,919,455 142,772,074 1,112,908,266 -After-tax cumulative IRR (%) NaN -13.37 -0.17 9.73 16.39 20.77 23.68 25.59 26.91 27.91 28.63 29.15 29.54 29.82 30.03 30.17 30.29 30.38 30.45 30.50 30.54 30.57 30.59 30.60 30.61 30.62 30.63 30.63 30.64 30.64 30.66 -After-tax cumulative NPV ($) -1,069,244,038 -260,834,913 -153,510,246 -58,522,284 25,351,382 99,117,119 163,270,187 217,645,265 264,598,067 308,617,272 347,606,124 382,012,228 412,301,287 438,804,091 461,624,797 480,624,234 498,117,711 513,912,899 527,843,647 540,099,738 550,846,462 559,316,379 565,735,149 571,107,423 576,243,900 580,790,538 584,790,831 588,300,142 591,359,021 593,979,443 612,717,451 +After-tax cumulative IRR (%) NaN -12.91 0.65 10.70 17.40 21.79 24.70 26.65 27.96 28.88 29.57 30.08 30.45 30.72 30.92 31.07 31.18 31.26 31.32 31.37 31.41 31.43 31.45 31.47 31.48 31.48 31.49 31.49 31.50 31.50 31.51 +After-tax cumulative NPV ($) -1,055,756,908 -253,267,474 -143,545,715 -46,460,926 39,277,598 114,860,694 181,194,056 238,743,100 287,474,933 330,099,021 369,693,483 404,726,244 435,625,953 462,838,612 486,740,535 507,582,932 525,453,035 540,343,610 554,336,820 566,773,302 577,728,422 586,505,730 593,466,995 599,550,064 604,792,204 609,178,033 612,991,540 616,557,531 619,701,075 622,461,191 641,239,205 AFTER-TAX LCOE AND PPA PRICE -Annual costs ($) -1,069,244,038 527,886,368 -259,962,800 -261,142,662 -262,236,834 -263,155,857 -263,516,423 -262,581,041 -262,649,790 -267,029,088 -268,511,483 -269,838,618 -271,077,906 -272,021,093 -272,113,881 -270,455,176 -274,300,650 -277,220,911 -278,879,276 -280,457,820 -281,904,581 -297,934,481 -312,738,788 -312,222,210 -317,915,861 -320,146,039 -322,171,296 -324,144,473 -325,893,849 -326,888,694 655,574,037 -PPA revenue ($) 0 398,356,471 400,854,971 404,015,457 406,780,894 408,810,043 408,653,832 403,527,928 402,097,301 416,819,877 420,523,180 423,535,733 426,105,735 427,441,971 425,448,518 416,721,913 428,603,740 436,851,817 440,188,887 443,061,909 445,265,887 445,452,800 440,827,914 435,054,417 452,474,970 456,614,321 459,742,311 462,421,669 463,990,994 462,434,870 454,963,130 -Electricity to grid (kWh) 0.0 4,193,226,013 4,219,526,008 4,227,429,703 4,231,130,578 4,227,174,470 4,200,800,078 4,123,944,075 4,085,524,292 4,210,727,110 4,223,816,595 4,229,858,515 4,231,437,288 4,220,815,353 4,177,617,023 4,069,152,556 4,162,009,516 4,218,752,453 4,227,707,328 4,232,132,093 4,230,152,834 4,209,135,409 4,143,119,490 4,067,069,431 4,207,503,902 4,223,608,553 4,230,238,414 4,232,692,624 4,225,013,608 4,189,101,096 4,100,244,505 +Annual costs ($) -1,055,756,908 521,099,309 -256,820,535 -257,997,079 -259,106,708 -260,154,483 -260,993,517 -261,240,562 -260,238,653 -260,987,629 -265,028,340 -266,504,640 -267,852,113 -269,159,780 -270,362,020 -271,192,006 -271,101,898 -269,508,711 -274,716,260 -276,883,822 -278,595,485 -295,140,348 -311,645,349 -313,070,665 -313,876,960 -313,345,254 -314,773,332 -319,850,882 -322,151,136 -324,350,848 643,031,558 +PPA revenue ($) 0 398,360,985 400,859,527 404,023,741 406,864,580 409,397,136 411,063,538 410,414,761 404,969,797 406,031,044 419,401,219 423,001,126 426,005,415 428,743,476 430,961,457 431,646,734 428,727,061 419,997,324 436,749,150 441,881,181 445,124,646 448,012,403 450,560,179 452,154,561 451,204,088 444,986,839 445,920,708 460,361,453 464,070,591 467,122,922 469,876,708 +Electricity to grid (kWh) 0.0 4,193,273,525 4,219,573,970 4,227,516,388 4,232,001,035 4,233,245,126 4,225,570,913 4,194,325,606 4,114,710,394 4,101,737,992 4,212,547,398 4,224,519,385 4,230,441,060 4,233,667,186 4,231,750,368 4,214,888,525 4,163,207,043 4,055,985,743 4,194,671,056 4,220,853,770 4,228,811,003 4,233,321,393 4,234,588,146 4,226,928,684 4,195,686,141 4,116,056,232 4,103,061,353 4,213,834,812 4,225,738,401 4,231,569,184 4,234,649,495 -Present value of annual costs ($) 2,166,141,497 -Present value of annual energy nominal (kWh) 28,322,755,320 -LCOE Levelized cost of energy nominal (cents/kWh) 7.65 +Present value of annual costs ($) 2,140,838,644 +Present value of annual energy nominal (kWh) 28,355,365,264 +LCOE Levelized cost of energy nominal (cents/kWh) 7.55 -Present value of PPA revenue ($) 2,778,858,948 -Present value of annual energy nominal (kWh) 28,322,755,320 +Present value of PPA revenue ($) 2,782,077,849 +Present value of annual energy nominal (kWh) 28,355,365,264 LPPA Levelized PPA price nominal (cents/kWh) 9.81 PROJECT STATE INCOME TAXES -EBITDA ($) 0 268,120,008 270,618,508 273,778,994 276,544,431 278,573,580 278,417,368 273,291,465 271,860,838 286,583,414 290,286,717 293,299,270 295,869,272 297,205,508 295,212,055 286,485,450 298,367,277 306,615,353 309,952,424 312,825,446 315,029,424 315,216,337 310,591,451 304,817,954 322,238,507 326,377,858 329,505,848 332,185,206 333,754,531 332,198,407 1,661,281,714 +EBITDA ($) 0 271,408,511 273,907,053 277,071,267 279,912,106 282,444,662 284,111,065 283,462,287 278,017,323 279,078,570 292,448,745 296,048,652 299,052,941 301,791,002 304,008,984 304,694,260 301,774,587 293,044,850 309,796,677 314,928,707 318,172,172 321,059,929 323,607,705 325,202,088 324,251,614 318,034,365 318,968,234 333,408,979 337,118,117 340,170,448 1,662,620,369 State taxable PBI income ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Interest earned on reserves ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 State taxable IBI income ($) 0 State taxable CBI income ($) 0 minus: -Debt interest payment ($) 0 89,816,499 88,597,952 87,311,166 85,952,321 84,517,380 83,002,082 81,401,927 79,712,164 77,927,774 76,043,459 74,053,621 71,952,353 69,733,414 67,390,214 64,915,795 62,302,808 59,543,495 56,629,659 53,552,649 50,303,327 46,872,042 43,248,605 39,422,256 35,381,631 31,114,732 26,608,886 21,850,712 16,826,081 11,520,070 5,916,923 -Total state tax depreciation ($) 0 56,803,590 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 56,803,590 0 0 0 0 0 0 0 0 0 +Debt interest payment ($) 0 88,683,580 87,480,404 86,209,849 84,868,143 83,451,302 81,955,118 80,375,147 78,706,698 76,944,816 75,084,269 73,119,531 71,044,767 68,853,817 66,540,174 64,096,966 61,516,939 58,792,431 55,915,350 52,877,152 49,668,815 46,280,812 42,703,080 38,924,995 34,935,338 30,722,260 26,273,249 21,575,094 16,613,842 11,374,760 5,842,289 +Total state tax depreciation ($) 0 56,087,086 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 56,087,086 0 0 0 0 0 0 0 0 0 equals: -State taxable income ($) 0 121,499,919 68,413,377 72,860,648 76,984,931 80,449,021 81,808,108 78,282,358 78,541,495 95,048,460 100,636,079 105,638,470 110,309,740 113,864,915 114,214,662 107,962,476 122,457,289 133,464,680 139,715,586 145,665,618 151,118,919 211,540,706 267,342,846 265,395,698 286,856,875 295,263,126 302,896,962 310,334,494 316,928,450 320,678,336 1,655,364,791 +State taxable income ($) 0 126,637,845 74,252,478 78,687,247 82,869,791 86,819,189 89,981,775 90,912,968 87,136,453 89,959,582 105,190,305 110,754,950 115,834,002 120,763,014 125,294,638 128,423,122 128,083,477 122,078,248 141,707,156 149,877,384 156,329,186 218,692,032 280,904,625 286,277,092 289,316,276 287,312,106 292,694,985 311,833,886 320,504,276 328,795,689 1,656,778,080 State income tax rate (frac) 0.0 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 -State tax benefit (liability) ($) 0 -8,504,994 -4,788,936 -5,100,245 -5,388,945 -5,631,431 -5,726,568 -5,479,765 -5,497,905 -6,653,392 -7,044,526 -7,394,693 -7,721,682 -7,970,544 -7,995,026 -7,557,373 -8,572,010 -9,342,528 -9,780,091 -10,196,593 -10,578,324 -14,807,849 -18,713,999 -18,577,699 -20,079,981 -20,668,419 -21,202,787 -21,723,415 -22,184,992 -22,447,484 -115,875,535 +State tax benefit (liability) ($) 0 -8,864,649 -5,197,673 -5,508,107 -5,800,885 -6,077,343 -6,298,724 -6,363,908 -6,099,552 -6,297,171 -7,363,321 -7,752,846 -8,108,380 -8,453,411 -8,770,625 -8,989,619 -8,965,843 -8,545,477 -9,919,501 -10,491,417 -10,943,043 -15,308,442 -19,663,324 -20,039,396 -20,252,139 -20,111,847 -20,488,649 -21,828,372 -22,435,299 -23,015,698 -115,974,466 PROJECT FEDERAL INCOME TAXES -EBITDA ($) 0 268,120,008 270,618,508 273,778,994 276,544,431 278,573,580 278,417,368 273,291,465 271,860,838 286,583,414 290,286,717 293,299,270 295,869,272 297,205,508 295,212,055 286,485,450 298,367,277 306,615,353 309,952,424 312,825,446 315,029,424 315,216,337 310,591,451 304,817,954 322,238,507 326,377,858 329,505,848 332,185,206 333,754,531 332,198,407 1,661,281,714 +EBITDA ($) 0 271,408,511 273,907,053 277,071,267 279,912,106 282,444,662 284,111,065 283,462,287 278,017,323 279,078,570 292,448,745 296,048,652 299,052,941 301,791,002 304,008,984 304,694,260 301,774,587 293,044,850 309,796,677 314,928,707 318,172,172 321,059,929 323,607,705 325,202,088 324,251,614 318,034,365 318,968,234 333,408,979 337,118,117 340,170,448 1,662,620,369 Interest earned on reserves ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -State tax benefit (liability) ($) 0 -8,504,994 -4,788,936 -5,100,245 -5,388,945 -5,631,431 -5,726,568 -5,479,765 -5,497,905 -6,653,392 -7,044,526 -7,394,693 -7,721,682 -7,970,544 -7,995,026 -7,557,373 -8,572,010 -9,342,528 -9,780,091 -10,196,593 -10,578,324 -14,807,849 -18,713,999 -18,577,699 -20,079,981 -20,668,419 -21,202,787 -21,723,415 -22,184,992 -22,447,484 -115,875,535 +State tax benefit (liability) ($) 0 -8,864,649 -5,197,673 -5,508,107 -5,800,885 -6,077,343 -6,298,724 -6,363,908 -6,099,552 -6,297,171 -7,363,321 -7,752,846 -8,108,380 -8,453,411 -8,770,625 -8,989,619 -8,965,843 -8,545,477 -9,919,501 -10,491,417 -10,943,043 -15,308,442 -19,663,324 -20,039,396 -20,252,139 -20,111,847 -20,488,649 -21,828,372 -22,435,299 -23,015,698 -115,974,466 State ITC total income ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 State PTC income ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Federal taxable IBI income ($) 0 Federal taxable CBI income ($) 0 Federal taxable PBI income ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 minus: -Debt interest payment ($) 0 89,816,499 88,597,952 87,311,166 85,952,321 84,517,380 83,002,082 81,401,927 79,712,164 77,927,774 76,043,459 74,053,621 71,952,353 69,733,414 67,390,214 64,915,795 62,302,808 59,543,495 56,629,659 53,552,649 50,303,327 46,872,042 43,248,605 39,422,256 35,381,631 31,114,732 26,608,886 21,850,712 16,826,081 11,520,070 5,916,923 -Total federal tax depreciation ($) 0 56,803,590 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 113,607,179 56,803,590 0 0 0 0 0 0 0 0 0 +Debt interest payment ($) 0 88,683,580 87,480,404 86,209,849 84,868,143 83,451,302 81,955,118 80,375,147 78,706,698 76,944,816 75,084,269 73,119,531 71,044,767 68,853,817 66,540,174 64,096,966 61,516,939 58,792,431 55,915,350 52,877,152 49,668,815 46,280,812 42,703,080 38,924,995 34,935,338 30,722,260 26,273,249 21,575,094 16,613,842 11,374,760 5,842,289 +Total federal tax depreciation ($) 0 56,087,086 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 112,174,171 56,087,086 0 0 0 0 0 0 0 0 0 equals: -Federal taxable income ($) 0 112,994,925 63,624,440 67,760,403 71,595,986 74,817,590 76,081,540 72,802,593 73,043,590 88,395,068 93,591,554 98,243,777 102,588,058 105,894,371 106,219,635 100,405,103 113,885,279 124,122,152 129,935,495 135,469,024 140,540,594 196,732,857 248,628,846 246,817,999 266,776,894 274,594,707 281,694,175 288,611,079 294,743,459 298,230,853 1,539,489,256 +Federal taxable income ($) 0 117,773,196 69,054,805 73,179,140 77,068,905 80,741,845 83,683,051 84,549,060 81,036,901 83,662,411 97,826,983 103,002,103 107,725,622 112,309,603 116,524,014 119,433,504 119,117,633 113,532,771 131,787,655 139,385,967 145,386,143 203,383,590 261,241,301 266,237,696 269,064,137 267,200,258 272,206,336 290,005,514 298,068,976 305,779,991 1,540,803,615 Federal income tax rate (frac) 0.0 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 -Federal tax benefit (liability) ($) 0 -23,728,934 -13,361,132 -14,229,685 -15,035,157 -15,711,694 -15,977,123 -15,288,545 -15,339,154 -18,562,964 -19,654,226 -20,631,193 -21,543,492 -22,237,818 -22,306,123 -21,085,072 -23,915,909 -26,065,652 -27,286,454 -28,448,495 -29,513,525 -41,313,900 -52,212,058 -51,831,780 -56,023,148 -57,664,889 -59,155,777 -60,608,327 -61,896,126 -62,628,479 -323,292,744 +Federal tax benefit (liability) ($) 0 -24,732,371 -14,501,509 -15,367,619 -16,184,470 -16,955,788 -17,573,441 -17,755,303 -17,017,749 -17,569,106 -20,543,667 -21,630,442 -22,622,381 -23,585,017 -24,470,043 -25,081,036 -25,014,703 -23,841,882 -27,675,407 -29,271,053 -30,531,090 -42,710,554 -54,860,673 -55,909,916 -56,503,469 -56,112,054 -57,163,331 -60,901,158 -62,594,485 -64,213,798 -323,568,759 CASH INCENTIVES Federal IBI income ($) 0 @@ -378,30 +376,30 @@ Federal PTC income ($) 0 0 0 State PTC income ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Federal ITC amount income ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -Federal ITC percent income ($) 0 801,933,028 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -Federal ITC total income ($) 0 801,933,028 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Federal ITC percent income ($) 0 791,817,681 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Federal ITC total income ($) 0 791,817,681 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 State ITC amount income ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 State ITC percent income ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 State ITC total income ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DEBT REPAYMENT -Debt balance ($) 1,603,866,057 1,582,106,287 1,559,127,971 1,534,862,869 1,509,238,921 1,482,180,032 1,453,605,845 1,423,431,504 1,391,567,400 1,357,918,906 1,322,386,096 1,284,863,449 1,245,239,534 1,203,396,679 1,159,210,625 1,112,550,151 1,063,276,691 1,011,243,917 956,297,308 898,273,689 837,000,747 772,296,520 703,968,857 631,814,844 555,620,207 475,158,670 390,191,287 300,465,731 205,715,543 105,659,345 0 -Debt interest payment ($) 0 89,816,499 88,597,952 87,311,166 85,952,321 84,517,380 83,002,082 81,401,927 79,712,164 77,927,774 76,043,459 74,053,621 71,952,353 69,733,414 67,390,214 64,915,795 62,302,808 59,543,495 56,629,659 53,552,649 50,303,327 46,872,042 43,248,605 39,422,256 35,381,631 31,114,732 26,608,886 21,850,712 16,826,081 11,520,070 5,916,923 -Debt principal payment ($) 0 21,759,769 22,978,316 24,265,102 25,623,948 27,058,889 28,574,187 30,174,341 31,864,104 33,648,494 35,532,810 37,522,647 39,623,915 41,842,855 44,186,054 46,660,474 49,273,460 52,032,774 54,946,609 58,023,619 61,272,942 64,704,227 68,327,663 72,154,013 76,194,637 80,461,537 84,967,383 89,725,556 94,750,188 100,056,198 105,659,345 -Debt total payment ($) 0 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 +Debt balance ($) 1,583,635,362 1,562,150,064 1,539,461,590 1,515,502,560 1,490,201,825 1,463,484,249 1,435,270,489 1,405,476,758 1,374,014,578 1,340,790,516 1,305,705,907 1,268,656,560 1,229,532,449 1,188,217,387 1,144,588,683 1,098,516,771 1,049,864,832 998,488,384 944,234,855 886,943,129 826,443,066 762,554,999 695,089,201 623,845,318 548,611,777 469,165,159 385,269,529 296,675,745 203,120,708 104,326,589 0 +Debt interest payment ($) 0 88,683,580 87,480,404 86,209,849 84,868,143 83,451,302 81,955,118 80,375,147 78,706,698 76,944,816 75,084,269 73,119,531 71,044,767 68,853,817 66,540,174 64,096,966 61,516,939 58,792,431 55,915,350 52,877,152 49,668,815 46,280,812 42,703,080 38,924,995 34,935,338 30,722,260 26,273,249 21,575,094 16,613,842 11,374,760 5,842,289 +Debt principal payment ($) 0 21,485,298 22,688,475 23,959,029 25,300,735 26,717,576 28,213,760 29,793,731 31,462,180 33,224,062 35,084,609 37,049,347 39,124,111 41,315,061 43,628,705 46,071,912 48,651,939 51,376,448 54,253,529 57,291,726 60,500,063 63,888,067 67,465,798 71,243,883 75,233,540 79,446,619 83,895,629 88,593,785 93,555,037 98,794,119 104,326,589 +Debt total payment ($) 0 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 DSCR (DEBT FRACTION) -EBITDA ($) 0 268,120,008 270,618,508 273,778,994 276,544,431 278,573,580 278,417,368 273,291,465 271,860,838 286,583,414 290,286,717 293,299,270 295,869,272 297,205,508 295,212,055 286,485,450 298,367,277 306,615,353 309,952,424 312,825,446 315,029,424 315,216,337 310,591,451 304,817,954 322,238,507 326,377,858 329,505,848 332,185,206 333,754,531 332,198,407 1,661,281,714 +EBITDA ($) 0 271,408,511 273,907,053 277,071,267 279,912,106 282,444,662 284,111,065 283,462,287 278,017,323 279,078,570 292,448,745 296,048,652 299,052,941 301,791,002 304,008,984 304,694,260 301,774,587 293,044,850 309,796,677 314,928,707 318,172,172 321,059,929 323,607,705 325,202,088 324,251,614 318,034,365 318,968,234 333,408,979 337,118,117 340,170,448 1,662,620,369 minus: Reserves major equipment 1 funding ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Reserves major equipment 2 funding ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Reserves major equipment 3 funding ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Reserves receivables funding ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 equals: -Cash available for debt service (CAFDS) ($) 0 268,120,008 270,618,508 273,778,994 276,544,431 278,573,580 278,417,368 273,291,465 271,860,838 286,583,414 290,286,717 293,299,270 295,869,272 297,205,508 295,212,055 286,485,450 298,367,277 306,615,353 309,952,424 312,825,446 315,029,424 315,216,337 310,591,451 304,817,954 322,238,507 326,377,858 329,505,848 332,185,206 333,754,531 332,198,407 1,661,281,714 -Debt total payment ($) 0 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 111,576,269 -DSCR (pre-tax) 0.0 2.40 2.43 2.45 2.48 2.50 2.50 2.45 2.44 2.57 2.60 2.63 2.65 2.66 2.65 2.57 2.67 2.75 2.78 2.80 2.82 2.83 2.78 2.73 2.89 2.93 2.95 2.98 2.99 2.98 14.89 +Cash available for debt service (CAFDS) ($) 0 271,408,511 273,907,053 277,071,267 279,912,106 282,444,662 284,111,065 283,462,287 278,017,323 279,078,570 292,448,745 296,048,652 299,052,941 301,791,002 304,008,984 304,694,260 301,774,587 293,044,850 309,796,677 314,928,707 318,172,172 321,059,929 323,607,705 325,202,088 324,251,614 318,034,365 318,968,234 333,408,979 337,118,117 340,170,448 1,662,620,369 +Debt total payment ($) 0 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 110,168,878 +DSCR (pre-tax) 0.0 2.46 2.49 2.51 2.54 2.56 2.58 2.57 2.52 2.53 2.65 2.69 2.71 2.74 2.76 2.77 2.74 2.66 2.81 2.86 2.89 2.91 2.94 2.95 2.94 2.89 2.90 3.03 3.06 3.09 15.09 RESERVES Reserves working capital funding ($) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/tests/examples/Fervo_Project_Cape-4.txt b/tests/examples/Fervo_Project_Cape-4.txt index 9bfbb44d..f07c8196 100644 --- a/tests/examples/Fervo_Project_Cape-4.txt +++ b/tests/examples/Fervo_Project_Cape-4.txt @@ -27,7 +27,7 @@ Capital Cost for Power Plant for Electricity Generation, 1900, -- https://better Exploration Capital Cost, 30, -- Estimate significantly higher exploration costs than default correlation in consideration of potential risks associated with second/third/fourth-of-a-kind EGS projects Well Drilling Cost Correlation, 3, -- VERTICAL_LARGE (2025 NREL Geothermal Drilling Cost Curve Update) -Well Drilling and Completion Capital Cost Adjustment Factor, 0.84, -- Adjust correlation-calculated value of $4.72M/well to $3.96M/well per Tim Latimer on 2025-02-12 Volts podcast: less than $4M/well +Well Drilling and Completion Capital Cost Adjustment Factor, 0.8, -- Adjust correlation-calculated value of $4.72M/well to $3.96M/well per Tim Latimer on 2025-02-12 Volts podcast: less than $4M/well Reservoir Stimulation Capital Cost Adjustment Factor, 2.66, -- Estimated cost of ~$2M per well. Typical range for Nth-of-kind projects may be $0.5–2M. Field Gathering System Capital Cost Adjustment Factor, 0.54, -- Gathering costs represent 2% of facilities CAPEX per https://www.linkedin.com/pulse/fervo-energy-technology-day-2024-entering-geothermal-decade-matson-n4stc/ @@ -50,8 +50,8 @@ Reservoir Thermal Conductivity, 3.05 Reservoir Porosity, 0.0118 Reservoir Impedance, 0.001565 -Reservoir Volume Option, 2, -- RES_VOL_FRAC_SEP (Specify reservoir volume and fracture separation) -Reservoir Volume, 5418039158, -- Based on 102 fractures per well +Reservoir Volume Option, 1, -- FRAC_NUM_SEP: Reservoir volume calculated with fracture separation and number of fractures as input +Number of Fractures, 12036, -- 102 fractures per well Fracture Separation, 18, -- Per https://eartharxiv.org/repository/view/7665/, lateral length is 4700 ft = 1432 m. Dividing 1432 by 80 = ~18 m fracture spacing. Fracture Shape, 3, -- Square Fracture Height, 165.3, -- Based on total fracture surface area of 30 million ft^2 per well https://pangea.stanford.edu/ERE/pdf/IGAstandard/SGW/2025/Fercho.pdf @@ -74,9 +74,10 @@ Utilization Factor, .9 Plant Outlet Pressure, 1000 psi, -- https://doi.org/10.31223/X5VH8C Production Wellhead Pressure, 325 psi, -- https://doi.org/10.31223/X5VH8C Circulation Pump Efficiency, 0.80 -Well Geometry Configuration, 4 -Number of Multilateral Sections, 3 -Nonvertical Length per Multilateral Section, 4700 feet + +Well Geometry Configuration, 4, -- L +Number of Multilateral Sections, 0, -- This parameter is set to 0 because, for this case study, the cost of horizontal drilling (which would otherwise account for approximately 118 multilateral sections) is included within the 'vertical drilling cost.' This approach allows us to more directly convey the overall well drilling and completion cost, which is under $4 million. +Nonvertical Length per Multilateral Section, 4700 feet, -- Deployment of Enhanced Geothermal System Technology Leads to Rapid Cost Reductions and Performance Improvements. p. 3. https://doi.org/10.31223/X5VH8C Multilaterals Cased, True # *** SIMULATION PARAMETERS *** diff --git a/tests/geophires_x_client_tests/test_imperative_instantiation_in_subprocess.py b/tests/geophires_x_client_tests/test_imperative_instantiation_in_subprocess.py new file mode 100644 index 00000000..e45810cc --- /dev/null +++ b/tests/geophires_x_client_tests/test_imperative_instantiation_in_subprocess.py @@ -0,0 +1,71 @@ +# ruff: noqa: S603 + +import subprocess +import sys +import tempfile +from pathlib import Path + +from base_test_case import BaseTestCase + + +class GeophiresClientImperativeInstantiationTestCase(BaseTestCase): + + # noinspection PyMethodMayBeStatic + def test_imperative_instantiation_in_subprocess(self): + """ + Verifies that GeophiresXClient can be instantiated at the global scope + in a script without causing a multiprocessing-related RuntimeError. + + This test directly simulates the failure condition by writing and executing + a separate Python script as a subprocess. This ensures that the fix + (checking for 'MainProcess') is working correctly on systems that use + the 'spawn' start method for multiprocessing (like macOS and Windows). + """ + project_root = Path(__file__).parent.parent.resolve() + + script_content = f""" +import sys +# We must add the project root to the path for the import to work. +sys.path.insert(0, r'{project_root}') + +from geophires_x_client import GeophiresXClient + +print("Attempting to instantiate GeophiresXClient at the global scope...") + +# This is the line that would have previously crashed with a RuntimeError. +client = GeophiresXClient() + +print("Instantiation successful.") + +# It is critical to shut down the client to release the manager process, +# otherwise it can linger and interfere with other tests in the suite. +GeophiresXClient.shutdown() + +print("Shutdown successful.") + +# A final message to confirm the script completed without errors. +print("SUCCESS") +""" + + with tempfile.TemporaryDirectory() as tmpdir: + test_script_path = Path(tmpdir) / 'run_client_test.py' + test_script_path.write_text(script_content) + + # fmt:off + result = subprocess.run( + [sys.executable, str(test_script_path)], + capture_output=True, + text=True, + timeout=60 + ) + # fmt:on + + assert result.returncode == 0, ( + f'Subprocess failed with exit code {result.returncode}. This indicates a crash.\\n' + f'--- STDOUT ---\\n{result.stdout}\\n' + f'--- STDERR ---\\n{result.stderr}' + ) + + assert 'SUCCESS' in result.stdout, ( + "Subprocess completed but did not print the final 'SUCCESS' message.\\n" f"--- STDOUT ---\\n{result.stdout}" + ) diff --git a/tests/geophires_x_tests/test_economics_sam.py b/tests/geophires_x_tests/test_economics_sam.py index 54eec89d..7d3bc1f0 100644 --- a/tests/geophires_x_tests/test_economics_sam.py +++ b/tests/geophires_x_tests/test_economics_sam.py @@ -17,11 +17,11 @@ # noinspection PyProtectedMember from geophires_x.EconomicsSam import ( calculate_sam_economics, - _sig_figs, get_sam_cash_flow_profile_tabulated_output, _ppa_pricing_model, _get_fed_and_state_tax_rates, ) +from geophires_x.GeoPHIRESUtils import sig_figs # noinspection PyProtectedMember from geophires_x.EconomicsSamCashFlow import _clean_profile, _is_category_row_label, _is_designator_row_label @@ -459,8 +459,8 @@ def test_is_designator_row_label(self): self.assertTrue(_is_designator_row_label('plus PBI if not available for debt service:')) def test_sig_figs(self): - self.assertListEqual(_sig_figs([1.14, 2.24], 2), [1.1, 2.2]) - self.assertListEqual(_sig_figs((1.14, 2.24), 2), [1.1, 2.2]) + self.assertListEqual(sig_figs([1.14, 2.24], 2), [1.1, 2.2]) + self.assertListEqual(sig_figs((1.14, 2.24), 2), [1.1, 2.2]) def test_get_fed_and_state_tax_rates(self): self.assertEqual(([21], [7]), _get_fed_and_state_tax_rates(0.28)) diff --git a/tests/geophires_x_tests/test_fervo_project_cape_4.py b/tests/geophires_x_tests/test_fervo_project_cape_4.py new file mode 100644 index 00000000..542114c1 --- /dev/null +++ b/tests/geophires_x_tests/test_fervo_project_cape_4.py @@ -0,0 +1,305 @@ +from __future__ import annotations + +import re +from typing import Any + +from base_test_case import BaseTestCase +from geophires_x.GeoPHIRESUtils import sig_figs +from geophires_x.Parameter import HasQuantity +from geophires_x_client import GeophiresInputParameters +from geophires_x_client import GeophiresXClient +from geophires_x_client import GeophiresXResult + + +class FervoProjectCape4TestCase(BaseTestCase): + + def test_fervo_project_cape_4_results_against_reference_values(self): + """ + Asserts that results conform to some of the key reference values claimed in docs/Fervo_Project_Cape-4.md. + """ + + r = GeophiresXClient().get_geophires_result( + GeophiresInputParameters(from_file_path=self._get_test_file_path('../examples/Fervo_Project_Cape-4.txt')) + ) + + min_net_gen = r.result['SURFACE EQUIPMENT SIMULATION RESULTS']['Minimum Net Electricity Generation']['value'] + self.assertGreater(min_net_gen, 500) + self.assertLess(min_net_gen, 505) + + max_total_gen = r.result['SURFACE EQUIPMENT SIMULATION RESULTS']['Maximum Total Electricity Generation'][ + 'value' + ] + self.assertGreater(max_total_gen, 600) + self.assertLess(max_total_gen, 650) + + lcoe = r.result['SUMMARY OF RESULTS']['Electricity breakeven price']['value'] + self.assertGreater(lcoe, 7.5) + self.assertLess(lcoe, 8.5) + + redrills = r.result['ENGINEERING PARAMETERS']['Number of times redrilling']['value'] + self.assertGreater(redrills, 2) + self.assertLess(redrills, 7) + + well_cost = r.result['CAPITAL COSTS (M$)']['Drilling and completion costs per well']['value'] + self.assertLess(well_cost, 4.0) + self.assertGreater(well_cost, 3.0) + + pumping_power_pct = r.result['SURFACE EQUIPMENT SIMULATION RESULTS'][ + 'Initial pumping power/net installed power' + ]['value'] + self.assertGreater(pumping_power_pct, 13) + self.assertLess(pumping_power_pct, 17) + + self.assertEqual( + r.result['SUMMARY OF RESULTS']['Number of production wells']['value'], + r.result['SUMMARY OF RESULTS']['Number of injection wells']['value'], + ) + + def test_case_study_documentation(self): + """ + Parses result values from case study documentation markdown and checks that they match the actual result. + Useful for catching when minor updates are made to the case study which need to be manually synced to the + documentation. + + Note: for future case studies, generate the documentation markdown from the input/result rather than writing + (entirely) by hand so that they are guaranteed to be in sync and don't need to be tested like this, + which has proved messy. + """ + + documentation_file_content = '\n'.join( + self._get_test_file_content('../../docs/Fervo_Project_Cape-4.md', encoding='utf-8') + ) + inputs_in_markdown = self.parse_markdown_inputs_structured(documentation_file_content) + results_in_markdown = self.parse_markdown_results_structured(documentation_file_content) + + self.assertEqual(3.96, results_in_markdown['Well Drilling and Completion Cost']['value']) + self.assertEqual('MUSD/well', results_in_markdown['Well Drilling and Completion Cost']['unit']) + + class _Q(HasQuantity): + def __init__(self, vu: dict[str, Any]): + self.value = vu['value'] + + # https://stackoverflow.com/questions/2280334/shortest-way-of-creating-an-object-with-arbitrary-attributes-in-python + self.CurrentUnits = type('', (), {})() + + self.CurrentUnits.value = vu['unit'] + + capex_q = _Q(results_in_markdown['Project capital costs: Total CAPEX']).quantity() + markdown_capex_USD_per_kW = ( + capex_q.to('USD').magnitude + / _Q(results_in_markdown['Maximum Total Electricity Generation']).quantity().to('kW').magnitude + ) + self.assertAlmostEqual( + sig_figs(markdown_capex_USD_per_kW, 3), results_in_markdown['Project capital costs: $/kW']['value'] + ) + + field_mapping = { + 'LCOE': 'Electricity breakeven price', + 'Project capital costs: Total CAPEX': 'Total CAPEX', + 'Well Drilling and Completion Cost': 'Drilling and completion costs per well', + } + + ignore_keys = ['Project capital costs: $/kW', 'Total fracture surface area per production well'] + + example_result = GeophiresXResult(self._get_test_file_path('../examples/Fervo_Project_Cape-4.out')) + example_result_values = {} + for key, _ in results_in_markdown.items(): + if key not in ignore_keys: + mapped_key = field_mapping.get(key) if key in field_mapping else key + entry = example_result._get_result_field(mapped_key) + if entry is not None and 'value' in entry: + entry['value'] = sig_figs(entry['value'], 3) + + example_result_values[key] = entry + + for ignore_key in ignore_keys: + if ignore_key in results_in_markdown: + del results_in_markdown[ignore_key] + + results_in_markdown['Well Drilling and Completion Cost']['unit'] = results_in_markdown[ + 'Well Drilling and Completion Cost' + ]['unit'].replace('/well', '') + self.assertDictAlmostEqual(example_result_values, results_in_markdown, places=3) + + result_capex_USD_per_kW = ( + _Q(example_result._get_result_field('Total CAPEX')).quantity().to('USD').magnitude + / _Q(example_result._get_result_field('Maximum Total Electricity Generation')).quantity().to('kW').magnitude + ) + self.assertAlmostEqual(sig_figs(result_capex_USD_per_kW, 3), sig_figs(markdown_capex_USD_per_kW, 3)) + + num_doublets = inputs_in_markdown['Number of Doublets']['value'] + self.assertEqual( + example_result.result['SUMMARY OF RESULTS']['Number of production wells']['value'], num_doublets + ) + + num_fracs_per_well = inputs_in_markdown['Number of Fractures per well']['value'] + expected_total_fracs = num_doublets * 2 * num_fracs_per_well + self.assertEqual( + expected_total_fracs, example_result.result['RESERVOIR PARAMETERS']['Number of fractures']['value'] + ) + + self.assertEqual( + example_result.result['RESERVOIR PARAMETERS']['Reservoir volume']['value'], + inputs_in_markdown['Reservoir Volume']['value'], + ) + + def parse_markdown_results_structured(self, markdown_text: str) -> dict: + """ + Parses result values from markdown into a structured dictionary with values and units. + """ + raw_results = {} + table_pattern = re.compile(r'^\s*\|\s*(?!-)([^|]+?)\s*\|\s*([^|]+?)\s*\|', re.MULTILINE) + + try: + results_start_index = markdown_text.index('## Results') + search_area = markdown_text[results_start_index:] + + matches = table_pattern.findall(search_area) + + # Use key_ and value_ to avoid shadowing + for match in matches: + key_ = match[0].strip() + value_ = match[1].strip() + if key_.lower() not in ('metric', 'parameter'): + raw_results[key_] = value_ + except ValueError: + print("Warning: '## Results' section not found.") + return {} + + # Consistency check + special_case_pattern = re.compile(r'LCOE\s*=\s*(\S+)\s*and\s*CAPEX\s*=\s*(\S+)') + special_case_match = special_case_pattern.search(markdown_text) + if special_case_match: + lcoe_text = special_case_match.group(1).rstrip('.,;') + lcoe_table_base = raw_results.get('LCOE', '').split('(')[0].strip() + if lcoe_text != lcoe_table_base: + raise ValueError( + f'LCOE mismatch: Text value ({lcoe_text}) does not match table value ({lcoe_table_base}).' + ) + + # Now, process the raw results into the structured format + structured_results = {} + # Use key_ and value_ to avoid shadowing + for key_, value_ in raw_results.items(): + if key_ in [ + 'After-tax IRR', + 'Average Production Temperature', + 'LCOE', + 'Maximum Total Electricity Generation', + 'Minimum Net Electricity Generation', + 'Number of times redrilling', + 'Project capital costs: Total CAPEX', + 'Project capital costs: $/kW', + 'WACC', + 'Well Drilling and Completion Cost', + ]: + structured_results[key_] = self._parse_value_unit(value_) + + return structured_results + + def parse_markdown_inputs_structured(self, markdown_text: str) -> dict: + """ + Parses all input values from all tables under the '## Inputs' section + of a markdown file into a structured dictionary. + """ + try: + # Isolate the content from "## Inputs" to the next "## " header + sections = re.split(r'(^##\s.*)', markdown_text, flags=re.MULTILINE) + inputs_header_index = next(i for i, s in enumerate(sections) if s.startswith('## Inputs')) + inputs_content = sections[inputs_header_index + 1] + except (StopIteration, IndexError): + print("Warning: '## Inputs' section not found or is empty.") + return {} + + raw_inputs = {} + table_pattern = re.compile(r'^\s*\|\s*(?!-)([^|]+?)\s*\|\s*([^|]+?)\s*\|', re.MULTILINE) + matches = table_pattern.findall(inputs_content) + + for match in matches: + key_ = match[0].strip() + value_ = match[1].strip() + if key_.lower() not in ('parameter', 'metric'): + raw_inputs[key_] = value_ + + structured_inputs = {} + for key_, value_ in raw_inputs.items(): + structured_inputs[key_] = self._parse_value_unit(value_) + + return structured_inputs + + # noinspection PyMethodMayBeStatic + def _parse_value_unit(self, raw_string: str) -> dict: + """ + A helper function to parse a string and extract a numerical value and its unit. + It handles various formats like currency, percentages, text, and scientific notation. + """ + clean_str = re.split(r'\s*\(|,(?!\s*\d)', raw_string)[0].strip() + + # Case 1: LCOE format ($X.X/MWh -> cents/kWh) + match = re.match(r'^\$(\d+\.?\d*)/MWh$', clean_str) + if match: + value = float(match.group(1)) + return {'value': round(value / 10, 2), 'unit': 'cents/kWh'} + + # Case 2: Billion dollar format ($X.XB -> MUSD) + match = re.match(r'^\$(\d+\.?\d*)B$', clean_str) + if match: + value = float(match.group(1)) + return {'value': value * 1000, 'unit': 'MUSD'} + + # Case 3: Million dollar format ($X.XM or $X.XM/unit) + match = re.match(r'^\$(\d+\.?\d*)M(\/.*)?$', clean_str) + if match: + value = float(match.group(1)) + unit_suffix = match.group(2) + unit = 'MUSD' + if unit_suffix: + unit = f'MUSD{unit_suffix}' + return {'value': value, 'unit': unit} + + # Case 4: Dollar per kW format ($X/kW -> USD/kW) + match = re.match(r'^\$(\d+\.?\d*)/kW$', clean_str) + if match: + value = float(match.group(1)) + return {'value': value, 'unit': 'USD/kW'} + + # Case 5: Percentage format (X.X%) + match = re.search(r'(\d+\.?\d*)%$', clean_str) + if match: + value = float(match.group(1)) + return {'value': value, 'unit': '%'} + + # Case 6: Temperature format (X℃ -> degC) + match = re.search(r'(\d+\.?\d*)\s*℃$', clean_str) + if match: + value = float(match.group(1)) + return {'value': value, 'unit': 'degC'} + + # Case 7: Scientific notation format (X.X*10⁶ Y) + match = re.match(r'^(\d+\.?\d*)\s*[×xX]\s*10[⁶6]\s*(.*)$', clean_str) + if match: + base_value = float(match.group(1)) + unit = match.group(2).strip() + return {'value': base_value * 1e6, 'unit': unit} + + # Case 8: Generic number and unit parser + if clean_str.startswith('9⅝'): + parts = clean_str.split(' ') + value = 9.0 + 5.0 / 8.0 + unit = parts[1] if len(parts) > 1 else 'unknown' + return {'value': value, 'unit': unit} + + match = re.search(r'([\d\.,]+)\s*(.*)', clean_str) + if match: + value_str = match.group(1).replace(',', '').replace(' ', '') + unit = match.group(2).strip() + + if '.' in value_str: + value = float(value_str) + else: + value = int(value_str) + + return {'value': value, 'unit': unit if unit else 'count'} + + # Fallback for text-only values + return {'value': clean_str, 'unit': 'text'} diff --git a/tests/test_geophires_x.py b/tests/test_geophires_x.py index caf0e294..5ae240b1 100644 --- a/tests/test_geophires_x.py +++ b/tests/test_geophires_x.py @@ -939,42 +939,3 @@ def test_sbt_coaxial_raises_error(self): ) client.get_geophires_result(params) self.assertIn('SBT with coaxial configuration is not implemented', str(e.exception)) - - def test_fervo_project_cape_4_results_against_reference_values(self): - """ - Asserts that results conform to some of the key reference values claimed in docs/Fervo_Project_Cape-4.md. - """ - - r = GeophiresXClient().get_geophires_result( - GeophiresInputParameters(from_file_path=self._get_test_file_path('examples/Fervo_Project_Cape-4.txt')) - ) - - min_net_gen = r.result['SURFACE EQUIPMENT SIMULATION RESULTS']['Minimum Net Electricity Generation']['value'] - self.assertGreater(min_net_gen, 500) - self.assertLess(min_net_gen, 505) - - max_total_gen = r.result['SURFACE EQUIPMENT SIMULATION RESULTS']['Maximum Total Electricity Generation'][ - 'value' - ] - self.assertGreater(max_total_gen, 600) - self.assertLess(max_total_gen, 650) - - lcoe = r.result['SUMMARY OF RESULTS']['Electricity breakeven price']['value'] - self.assertGreater(lcoe, 7.5) - self.assertLess(lcoe, 8.5) - - redrills = r.result['ENGINEERING PARAMETERS']['Number of times redrilling']['value'] - self.assertGreater(redrills, 2) - self.assertLess(redrills, 7) - - well_cost = r.result['CAPITAL COSTS (M$)']['Drilling and completion costs per vertical production well'][ - 'value' - ] - self.assertLess(well_cost, 4.0) - self.assertGreater(well_cost, 3.0) - - pumping_power_pct = r.result['SURFACE EQUIPMENT SIMULATION RESULTS'][ - 'Initial pumping power/net installed power' - ]['value'] - self.assertGreater(pumping_power_pct, 13) - self.assertLess(pumping_power_pct, 17)