diff --git a/.aiexclude b/.aiexclude new file mode 100644 index 000000000..03bd4129b --- /dev/null +++ b/.aiexclude @@ -0,0 +1 @@ +*.env diff --git a/.bumpversion.cfg b/.bumpversion.cfg index b16532369..d397297b7 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.10.24 +current_version = 3.10.25 commit = True tag = True diff --git a/.cookiecutterrc b/.cookiecutterrc index 56f2be8c4..1efcb17eb 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.10.24 + version: 3.10.25 version_manager: "bump2version" website: "https://github.com/NREL" year_from: "2023" diff --git a/.editorconfig b/.editorconfig index 586c7367d..74879ed01 100644 --- a/.editorconfig +++ b/.editorconfig @@ -18,3 +18,7 @@ indent_size = 2 [*.tsv] indent_style = tab + +[*.jinja] +trim_trailing_whitespace = false +indent_size = unset diff --git a/.gitignore b/.gitignore index a922924be..1b686f1b4 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,11 @@ requirements_2025-08-11.txt .build .cache .eggs + .env +*.env +tests/regenerate-example-result.env + .installed.cfg .ve bin @@ -92,8 +96,10 @@ output/*/index.html # Sphinx/docs docs/_build +docs/temp.txt docs/reference/geophires-request.json docs/reference/parameters.rst +docs/Fervo_Project_Cape-5.md docs/geophires-request.json docs/parameters.rst docs/hip-ra-x-request.json diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0c2f9f01e..b817a7080 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,8 @@ GEOPHIRES v3 (2023-2025) 3.10 ^^^^ +3.10.25: `Add Number of Injection Wells per Production Well parameter `__ + 3.10: `SAM Economic Models: Multiple Construction Years; Number of Fractures per Stimulated Well parameter; Royalty Rate Escalation Start Year parameter `__ | `release `__ 3.9 diff --git a/MANIFEST.in b/MANIFEST.in index 3440150e6..86c6af67b 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,6 +3,7 @@ graft src graft ci graft tests +include .aiexclude include .bumpversion.cfg include .cookiecutterrc include .coveragerc diff --git a/README.rst b/README.rst index ad9530116..2fc6a6a53 100644 --- a/README.rst +++ b/README.rst @@ -58,9 +58,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.10.24.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.10.25.svg :alt: Commits since latest release - :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.10.24...main + :target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.10.25...main .. |docs| image:: https://readthedocs.org/projects/GEOPHIRES-X/badge/?style=flat :target: https://nrel.github.io/GEOPHIRES-X @@ -168,6 +168,10 @@ Example-specific web interface deeplinks are listed in the Link column. - Input file - Case report file - Link + * - Case Study: 500 MWe EGS modeled on Fervo Cape Station (`documentation `__) + - `Fervo_Project_Cape-4.txt `__ + - `.out `__ + - `link `__ * - Example 1: EGS Electricity - `example1.txt `__ - `.out `__ @@ -288,10 +292,10 @@ Example-specific web interface deeplinks are listed in the Link column. - `Fervo_Project_Cape-3.txt `__ - `.out `__ - `link `__ - * - Case Study: 500 MWe EGS Project Modeled on Fervo Cape Station (`documentation `__) - - `Fervo_Project_Cape-4.txt `__ - - `.out `__ - - `link `__ + * - 100 MWe EGS modeled on Fervo Cape Station Phase I + - `Fervo_Project_Cape-5.txt `__ + - `.out `__ + - `link `__ * - Superhot Rock (SHR) Example 1 - `example_SHR-1.txt `__ - `.out `__ diff --git a/docs/Fervo_Project_Cape-4.md b/docs/Fervo_Project_Cape-4.md index 4c0c3f8ea..9aa975219 100644 --- a/docs/Fervo_Project_Cape-4.md +++ b/docs/Fervo_Project_Cape-4.md @@ -1,4 +1,9 @@ -# Case Study: 500 MWe EGS Project Modeled on Fervo Cape Station +# [Deprecated] Case Study: 500 MWe EGS Project Modeled on Fervo Cape Station + +**⚠️ This is a previous version of the case study. The case study has been updated since the release of this version.** +[Click here](Fervo_Project_Cape-5.html) to find the latest version. + +--- The GEOPHIRES example `Fervo_Project_Cape-4` is a case study of a 500 MWe EGS Project modeled on Fervo Cape Station with its April 2025-announced diff --git a/docs/Fervo_Project_Cape-5.md.jinja b/docs/Fervo_Project_Cape-5.md.jinja new file mode 100644 index 000000000..ba7e5a335 --- /dev/null +++ b/docs/Fervo_Project_Cape-5.md.jinja @@ -0,0 +1,386 @@ +# GEOPHIRES Case Study: 500 MW EGS modeled on Fervo Cape Station + +## Introduction + +The GEOPHIRES example `Fervo_Project_Cape-5` is a case study of a 500 MWe EGS project modeled +on Phases I and II of [Fervo Energy's Cape Station](https://capestation.com/). + +Key case study results include LCOE = {{ '$' ~ lcoe_usd_per_mwh ~ '/MWh' }} and IRR = {{ irr_pct ~ '%' }}. + +[Click here](https://gtp.scientificwebservices.com/geophires/?geophires-example-id=Fervo_Project_Cape-5) to +interactively explore the case study in the GEOPHIRES web interface. + +### Modeling Overview: A Conservative, Second-of-a-Kind Analog + +This case study models a 500 MWe Enhanced Geothermal System (EGS) project designed to represent a "Second-of-a-Kind" ( +SOAK) deployment. +Rather than serving as an exact facsimile of Cape Station as built, this study estimates what a non-Fervo developer +could achieve on a geologically identical site, relying primarily on publicly available data and standardized +engineering estimates. +The model assumes the developer is a "fast follower": benefiting from the proof-of-concept established by Cape Station +Phase I but operating without access to Fervo’s private supply chain or proprietary optimization data. + +**Public Data Reliance:** Inputs utilize exact values for publicly available parameters, such as geothermal +gradient and reservoir density. +Where data is proprietary, values are inferred from public announcements or extrapolated from standard industry +correlations. + +**Conservative Constraints:** To ensure the model serves as a robust feasibility test, some inputs are intentionally +conservative compared to Fervo’s stated targets, such as drilling costs and water loss. + +**Fast Follower Advantage:** By entering the market after Fervo’s initial de-risking campaigns, the modeled developer +avoids the high "tuition costs" of early experimentation. +For example, while Fervo’s initial drilling costs at Cape Station ranged +from [$9.4M down to $4.8M per well](https://houston.innovationmap.com/fervo-energy-drilling-utah-project-2667300142.html) +as they climbed the learning curve, +this model assumes a developer can bypass those initial high-cost outliers, +instead initiating their campaign at a stabilized commercial baseline (modeled here +at {{ '$' ~ drilling_costs_per_well_musd ~ 'M/well' }}, aligned with the NREL ATB and 2025 cost curves). +This reflects a developer who capitalizes on established industry knowledge to skip the "First-of-a-Kind" (FOAK) +premiums but has not yet achieved the fully optimized learning rates of a mature "Nth-of-a-kind" operator. + +### Intended Use Cases + +This case study is designed to function as a public utility for the geothermal sector, serving two primary roles: + +**Industry Benchmark:** By relying primarily on verifiable public data and independent expert consensus, this model +establishes a transparent baseline for EGS viability. +It tests the premise that Fervo’s success at Cape Station is a replicable standard for the next-generation geothermal +industry. +The results serve as reference points for what is achievable using current technology in high-grade resources. + +**Template for Resource Assessment & Custom Modeling:** The example input file (`Fervo_Project_Cape-5.txt`) is intended +as customizable template for modeling other resources. +Users can input local geologic data (gradient, rock properties) into this template to evaluate how a Cape Station-style +design would perform in different geographies (e.g., Nevada vs. Utah vs. International). +Different plant sizes and performance targets can be modeled by adjusting the number of production wells, fractures per well, +and other technical & engineering parameters. +The model allows users to stress-test economic assumptions, such as the PPA price or Investment Tax Credit (ITC), to see +how policy changes impact the feasibility of replicating this design elsewhere. + +## Methodology + +The Inputs and Results tables document key assumptions, inputs, and a comparison of results with reference +values. +Note that these are not the exhaustive sets of inputs and results, which are available in source code and +the [web interface](https://gtp.scientificwebservices.com/geophires/?geophires-example-id=Fervo_Project_Cape-5). + +### Inputs + +See [Fervo_Project_Cape-5.txt](https://github.com/softwareengineerprogrammer/GEOPHIRES/blob/main/tests/examples/Fervo_Project_Cape-5.txt) +in source code for the full set of inputs. + +#### Reservoir Parameters + +{{ reservoir_parameters_table_md }} + +#### Well Bores Parameters + +{{ well_bores_parameters_table_md }} + +#### Surface Plant Parameters + +{{ surface_plant_parameters_table_md }} + +#### Construction Parameters + +{{ construction_parameters_table_md }} + + +#### Economic Parameters + +{{ economics_parameters_table_md }} + +### Calibration with Fervo-implemented Field Design + +[Designing the Record-Breaking Enhanced Geothermal System at Project Cape](https://www.resfrac.com/wp-content/uploads/2025/06/Singh-2025-Fervo-Project-Cape.pdf) (Singh et al., 2025) +describes reservoir modeling (ResFrac) that informed the Cape Station field implementation[^field-implementation-configuration-note]. + +[^field-implementation-configuration-note]: Note on Configuration: While the specific Bearskin and Gold pads (Phase II) utilize an inverted 2:3 ratio (3 injectors for 2 producers), this case study assumes the 3:2 ratio identified in the paper's optimization studies ("Study 1") represents the standard repeating module for the full-scale 400+ MWe system. The higher injector count in Phase II is interpreted as a transient requirement for field delineation and initial pressure support (boundary conditions) rather than the long-term commercial standard. + +An equivalent GEOPHIRES simulation was run using the case study's reservoir engineering parameters, with the following modifications to align with Singh et al.'s modeling scenario: + +{{ reservoir_engineering_reference_simulation_params_table_md }} + +The following table compares the average production temperature profile from the "700 ft bench spacing" scenario in Singh et al. with the GEOPHIRES simulation. +Note that both figures show temperature in Fahrenheit rather than Celsius. + +{# @formatter:off #} +| Fervo-implemented Design Simulation (Fig. 18.) | Case Study Equivalent GEOPHIRES Simulation | +|---|---| +| | | +{# @formatter:on #} + +While the initial and final (Year 15) temperatures are consistent, the production curves exhibit distinct profiles due to the different modeling approaches: + +1. Reference Simulation (Left): The Singh et al. (2025) curve reflects a fully coupled numerical simulation (ResFrac) that accounts for complex fracture heterogeneity, inter-well interference, and variable flow paths. The gradual decline starting around Year 3 indicates thermal dispersion, where cold injection fluid mixes with hot reservoir fluid along faster flow paths earlier in the project life. +1. GEOPHIRES Simulation (Right): The GEOPHIRES result utilizes the Gringarten (1975) analytical solution for flow in fractured rock. This model assumes a uniform thermal sweep across an idealized fracture surface. Consequently, it maintains a flat, maximum production temperature for a longer duration until the cold front reaches the production well (thermal breakthrough), resulting in a sharper, later decline. + +Despite these structural differences, the comparison validates the basis for the case study's reservoir engineering parameters, as the aggregate heat extraction and year-15 endpoint align closely with the numerical simulation baseline. + + +## Results + +See [Fervo_Project_Cape-5.out](https://github.com/softwareengineerprogrammer/GEOPHIRES/blob/main/tests/examples/Fervo_Project_Cape-5.out) +in source code for the complete results. + +### Economic Results + +| Metric | Result Value | Reference Value(s) | Reference Source | +|---------------|---------------------------------------------|--------------------------------------------------|------------------| +| LCOE | {{ '$' ~ lcoe_usd_per_mwh ~ '/MWh' }} | \$80/MWh | Horne et al, 2025. | +| After-tax IRR | {{ irr_pct ~ '%' }} | 15–25% | Typical levered returns for energy projects | +| Project NPV | {{ '$' ~ npv_musd ~ 'M' }} | | .. N/A | +{# Note that the '.. N/A' entry in the last row is required for the table to render in HTML (presumable m2r2/sphinx build issue) #} + +### Capital Costs + +{# @formatter:off #} +| Metric | Result Value | Reference Value(s) | Reference Source | +|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------|--------------------------------------------------|------------------| +| WACC | {{ wacc_pct ~ '%' }} | 8.3% | Fervo's target goal is to eventually achieve a "Solar Standard" WACC of 8.3% (Matson, 2024). | +| Exploration Costs | {{ '$' ~ exploration_cost_musd ~ 'M' }} | {{ '$' ~ drilling_costs_per_well_musd*5 ~ 'M' }} | 2024b ATB NF-EGS conservative scenario exploration assumption of 5 full-size wells (NREL, 2025). Case study result conservatively includes additional costs for geophysical survey, indirect costs, and contingency. | +| Well Drilling and Completion Costs | {{ '$' ~ drilling_costs_musd ~ 'M' }} total ({{ '$' ~ drilling_costs_per_well_musd ~ 'M/well' }}) | $<4M/well | Latimer, 2025. | +| Stimulation Costs | {{ '$' ~ stim_costs_musd ~ 'M' }} total ({{ '$' ~ stim_costs_per_well_musd ~ 'M/well' }}) | $4.65M/well | Based on 46%:54% drilling:stimulation cost ratio (Yusifov & Enriquez, 2025). | +| Surface Power Plant Costs | {{ '$' ~ surface_power_plant_costs_gusd ~ 'B' }} | | | +| Field Gathering System Costs | {{ '$' ~ field_gathering_cost_musd ~ 'M' }} ({{ field_gathering_cost_pct_occ ~ '%' }} of OCC) | 2% of OCC | Matson, 2024. | +| Overnight Capital Cost | {{ '$' ~ occ_gusd ~ 'B' }} | | | +| Total CAPEX | {{ '$' ~ total_capex_gusd ~ 'B' }} (OCC + interest and inflation during construction) | | | +| Total CAPEX: $/kW | {{ '$' ~ capex_usd_per_kw ~ '/kW' }} (based on maximum net electricity generation) | $5000/kW; $4500/kW; $3000–$6000/kW | McClure, 2024; Horne et al, 2025; Latimer, 2025. | +{# @formatter:on #} + + +### Technical & Engineering Results + +{# @formatter:off #} +| Metric | Result Value | Reference Value(s) | Reference Source | +|--------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|---| +| Total fracture surface area per well | {{ total_fracture_surface_area_per_well_mm2 }}×10⁶ m² ({{ total_fracture_surface_area_per_well_mft2 }} million ft²) | Project Red: 2.787×10⁶ m² (30 million ft²) | Greater fracture surface area expected than Project Red (Fercho et al, 2025). | +| Reservoir Volume | {{ reservoir_volume_m3 }} m³ | | Calculated from fracture area × fracture separation × number of fractures per well × number of wells | +| Bottom-hole Temperature (BHT) | {{ bht_temp_degc ~ '℃' }} | 200–241℃ | Fercho et al., 2024; Singh et al., 2025. | +| Initial Production Temperature | {{ initial_production_temperature_degc ~ '℃' }} | 196–208℃ | Approximate range of initial production temperatures between shallower and deeper producers (Singh et al., 2025).| +| Average Production Temperature | {{ average_production_temperature_degc ~ '℃' }} | 199–209℃ | Approximate range of thermally conditioned production temperatures between shallower and deeper producers (Singh et al., 2025). | +| Maximum Total Electricity Generation | {{ max_total_generation_mwe }} MW | 600 MW | Combined nameplate capacity of 10×60 MWe Gen 2 ORCs. A total of 8×60 MWe Gen 2 ORCs have been announced for Phase II; 3 from Turboden and 5 from Baker Hughes (Turboden, 2025; Jacobs, 2025). This equates to 480 MW gross capacity for Phase II's 400 MW net capacity. An equivalent SOAK 500 MW project would therefore require 10 Gen 2 ORC units. (Note that the modular Gen 2 ORCs are not individually modeled in this case study, and are assumed to be combined into a single power plant). | +| Minimum Net Electricity Generation | {{ min_net_generation_mwe }} MW | 500 MW | The announced 500 MWe capacity (Fervo Energy, 2025) is interpreted to mean that the PPA penalizes Cape Station if net electricity generation falls below 500 MWe. | +| 2-year Average Net Power Production per Production Well | {{ two_year_avg_net_power_mwe_per_production_well }} MW | 7.6–11.5 MW | Figures 4 and 12 (Singh et al., 2025). | +| Injection Pumping Parasitic Load (Average Pumping Power/Average Total Electricity Generation) | {{ parasitic_loss_pct ~ '%' }} | Upper bound: 16.7% | Procurement of 480 MW of Gen 2 ORC units for 400 MW net capacity in Phase II allows for up to 16.7% total on-site consumption (80 MW; including injection pumping power). | +| Average Net Electricity Generation | {{ avg_net_generation_mwe }} MW | | | +| Maximum Net Electricity Generation | {{ max_net_generation_mwe}} MW | | | +| Number of times redrilling | {{ number_of_times_redrilling }} | 2–5 | Redrilling expected to be required within 5–10 years of project start | +| Total wells drilled over project lifetime | {{ total_wells_including_redrilling }} | 320 | Total wells permitted by environmental assessment (BLM, 2024). | +{# @formatter:on #} + + +### Sensitivity Analysis + +The following charts show the sensitivity of key metrics to various inputs. +Each chart shows the sensitivity of a single metric, such as LCOE, to the set of tested input values. +The leftmost chart column shows the parameter being tested and its baseline case study input value in parentheses. +The bars for each row show the deltas of the metric value from the baseline case study value for the values tested for +that parameter. +Green bars indicate favorable outcomes, such as lower LCOE or higher IRR, while gray bars indicate unfavorable outcomes, +such as higher LCOE or lower IRR. + +Click the bars to view the sensitivity analysis result for the input value in the web interface. + +### LCOE + +.. raw:: html + + + + + + LCOE Sensitivity Analysis Chart + +#### Impact of PPA Price on LCOE + +The sensitivity analysis reveals a positive correlation between the Power Purchase +Agreement (PPA) price and the Levelized Cost of Electricity (LCOE). While counterintuitive, this is a function of SAM +Economic Models treating federal and state income taxes as operating cash outflows. + +In SAM Economic Models, the PPA price is a fixed input that determines project revenue. A higher PPA price generates +higher taxable income, which in turn increases the project's annual income tax liability (a negative cash flow). Because +the LCOE calculation aggregates all lifetime project costs—including the tax burden—the additional tax costs incurred +from higher revenues result in a higher calculated LCOE. Conversely, a lower PPA price reduces taxable income, lowers +tax liability, and decreases the resulting LCOE. + +### IRR + +.. raw:: html + + + + IRR Sensitivity Analysis Chart + +### NPV + +.. raw:: html + + + + NPV Sensitivity Analysis Chart + +Users may wish to perform their own sensitivity analysis +using [GEOPHIRES's Monte Carlo simulation module](Monte-Carlo-User-Guide.html) or other data analysis tools. + +### Impact of Flow Rate on Project Economics + +Higher flow rate per production well does not necessarily result in improved project economics (e.g. lower LCOE or higher IRR). +Higher flow rates result in increased generation in the short term, but also cause faster thermal decline. +Additional make-up wells may need to be drilled to compensate for increased thermal decline and maintain a minimum net generation (redrilling), +the cost of which may offset incremental revenue from increased generation. +This tradeoff was considered in reservoir modeling that guided Fervo's field implementation (Singh et al., 2025). + +## Phase I 100 MWe Model + +{# TODO clarify it is SOAK and not as-built #} + +The case study also includes a 100 MWe Phase I model, `Fervo_Project_Cape-6`. +[Click here](https://gtp.scientificwebservices.com/geophires/?geophires-example-id=Fervo_Project_Cape-6) to view the +Phase I model in the GEOPHIRES web interface. + +## Previous Versions + +Documentation is available for the following previous case study versions, which are deprecated in favor of the current version: + +1. [Fervo_Project_Cape-4](Fervo_Project_Cape-4.html) + +{# TODO others e.g. Fervo_Project_Cape-3... #} + +--- + +## References + +Akindipe, D. and Witter. E. (2025). "2025 Geothermal Drilling Cost Curves +Update". https://pangea.stanford.edu/ERE/db/GeoConf/papers/SGW/2025/Akindipe.pdf?t=1740084555 + +Baytex Energy. (2024). Eagle Ford Presentation. +https://www.baytexenergy.com/content/uploads/2024/04/24-04-Baytex-Eagle-Ford-Presentation.pdf + +Beckers, K., McCabe, K. (2019) GEOPHIRES v2.0: updated geothermal techno-economic simulation tool. Geotherm Energy +7,5. https://doi.org/10.1186/s40517-019-0119-6 + +Fercho, S., Matson, G., McConville, E., Rhodes, G., Jordan, R., Norbeck, J.. (2024, February 12). +Geology, Temperature, Geophysics, Stress Orientations, and Natural Fracturing in the Milford +Valley, UT Informed by the Drilling Results of the First Horizontal Wells at the Cape Modern +Geothermal Project. https://pangea.stanford.edu/ERE/db/GeoConf/papers/SGW/2024/Fercho.pdf + +Fercho, S., Norbeck, J., Dadi, S., Matson, G., Borell, J., McConville, E., Webb, S., Bowie, C., & Rhodes, G. (2025). +Update on the geology, temperature, fracturing, and resource potential at the Cape Geothermal Project informed by data +acquired from the drilling of additional horizontal EGS wells. Proceedings of the 50th Workshop on Geothermal Reservoir +Engineering, Stanford University, Stanford, CA. https://pangea.stanford.edu/ERE/pdf/IGAstandard/SGW/2025/Fercho.pdf + +Fervo Energy. (2023, September 19). Fervo’s Commercialization Plans for Enhanced Geothermal Systems ( +EGS). https://egi.utah.edu/wp-content/uploads/2023/09/09.45-Emma-McConville-Fervo_EGI_Sept-19-2023.pdf + +Fervo Energy. (2023, September 25). Fervo Energy Breaks Ground on the World’s Largest Next-gen Geothermal Project. +https://fervoenergy.com/fervo-energy-breaks-ground-on-the-worlds-largest-next-gen-geothermal-project/ + +Fervo Energy. (2024, September 10). Fervo Energy’s Record-Breaking Production Results Showcase Rapid Scale Up of +Enhanced +Geothermal. https://www.businesswire.com/news/home/20240910997008/en/Fervo-Energys-Record-Breaking-Production-Results-Showcase-Rapid-Scale-Up-of-Enhanced-Geothermal + +Fervo Energy. (2025, March 31). Geothermal Mythbusting: Water Use and +Impacts. https://fervoenergy.com/geothermal-mythbusting-water-use-and-impacts/ + +Fervo Energy. (2025, April 15). Fervo Energy Announces 31 MW Power Purchase Agreement with Shell +Energy. https://fervoenergy.com/fervo-energy-announces-31-mw-power-purchase-agreement-with-shell-energy/ + +Fervo Energy (2025, June 11). Fervo Energy Secures $206 Million In New Financing To Accelerate Cape Station Development. +https://fervoenergy.com/fervo-secures-new-financing-to-accelerate-development/ + +Gradl, C. (2018). Review of Recent Unconventional Completion Innovations and their Applicability to EGS Wells. Stanford +Geothermal Workshop. +https://pangea.stanford.edu/ERE/pdf/IGAstandard/SGW/2018/Gradl.pdf + +Horne, R., Genter, A., McClure, M. et al. (2025) Enhanced geothermal systems for clean firm energy generation. Nat. Rev. +Clean Technol. 1, 148–160. https://doi.org/10.1038/s44359-024-00019-9 + +Jacobs, Trent. (2024, September 16). Fervo and FORGE Report Breakthrough Test Results, Signaling More Progress for +Enhanced +Geothermal. https://jpt.spe.org/fervo-and-forge-report-breakthrough-test-results-signaling-more-progress-for-enhanced-geothermal + +Jacobs, Trent. (2025, September 5). Baker Hughes Nabs Award for Next Phase of Fervo Energy's Geothermal Power Plant in +Utah. +https://jpt.spe.org/baker-hughes-nabs-award-for-next-phase-of-fervo-energygeothermal-power-plant-in-utah + +Ko, S., Ghassemi, A., & Uddenberg, M. (2023). Selection and Testing of Proppants for EGS. +Proceedings, 48th Workshop on Geothermal Reservoir Engineering, Stanford University, Stanford, California. +https://pangea.stanford.edu/ERE/db/GeoConf/papers/SGW/2023/Ko.pdf + +Latimer, T. (2025, February 12). Catching up with enhanced geothermal (D. Roberts, +Interviewer). https://www.volts.wtf/p/catching-up-with-enhanced-geothermal + +Matson, M. (2024, September 11). Fervo Energy Technology Day 2024: Entering "the Geothermal Decade" with Next-Generation +Geothermal +Energy. https://www.linkedin.com/pulse/fervo-energy-technology-day-2024-entering-geothermal-decade-matson-n4stc/ + +McClure, M. (2024, September 12). Digesting the Bonkers, Incredible, Off-the-Charts, Spectacular Results from the Fervo +and FORGE Enhanced Geothermal Projects. ResFrac Corporation Blog. +https://www.resfrac.com/blog/digesting-the-bonkers-incredible-off-the-charts-spectacular-results-from-the-fervo-and-forge-enhanced-geothermal-projects + +NCEI. US Climate +Normals. https://www.ncei.noaa.gov/access/us-climate-normals/#dataset=normals-annualseasonal&timeframe=30&station=USC00425654 + +NREL. (2024). Annual Technology Baseline: Geothermal (2024). +https://atb.nrel.gov/electricity/2024/geothermal + +NREL. (2025, February 26). Annual Technology Baseline: Geothermal (2024b). +https://atb.nrel.gov/electricity/2024b/geothermal + +Norbeck, J., Gradl, C., Latimer, T. (2024, September 10). Deployment of Enhanced Geothermal System Technology Leads to +Rapid Cost Reductions and Performance Improvements. https://doi.org/10.31223/X5VH8C + +Norbeck J., Latimer T. (2023). Commercial-Scale Demonstration of a First-of-a-Kind Enhanced Geothermal +System. https://doi.org/10.31223/X52X0B + +Quantum Proppant Technologies. (2020). Well Completion Technology. World +Oil. https://quantumprot.com/uploads/images/2b8583e8ce8038681a19d5ad1314e204.pdf + +Shiozawa, S., & McClure, M. (2014). EGS Designs with Horizontal Wells, Multiple Stages, and Proppant. ResFrac. +https://www.resfrac.com/wp-content/uploads/2024/07/Shiozawa.pdf + +Singh, A., Galban, G., McClure, M. (2025, June 9). +Proceedings of the 2025 Unconventional Resources Technology Conference. +https://www.resfrac.com/wp-content/uploads/2025/06/Singh-2025-Fervo-Project-Cape.pdf + +Southern Utah University. (2024, October 23). Fervo Energy, Southern Utah University, and Elemental Impact Launch +Geothermal Drilling & Completions Apprenticeship Program. +https://www.suu.edu/news/2024/10/geothermal-energy-joint-campaign.html + +Turboden. (2025, October 2). Turboden selected to deliver 180 MW of Fervo’s Gen 2 ORC Power Plants at Cape Station in +Utah. https://www.turboden.com/company/media/press/press-releases/4881/turboden-selected-to-deliver-180-mw-of-fervos-gen-2-orc-power-plants-at-cape-station-in-utah + +U.S. Department of the Interior Bureau of Land Management. (2024, October). +Finding of No Significant Impact and Decision Record DOI-BLM-UT-C010-2024-0018-EA. +https://eplanning.blm.gov/public_projects/2033002/200625761/20120795/251020775/DOI-BLM-UT-C010-2024-0018-EA_FONSI_DR_%20Fervo%20EA_signed.pdf + +US DOE. (2021). Combined Heat and Power Technology Fact Sheet Series: Waste Heat to +Power. https://betterbuildingssolutioncenter.energy.gov/sites/default/files/attachments/Waste_Heat_to_Power_Fact_Sheet.pdf + +Xing, P., England, K., Moore, J., McLennan, J. (2025, February 10). +Analysis of the 2024 Circulation Tests at Utah FORGE and the Response of Fiber Optic Sensing +Data. +https://pangea.stanford.edu/ERE/pdf/IGAstandard/SGW/2025/Xing2.pdf + +Yearsley, E., Kombrink, H. (2024, November 6). +A critical look at Fervo dataset suggests lower output. +https://geoexpro.com/a-critical-look-at-fervo-dataset-suggests-lower-output/ + +Yusifov, M., & Enriquez, N. (2025, July). From Core to Code: Powering the Al Revolution with Geothermal Energy. +Project InnerSpace. https://projectinnerspace.org/resources/Powering-the-AI-Revolution.pdf + +--- diff --git a/docs/GEOPHIRES-Examples.md b/docs/GEOPHIRES-Examples.md index 02ac82876..cb549abb9 100644 --- a/docs/GEOPHIRES-Examples.md +++ b/docs/GEOPHIRES-Examples.md @@ -7,4 +7,4 @@ or in the [web interface](https://gtp.scientificwebservices.com/geophires) under ## Case Study: 500 MWe EGS Project Modeled on Fervo Cape Station -See [Case Study: 500 MWe EGS Project Modeled on Fervo Cape Station](Fervo_Project_Cape-4.html). +See documentation: [Case Study: 500 MWe EGS Project Modeled on Fervo Cape Station](Fervo_Project_Cape-5.html). diff --git a/docs/Monte-Carlo-User-Guide.md b/docs/Monte-Carlo-User-Guide.md index 816de8c76..54dc6b61e 100644 --- a/docs/Monte-Carlo-User-Guide.md +++ b/docs/Monte-Carlo-User-Guide.md @@ -1,4 +1,4 @@ -# GEOPHIRES Monte Carlo User Guide +# Monte Carlo User Guide ## Example Setup diff --git a/docs/SAM-Economic-Models.md b/docs/SAM-Economic-Models.md index b71470518..326679507 100644 --- a/docs/SAM-Economic-Models.md +++ b/docs/SAM-Economic-Models.md @@ -121,9 +121,9 @@ Output Parameters: ### Case Study: 500 MWe EGS Project Modeled on Fervo Cape Station -[Web interface link](https://gtp.scientificwebservices.com/geophires/?geophires-example-id=Fervo_Project_Cape-4) +[Web interface link](https://gtp.scientificwebservices.com/geophires/?geophires-example-id=Fervo_Project_Cape-5) -See [Case Study: 500 MWe EGS Project Modeled on Fervo Cape Station](Fervo_Project_Cape-4.html). +Documentation: [Case Study: 500 MWe EGS Project Modeled on Fervo Cape Station](Fervo_Project_Cape-4.html). ### SAM Single Owner PPA diff --git a/docs/_images/fervo_project_cape-5-sensitivity-analysis-irr.png b/docs/_images/fervo_project_cape-5-sensitivity-analysis-irr.png new file mode 100644 index 000000000..7fa387d25 Binary files /dev/null and b/docs/_images/fervo_project_cape-5-sensitivity-analysis-irr.png differ diff --git a/docs/_images/fervo_project_cape-5-sensitivity-analysis-irr.svg b/docs/_images/fervo_project_cape-5-sensitivity-analysis-irr.svg new file mode 100644 index 000000000..1abab3461 --- /dev/null +++ b/docs/_images/fervo_project_cape-5-sensitivity-analysis-irr.svg @@ -0,0 +1,2952 @@ + + + + + + + + 2026-01-14T11:34:35.314403 + image/svg+xml + + + Matplotlib v3.10.8, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_images/fervo_project_cape-5-sensitivity-analysis-lcoe.png b/docs/_images/fervo_project_cape-5-sensitivity-analysis-lcoe.png new file mode 100644 index 000000000..35f5a7567 Binary files /dev/null and b/docs/_images/fervo_project_cape-5-sensitivity-analysis-lcoe.png differ diff --git a/docs/_images/fervo_project_cape-5-sensitivity-analysis-lcoe.svg b/docs/_images/fervo_project_cape-5-sensitivity-analysis-lcoe.svg new file mode 100644 index 000000000..4a15cdb75 --- /dev/null +++ b/docs/_images/fervo_project_cape-5-sensitivity-analysis-lcoe.svg @@ -0,0 +1,3129 @@ + + + + + + + + 2026-01-14T11:34:41.535134 + image/svg+xml + + + Matplotlib v3.10.8, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_images/fervo_project_cape-5-sensitivity-analysis-project_npv.png b/docs/_images/fervo_project_cape-5-sensitivity-analysis-project_npv.png new file mode 100644 index 000000000..b4d98bed7 Binary files /dev/null and b/docs/_images/fervo_project_cape-5-sensitivity-analysis-project_npv.png differ diff --git a/docs/_images/fervo_project_cape-5-sensitivity-analysis-project_npv.svg b/docs/_images/fervo_project_cape-5-sensitivity-analysis-project_npv.svg new file mode 100644 index 000000000..58aa80548 --- /dev/null +++ b/docs/_images/fervo_project_cape-5-sensitivity-analysis-project_npv.svg @@ -0,0 +1,3328 @@ + + + + + + + + 2026-01-14T11:34:39.966020 + image/svg+xml + + + Matplotlib v3.10.8, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_images/singh-et-al-2025_wht-700-ft-bench-spacing.png b/docs/_images/singh-et-al-2025_wht-700-ft-bench-spacing.png new file mode 100644 index 000000000..d027727ae Binary files /dev/null and b/docs/_images/singh-et-al-2025_wht-700-ft-bench-spacing.png differ diff --git a/docs/_images/singh_et_al_base_simulation-net-power-production.png b/docs/_images/singh_et_al_base_simulation-net-power-production.png new file mode 100644 index 000000000..fc3b0a619 Binary files /dev/null and b/docs/_images/singh_et_al_base_simulation-net-power-production.png differ diff --git a/docs/_images/singh_et_al_base_simulation-production-temperature.png b/docs/_images/singh_et_al_base_simulation-production-temperature.png new file mode 100644 index 000000000..f536c4f47 Binary files /dev/null and b/docs/_images/singh_et_al_base_simulation-production-temperature.png differ diff --git a/docs/conf.py b/docs/conf.py index 26674a995..011468e17 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,7 +18,7 @@ year = '2025' author = 'NREL' copyright = f'{year}, {author}' -version = release = '3.10.24' +version = release = '3.10.25' pygments_style = 'trac' templates_path = ['./templates'] diff --git a/docs/index.rst b/docs/index.rst index b145a94c3..5461acb6a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -8,8 +8,8 @@ Contents overview Theoretical-Basis-for-GEOPHIRES GEOPHIRES-Examples - Monte-Carlo-User-Guide SAM-Economic-Models + Monte-Carlo-User-Guide How-to-extend-GEOPHIRES-X .. toctree:: diff --git a/docs/requirements.txt b/docs/requirements.txt index 1b8df5e70..0f4a325d8 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,4 @@ sphinx>=1.3 sphinx-py3doc-enhanced-theme m2r2 +Jinja2 diff --git a/docs/templates/layout.html b/docs/templates/layout.html index 681f7aefb..8b157f779 100644 --- a/docs/templates/layout.html +++ b/docs/templates/layout.html @@ -25,5 +25,32 @@ /* Hide goofy-looking "Fork me on GitHub" badge */ display: none; } + + img.no-active:active { + max-width: 100%; + } + + /* Show interactive SVG on screen, hide print version */ + @media screen { + .sensitivity-chart-interactive { + display: block; + width: 100%; + } + .sensitivity-chart-print { + display: none; + } + } + + /* Show print version when printing, hide interactive SVG */ + @media print { + .sensitivity-chart-interactive { + display: none; + } + .sensitivity-chart-print { + display: block; + width: 100%; + max-width: 100%; + } + } {% endblock %} diff --git a/docs/watch_docs.py b/docs/watch_docs.py deleted file mode 100755 index 130ef909e..000000000 --- a/docs/watch_docs.py +++ /dev/null @@ -1,80 +0,0 @@ -#!python - -import os -import subprocess -import time - - -def get_file_states(directory): - """ - Returns a dictionary of file paths and their modification times. - """ - states = {} - for root, _, files in os.walk(directory): - for filename in files: - # Ignore hidden files, temporary editor files, and this script itself - # fmt:off - if (filename.startswith('.') or - filename.endswith('~') or filename == os.path.basename(__file__)): # noqa: PTH119 - # fmt:on - continue - - filepath = os.path.join(root, filename) - - # Avoid watching build directories if they are generated inside docs/ - if '_build' in filepath or 'build' in filepath: - continue - - try: - states[filepath] = os.path.getmtime(filepath) # noqa: PTH204 - except OSError: - pass - return states - - -def main(): - # Determine paths relative to this script - script_dir = os.path.dirname(os.path.abspath(__file__)) - project_root = os.path.dirname(script_dir) - - # Watch the directory where the script is located (docs/) - watch_dir = script_dir - - command = ['tox', '-e', 'docs'] - poll_interval = 2 # Seconds - - print(f"Watching '{watch_dir}' for changes...") - print(f"Project root determined as: '{project_root}'") - print(f"Command to run: {' '.join(command)}") - print('Press Ctrl+C to stop.') - - # Initial state - last_states = get_file_states(watch_dir) - - try: - while True: - time.sleep(poll_interval) - current_states = get_file_states(watch_dir) - - if current_states != last_states: - print('\n[Change Detected] Running docs build...') - - try: - # Run tox from the project root so it finds tox.ini - subprocess.run(command, cwd=project_root, check=False) # noqa: S603 - except FileNotFoundError: - print("Error: 'tox' command not found. Please ensure tox is installed.") - except Exception as e: - print(f'An error occurred: {e}') - - print(f"\nWaiting for further changes in '{watch_dir}'...") - - # Update state to the current state - last_states = get_file_states(watch_dir) - - except KeyboardInterrupt: - print('\nWatcher stopped.') - - -if __name__ == '__main__': - main() diff --git a/setup.py b/setup.py index 7aa1b7fe8..ff1b24c85 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def read(*names, **kwargs): setup( name='geophires-x', - version='3.10.24', + version='3.10.25', license='MIT', description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.', long_description='{}\n{}'.format( diff --git a/src/geophires_docs/__init__.py b/src/geophires_docs/__init__.py new file mode 100644 index 000000000..01b22f592 --- /dev/null +++ b/src/geophires_docs/__init__.py @@ -0,0 +1,7 @@ +from __future__ import annotations + +from pathlib import Path + +_PROJECT_ROOT: Path = Path(__file__).parent.parent.parent +_FPC5_INPUT_FILE_PATH: Path = _PROJECT_ROOT / 'tests/examples/Fervo_Project_Cape-5.txt' +_FPC5_RESULT_FILE_PATH: Path = _PROJECT_ROOT / 'tests/examples/Fervo_Project_Cape-5.out' diff --git a/src/geophires_docs/__main__.py b/src/geophires_docs/__main__.py new file mode 100644 index 000000000..545afd1aa --- /dev/null +++ b/src/geophires_docs/__main__.py @@ -0,0 +1,4 @@ +if __name__ == '__main__': + from geophires_docs import generate_fervo_project_cape_5_docs + + generate_fervo_project_cape_5_docs.generate_fervo_project_cape_5_docs() diff --git a/src/geophires_docs/generate_fervo_project_cape_5_docs.py b/src/geophires_docs/generate_fervo_project_cape_5_docs.py new file mode 100644 index 000000000..5be7ab321 --- /dev/null +++ b/src/geophires_docs/generate_fervo_project_cape_5_docs.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +from typing import Any + +from geophires_docs import _FPC5_INPUT_FILE_PATH +from geophires_docs import _FPC5_RESULT_FILE_PATH +from geophires_docs import _PROJECT_ROOT +from geophires_docs import generate_fervo_project_cape_5_md +from geophires_docs.generate_fervo_project_cape_5_graphs import generate_fervo_project_cape_5_graphs +from geophires_x_client import GeophiresInputParameters +from geophires_x_client import GeophiresXClient +from geophires_x_client import GeophiresXResult +from geophires_x_client import ImmutableGeophiresInputParameters + +_SINGH_ET_AL_BASE_SIMULATION_PARAMETERS: dict[str, Any] = { + 'Number of Production Wells': 4, + 'Number of Injection Wells per Production Well': '1.2, -- The Singh et al. scenario has 4 producers and 6 injectors. ' + 'We model one fewer injector here to account for the combined injection rate being lower for ' + 'the higher bench separation cases.', + 'Maximum Drawdown': '1, -- Redrilling not modeled in Singh et al. scenario. ' + '(The equivalent GEOPHIRES simulation allows drawdown to reach up to 100% without triggering redrilling)', + 'Plant Lifetime': 15, +} + + +# fmt:off +def get_singh_et_al_base_simulation_result(base_input_params: GeophiresInputParameters) \ + -> tuple[GeophiresInputParameters,GeophiresXResult]: + singh_et_al_base_simulation_input_params = ImmutableGeophiresInputParameters( + from_file_path=base_input_params.as_file_path(), + params=_SINGH_ET_AL_BASE_SIMULATION_PARAMETERS, + ) + # fmt:on + + singh_et_al_base_simulation_result = GeophiresXClient().get_geophires_result( + singh_et_al_base_simulation_input_params + ) + + return singh_et_al_base_simulation_input_params, singh_et_al_base_simulation_result + + +def generate_fervo_project_cape_5_docs(): + input_params: GeophiresInputParameters = ImmutableGeophiresInputParameters( + from_file_path=_FPC5_INPUT_FILE_PATH + ) + result = GeophiresXResult(_FPC5_RESULT_FILE_PATH) + + singh_et_al_base_simulation:tuple[GeophiresInputParameters,GeophiresXResult] = get_singh_et_al_base_simulation_result(input_params) + + generate_fervo_project_cape_5_graphs( + (input_params, result), + singh_et_al_base_simulation, + _PROJECT_ROOT / 'docs/_images' + ) + + generate_fervo_project_cape_5_md.generate_fervo_project_cape_5_md( + input_params, + result, + _SINGH_ET_AL_BASE_SIMULATION_PARAMETERS + ) + + +if __name__ == '__main__': + generate_fervo_project_cape_5_docs() diff --git a/src/geophires_docs/generate_fervo_project_cape_5_graphs.py b/src/geophires_docs/generate_fervo_project_cape_5_graphs.py new file mode 100644 index 000000000..38d01a3ca --- /dev/null +++ b/src/geophires_docs/generate_fervo_project_cape_5_graphs.py @@ -0,0 +1,169 @@ +from __future__ import annotations + +from pathlib import Path + +import numpy as np +from matplotlib import pyplot as plt + +from geophires_docs import _FPC5_INPUT_FILE_PATH +from geophires_docs import _FPC5_RESULT_FILE_PATH +from geophires_docs import _PROJECT_ROOT +from geophires_x_client import GeophiresInputParameters +from geophires_x_client import GeophiresXResult +from geophires_x_client import ImmutableGeophiresInputParameters + + +def generate_net_power_graph( + result: GeophiresXResult, output_dir: Path, filename='fervo_project_cape-5-net-power-production.png' +) -> str: + """ + Generate a graph of time vs net power production and save it to the output directory. + + Args: + result: The GEOPHIRES result object + output_dir: Directory to save the graph image + + Returns: + The filename of the generated graph + """ + print('Generating net power production graph...') + + # Extract data from power generation profile + profile = result.power_generation_profile + headers = profile[0] + data = profile[1:] + + # Find the indices for YEAR and NET POWER columns + year_idx = headers.index('YEAR') + net_power_idx = headers.index('NET POWER (MW)') + + # Extract years and net power values + years = np.array([row[year_idx] for row in data]) + net_power = np.array([row[net_power_idx] for row in data]) + + # Create the figure + fig, ax = plt.subplots(figsize=(10, 6)) + + # Plot the data + ax.plot(years, net_power, color='#3399e6', linewidth=2, marker='o', markersize=4) + + # Set labels and title + ax.set_xlabel('Time (Years)', fontsize=12) + ax.set_ylabel('Net Power Production (MW)', fontsize=12) + ax.set_title('Net Power Production Over Project Lifetime', fontsize=14) + + # Set axis limits + ax.set_xlim(years.min(), years.max()) + + # Add grid for better readability + ax.grid(True, linestyle='--', alpha=0.7) + + # Ensure the output directory exists + output_dir.mkdir(parents=True, exist_ok=True) + + # Save the figure + save_path = output_dir / filename + plt.savefig(save_path, dpi=150, bbox_inches='tight') + plt.close(fig) + + print(f'✓ Generated {save_path}') + return filename + + +def generate_production_temperature_graph( + result: GeophiresXResult, output_dir: Path, filename='fervo_project_cape-5-production-temperature.png' +) -> str: + """ + Generate a graph of time vs production temperature and save it to the output directory. + + Args: + result: The GEOPHIRES result object + output_dir: Directory to save the graph image + + Returns: + The filename of the generated graph + """ + print('Generating production temperature graph...') + + # Extract data from power generation profile + profile = result.power_generation_profile + headers = profile[0] + data = profile[1:] + + # Find the indices for YEAR and THERMAL DRAWDOWN columns + year_idx = headers.index('YEAR') + # Look for production temperature column - could be labeled differently + temp_idx = headers.index('GEOFLUID TEMPERATURE (degC)') + + # Extract years and temperature values + years = np.array([row[year_idx] for row in data]) + temperatures_celsius = np.array([row[temp_idx] for row in data]) + + # Convert Celsius to Fahrenheit + temperatures_fahrenheit = temperatures_celsius * 9 / 5 + 32 + + # Create the figure - taller than wide (portrait orientation) + fig, ax = plt.subplots(figsize=(6, 8)) + + # Plot the data - just the curve, no markers + ax.plot(years, temperatures_fahrenheit, color='#e63333', linewidth=2) + + # Set labels and title + ax.set_xlabel('Simulation time (Years)', fontsize=12) + ax.set_ylabel('Wellhead temperature (°F)', fontsize=12) + # ax.set_title('Production Temperature Over Project Lifetime', fontsize=14) + + # Set axis limits + ax.set_xlim(years.min(), years.max()) + ax.set_ylim(200, 450) + + # Set y-axis ticks every 50 degrees, with 400 explicitly labeled but not 200 or 450 + ax.set_yticks([250, 300, 350, 400]) + + # Add grid for better readability + ax.grid(True, linestyle='--', alpha=0.7) + + # Ensure the output directory exists + output_dir.mkdir(parents=True, exist_ok=True) + + # Save the figure + save_path = output_dir / filename + plt.savefig(save_path, dpi=150, bbox_inches='tight') + plt.close(fig) + + print(f'✓ Generated {save_path}') + return filename + + +def generate_fervo_project_cape_5_graphs( + base_case: tuple[GeophiresInputParameters, GeophiresXResult], + singh_et_al_base_simulation: tuple[GeophiresInputParameters, GeophiresXResult], + output_dir: Path, +) -> None: + # base_case_input_params: GeophiresInputParameters = base_case[0] + # result:GeophiresXResult = base_case[1] + + # generate_net_power_graph(result, output_dir) + # generate_production_temperature_graph(result, output_dir) + + singh_et_al_base_simulation_result: GeophiresXResult = singh_et_al_base_simulation[1] + + # generate_net_power_graph( + # singh_et_al_base_simulation_result, output_dir, filename='singh_et_al_base_simulation-net-power-production.png' + # ) + generate_production_temperature_graph( + singh_et_al_base_simulation_result, + output_dir, + filename='singh_et_al_base_simulation-production-temperature.png', + ) + + +if __name__ == '__main__': + docs_dir = _PROJECT_ROOT / 'docs' + images_dir = docs_dir / '_images' + + input_params_: GeophiresInputParameters = ImmutableGeophiresInputParameters(from_file_path=_FPC5_INPUT_FILE_PATH) + + result_ = GeophiresXResult(_FPC5_RESULT_FILE_PATH) + + generate_fervo_project_cape_5_graphs(input_params_, result_, images_dir) diff --git a/src/geophires_docs/generate_fervo_project_cape_5_md.py b/src/geophires_docs/generate_fervo_project_cape_5_md.py new file mode 100755 index 000000000..bd6bad929 --- /dev/null +++ b/src/geophires_docs/generate_fervo_project_cape_5_md.py @@ -0,0 +1,474 @@ +#!python +""" +Script to generate Fervo_Project_Cape-5.md from its jinja template. +This ensures the markdown documentation stays in sync with actual GEOPHIRES results. +""" + +from __future__ import annotations + +import json +from typing import Any + +import numpy as np +from jinja2 import Environment +from jinja2 import FileSystemLoader +from pint.facets.plain import PlainQuantity + +from geophires_docs import _FPC5_INPUT_FILE_PATH +from geophires_docs import _FPC5_RESULT_FILE_PATH +from geophires_docs import _PROJECT_ROOT +from geophires_x.GeoPHIRESUtils import is_int +from geophires_x.GeoPHIRESUtils import sig_figs +from geophires_x_client import GeophiresInputParameters +from geophires_x_client import GeophiresXResult +from geophires_x_client import ImmutableGeophiresInputParameters + +# Add project root to path to import GEOPHIRES and docs modules + + +def _get_input_parameters_dict( # TODO consolidate with FervoProjectCape5TestCase._get_input_parameters + _params: GeophiresInputParameters, include_parameter_comments: bool = False, include_line_comments: bool = False +) -> dict[str, Any]: + comment_idx = 0 + ret: dict[str, Any] = {} + for line in _params.as_text().split('\n'): + parts = line.strip().split(', ') # TODO generalize for array-type params + field = parts[0].strip() + if len(parts) >= 2 and not field.startswith('#'): + fieldValue = parts[1].strip() + if include_parameter_comments and len(parts) > 2: + fieldValue += ', ' + (', '.join(parts[2:])).strip() + ret[field] = fieldValue.strip() + + if include_line_comments and field.startswith('#'): + ret[f'_COMMENT-{comment_idx}'] = line.strip() + comment_idx += 1 + + # TODO preserve newlines + + return ret + + +def _get_schema() -> dict[str, Any]: + schema_file = _PROJECT_ROOT / 'src/geophires_x_schema_generator/geophires-request.json' + with open(schema_file, encoding='utf-8') as f: + return json.loads(f.read()) + + +def _get_parameter_schema(param_name: str) -> dict[str, Any]: + return _get_schema()['properties'][param_name] + + +def _get_parameter_schema_type(param_name: str) -> dict[str, Any]: + return _get_parameter_schema(param_name)['type'] + + +def _get_parameter_category(param_name: str) -> str: + return _get_parameter_schema(param_name)['category'] + + +def _get_parameter_units(param_name: str) -> str | None: + unit = _get_schema()['properties'][param_name]['units'] + + if unit == '': + return 'dimensionless' + + return unit + + +def _get_unit_display(parameter_units_from_schema: str) -> str: + if parameter_units_from_schema is None: + return '' + + display_unit_prefix = ( + ' ' + if not (parameter_units_from_schema and any(it in parameter_units_from_schema for it in ['%', 'USD', 'MUSD'])) + else '' + ) + display_unit = parameter_units_from_schema + for replacement in [ + ('kilometer', 'km'), + ('degC', '℃'), + ('meter', 'm'), + ('m**3', 'm³'), + ('m**2', 'm²'), + ('MUSD', 'M'), + ('USD', ''), + ]: + display_unit = display_unit.replace(replacement[0], replacement[1]) + + return f'{display_unit_prefix}{display_unit}' + + +def generate_fpc_reservoir_parameters_table_md(input_params: GeophiresInputParameters) -> str: + params_to_exclude = [ + 'Maximum Temperature', + 'Reservoir Porosity', + 'Reservoir Volume Option', + ] + + return get_fpc_category_parameters_table_md( + input_params, + 'Reservoir', + params_to_exclude, + ) + + +def generate_fpc_well_bores_parameters_table_md(input_params: GeophiresInputParameters) -> str: + return get_fpc_category_parameters_table_md( + input_params, + 'Well Bores', + parameters_to_exclude=['Number of Multilateral Sections'], + ) + + +def generate_fpc_surface_plant_parameters_table_md(input_params: GeophiresInputParameters) -> str: + return get_fpc_category_parameters_table_md( + input_params, + 'Surface Plant', + parameters_to_exclude=['End-Use Option', 'Construction Years'], + ) + + +def generate_fpc_construction_parameters_table_md(input_params: GeophiresInputParameters) -> str: + input_params_dict = _get_input_parameters_dict( + input_params, include_parameter_comments=True, include_line_comments=True + ) + schedule_param_name = 'Construction CAPEX Schedule' + construction_input_params = {} + for construction_param in ['Construction Years', schedule_param_name]: + construction_input_params[construction_param] = input_params_dict[construction_param] + + # Comment hardcoded here for now because handling of array parameters with comments might be buggy in client or + # web interface... + schedule_param_comment = ( + 'Array of fractions of overnight capital cost expenditure for each year, starting with ' + 'lower costs during initial years for exploration and increasing to higher costs during ' + 'later years as buildout progresses.' + ) + construction_input_params[schedule_param_name] = ( + f'{construction_input_params[schedule_param_name]}' f', -- {schedule_param_comment}' + ) + + return get_fpc_category_parameters_table_md( + ImmutableGeophiresInputParameters(params=construction_input_params), None + ) + + +def generate_fpc_economics_parameters_table_md(input_params: GeophiresInputParameters) -> str: + return get_fpc_category_parameters_table_md( + input_params, + 'Economics', + parameters_to_exclude=[ + 'Ending Electricity Sale Price', + 'Electricity Escalation Start Year', + 'Construction CAPEX Schedule', + 'Time steps per year', + 'Print Output to Console', + ], + ) + + +def get_fpc_category_parameters_table_md( + input_params: GeophiresInputParameters, category_name: str | None, parameters_to_exclude: list[str] | None = None +) -> str: + if parameters_to_exclude is None: + parameters_to_exclude = [] + + input_params_dict = _get_input_parameters_dict( + input_params, include_parameter_comments=True, include_line_comments=True + ) + + non_breaking_space = '\xa0' + + # noinspection MarkdownIncorrectTableFormatting + table_md = f""" +| Parameter | Input{non_breaking_space}Value | Comment | +|-------------------|-------------------------------------------|-------------| +""" + + table_entries = [] + for param_name, param_val_comment in input_params_dict.items(): + if param_name.startswith(('#', '_COMMENT-')): + continue + + if param_name in parameters_to_exclude: + continue + + category = _get_parameter_category(param_name) + if category_name is None or category == category_name: + param_val_comment_split = param_val_comment.split( + # ',', + ',' if _get_parameter_schema_type(param_name) != 'array' else ', ', + maxsplit=1, + ) + + param_val = param_val_comment_split[0] + + param_comment = ( + param_val_comment_split[1].replace('-- ', '') if len(param_val_comment_split) > 1 else ' .. N/A ' + ) + param_unit = _get_parameter_units(param_name) + if param_unit == 'dimensionless': + param_unit_display = '%' + param_val = sig_figs( + PlainQuantity(float(param_val), 'dimensionless').to('percent').magnitude, + 10, # trim floating point errors + ) + elif ' ' in param_val: + param_val_split = param_val.split(' ', maxsplit=1) + param_val = param_val_split[0] + param_unit_display = _get_unit_display(param_val_split[1]) + else: + param_unit_display = _get_unit_display(param_unit) + + param_unit_display_prefix = '$' if param_unit and 'USD' in param_unit else '' + + if is_int(param_val): + param_val = int(param_val) + + param_schema = _get_parameter_schema(param_name) + if param_schema and 'enum_values' in param_schema: + for enum_value in param_schema['enum_values']: + if enum_value['int_value'] == param_val: + enum_display = enum_value['value'] + # param_val = f'{param_val} ({enum_display})' + param_val = enum_display + break + + param_name_display = param_name.replace(' ', non_breaking_space, 2) + table_entries.append( + [param_name_display, f'{param_unit_display_prefix}{param_val}{param_unit_display}', param_comment] + ) + + for table_entry in table_entries: + table_md += f'| {table_entry[0]} | {table_entry[1]} | {table_entry[2]} |\n' + + return table_md.strip() + + +def _q(d: dict[str, Any]) -> PlainQuantity: + return PlainQuantity(d['value'], d['unit']) + + +def get_fpc5_input_parameter_values(input_params: GeophiresInputParameters, result: GeophiresXResult) -> dict[str, Any]: + print('Extracting input parameter values...') + + params = _get_input_parameters_dict(input_params) + r: dict[str, dict[str, Any]] = result.result + + exploration_cost_musd = _q(r['CAPITAL COSTS (M$)']['Exploration costs']).to('MUSD').magnitude + assert exploration_cost_musd == float( + params['Exploration Capital Cost'] + ), 'Exploration cost mismatch between parameters and result' + + return { + 'exploration_cost_musd': round(sig_figs(exploration_cost_musd, 2)), + 'wacc_pct': sig_figs(r['ECONOMIC PARAMETERS']['WACC']['value'], 3), + 'reservoir_volume_m3': f"{r['RESERVOIR PARAMETERS']['Reservoir volume']['value']:,}", + } + + +def get_result_values(result: GeophiresXResult) -> dict[str, Any]: + print('Extracting result values...') + + r: dict[str, dict[str, Any]] = result.result + + total_capex_q: PlainQuantity = _q(r['CAPITAL COSTS (M$)']['Total CAPEX']) + + surf_equip_sim = r['SURFACE EQUIPMENT SIMULATION RESULTS'] + min_net_generation_mwe = surf_equip_sim['Minimum Net Electricity Generation']['value'] + avg_net_generation_mwe = surf_equip_sim['Average Net Electricity Generation']['value'] + max_net_generation_mwe = surf_equip_sim['Maximum Net Electricity Generation']['value'] + max_total_generation_mwe = surf_equip_sim['Maximum Total Electricity Generation']['value'] + parasitic_loss_pct = ( + surf_equip_sim['Average Pumping Power']['value'] + / surf_equip_sim['Average Total Electricity Generation']['value'] + * 100.0 + ) + net_power_idx = result.power_generation_profile[0].index('NET POWER (MW)') + + def n_year_avg_net_power_mwe(years: int) -> float: + return np.average([it[net_power_idx] for it in result.power_generation_profile[1:]][:years]) + + two_year_avg_net_power_mwe = n_year_avg_net_power_mwe(2) + two_year_avg_net_power_mwe_per_production_well = two_year_avg_net_power_mwe / _number_of_production_wells(result) + + total_fracture_surface_area_per_well_m2 = _total_fracture_surface_area_per_well_m2(result) + + occ_q = _q(r['CAPITAL COSTS (M$)']['Overnight Capital Cost']) + + field_gathering_cost_musd = _q(r['CAPITAL COSTS (M$)']['Field gathering system costs']).to('MUSD').magnitude + field_gathering_cost_pct_occ = field_gathering_cost_musd / occ_q.to('MUSD').magnitude * 100.0 + + redrills = r['ENGINEERING PARAMETERS']['Number of times redrilling']['value'] + total_wells_including_redrilling = redrills * _number_of_wells(result) + + return { + # Economic Results + 'lcoe_usd_per_mwh': sig_figs( + _q(r['SUMMARY OF RESULTS']['Electricity breakeven price']).to('USD / MWh').magnitude, 3 + ), + 'irr_pct': sig_figs(r['ECONOMIC PARAMETERS']['After-tax IRR']['value'], 3), + 'npv_musd': sig_figs(r['ECONOMIC PARAMETERS']['Project NPV']['value'], 3), + # Capital Costs + 'drilling_costs_musd': round(sig_figs(_drilling_costs_musd(result), 3)), + 'drilling_costs_per_well_musd': sig_figs(_drilling_costs_per_well_musd(result), 3), + 'stim_costs_musd': round(sig_figs(_stim_costs_musd(result), 3)), + 'stim_costs_per_well_musd': sig_figs(_stim_costs_per_well_musd(result), 3), + 'surface_power_plant_costs_gusd': sig_figs( + _q(r['CAPITAL COSTS (M$)']['Surface power plant costs']).to('GUSD').magnitude, 3 + ), + 'field_gathering_cost_musd': round(sig_figs(field_gathering_cost_musd, 3)), + 'field_gathering_cost_pct_occ': round(sig_figs(field_gathering_cost_pct_occ, 1)), + 'occ_gusd': sig_figs(occ_q.to('GUSD').magnitude, 3), + 'total_capex_gusd': sig_figs(total_capex_q.to('GUSD').magnitude, 3), + 'capex_usd_per_kw': round( + sig_figs((total_capex_q / PlainQuantity(max_net_generation_mwe, 'MW')).to('USD / kW').magnitude, 2) + ), + # Technical & Engineering Results + 'bht_temp_degc': r['RESERVOIR PARAMETERS']['Bottom-hole temperature']['value'], + 'min_net_generation_mwe': round(sig_figs(min_net_generation_mwe, 3)), + 'avg_net_generation_mwe': round(sig_figs(avg_net_generation_mwe, 3)), + 'max_net_generation_mwe': round(sig_figs(max_net_generation_mwe, 3)), + 'max_total_generation_mwe': round(sig_figs(max_total_generation_mwe, 3)), + 'two_year_avg_net_power_mwe_per_production_well': sig_figs(two_year_avg_net_power_mwe_per_production_well, 2), + 'parasitic_loss_pct': sig_figs(parasitic_loss_pct, 3), + 'number_of_times_redrilling': redrills, + 'total_wells_including_redrilling': total_wells_including_redrilling, + 'initial_production_temperature_degc': round( + sig_figs(r['RESERVOIR SIMULATION RESULTS']['Initial Production Temperature']['value'], 3) + ), + 'average_production_temperature_degc': round( + sig_figs(r['RESERVOIR SIMULATION RESULTS']['Average Production Temperature']['value'], 3) + ), + 'total_fracture_surface_area_per_well_mm2': sig_figs(total_fracture_surface_area_per_well_m2 / 1e6, 2), + 'total_fracture_surface_area_per_well_mft2': round( + sig_figs( + PlainQuantity(total_fracture_surface_area_per_well_m2, 'm ** 2').to('foot ** 2').magnitude * 1e-6, 2 + ) + ), + # TODO port all input and result values here instead of hardcoding them in the template + } + + +def _number_of_production_wells(result: GeophiresXResult) -> int: + return result.result['SUMMARY OF RESULTS']['Number of production wells']['value'] + + +def _number_of_wells(result: GeophiresXResult) -> int: + r: dict[str, dict[str, Any]] = result.result + + number_of_wells = r['SUMMARY OF RESULTS']['Number of injection wells']['value'] + _number_of_production_wells( + result + ) + + return number_of_wells + + +def _drilling_costs_musd(result: GeophiresXResult) -> float: + r: dict[str, dict[str, Any]] = result.result + + return _q(r['CAPITAL COSTS (M$)']['Drilling and completion costs']).to('MUSD').magnitude + + +def _drilling_costs_per_well_musd(result: GeophiresXResult) -> float: + return _drilling_costs_musd(result) / _number_of_wells(result) + + +def _stim_costs_per_well_musd(result: GeophiresXResult) -> float: + stim_costs_per_well_musd = _stim_costs_musd(result) / _number_of_wells(result) + return stim_costs_per_well_musd + + +def _stim_costs_musd(result: GeophiresXResult) -> float: + r: dict[str, dict[str, Any]] = result.result + + stim_costs_musd = _q(r['CAPITAL COSTS (M$)']['Stimulation costs']).to('MUSD').magnitude + return stim_costs_musd + + +def _stim_costs_per_well_musd(result: GeophiresXResult) -> float: + stim_costs_per_well_musd = _stim_costs_musd(result) / _number_of_wells(result) + return stim_costs_per_well_musd + + +def _total_fracture_surface_area_per_well_m2(result: GeophiresXResult) -> float: + r: dict[str, dict[str, Any]] = result.result + res_params = r['RESERVOIR PARAMETERS'] + return ( + _q(res_params['Fracture area']).to('m ** 2').magnitude + * res_params['Number of fractures']['value'] + / _number_of_wells(result) + ) + + +def generate_res_eng_reference_sim_params_table_md( + base_case_input_params: GeophiresInputParameters, res_eng_reference_sim_params: dict[str, Any] +) -> str: + return get_fpc_category_parameters_table_md( + ImmutableGeophiresInputParameters( + # from_file_path=base_case_input_params.as_file_path(), + params=res_eng_reference_sim_params + ), + None, + ) + + +def generate_fervo_project_cape_5_md( + input_params: GeophiresInputParameters, + result: GeophiresXResult, + res_eng_reference_sim_params: dict[str, Any] | None = None, +) -> None: + if res_eng_reference_sim_params is None: + res_eng_reference_sim_params = {} + + # noinspection PyDictCreation + template_values = {**get_fpc5_input_parameter_values(input_params, result), **get_result_values(result)} + + for template_key, md_method in { + 'reservoir_parameters_table_md': generate_fpc_reservoir_parameters_table_md, + 'surface_plant_parameters_table_md': generate_fpc_surface_plant_parameters_table_md, + 'well_bores_parameters_table_md': generate_fpc_well_bores_parameters_table_md, + 'economics_parameters_table_md': generate_fpc_economics_parameters_table_md, + 'construction_parameters_table_md': generate_fpc_construction_parameters_table_md, + }.items(): + template_values[template_key] = md_method(input_params) + + template_values['reservoir_engineering_reference_simulation_params_table_md'] = ( + generate_res_eng_reference_sim_params_table_md(input_params, res_eng_reference_sim_params) + ) + + docs_dir = _PROJECT_ROOT / 'docs' + + # Set up Jinja environment + env = Environment(loader=FileSystemLoader(docs_dir), autoescape=True) + template = env.get_template('Fervo_Project_Cape-5.md.jinja') + + # Render template + print('Rendering template...') + output = template.render(**template_values) + + # Write output + output_file = docs_dir / 'Fervo_Project_Cape-5.md' + output_file.write_text(output, encoding='utf-8') + + print(f'✓ Generated {output_file}') + print('\nKey results:') + print(f"\tLCOE: ${template_values['lcoe_usd_per_mwh']}/MWh") + print(f"\tIRR: {template_values['irr_pct']}%") + print(f"\tTotal CAPEX: ${template_values['total_capex_gusd']}B") + + +def main(): + """ + Generate Fervo_Project_Cape-5.md (markdown documentation) from the Jinja template. + """ + + input_params: GeophiresInputParameters = ImmutableGeophiresInputParameters(from_file_path=_FPC5_INPUT_FILE_PATH) + result = GeophiresXResult(_FPC5_RESULT_FILE_PATH) + generate_fervo_project_cape_5_md(input_params, result) + + +if __name__ == '__main__': + main() diff --git a/src/geophires_docs/watch_docs.py b/src/geophires_docs/watch_docs.py new file mode 100755 index 000000000..80fbc0ae1 --- /dev/null +++ b/src/geophires_docs/watch_docs.py @@ -0,0 +1,125 @@ +#!python +# Automatically rebuilds docs locally when changes are detected. +# Usage, from the project root: +# ./src/geophires_docs/watch_docs.py + +import os +import subprocess +import time +from pathlib import Path +from typing import Any + + +def _get_logger(): + # sh = logging.StreamHandler(sys.stdout) + # sh.setLevel(logging.INFO) + # sh.setFormatter(logging.Formatter(fmt='[%(asctime)s][%(levelname)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S')) + # + # ret = logging.getLogger(__name__) + # ret.addHandler(sh) + # return ret + + # noinspection PyMethodMayBeStatic + class _PrintLogger: + def info(self, msg): + print(f'[INFO] {msg}') + + def error(self, msg): + print(f'[ERROR] {msg}') + + return _PrintLogger() + + +_log = _get_logger() + + +def get_file_states(directory) -> dict[str, Any]: + """ + Returns a dictionary of file paths and their modification times. + """ + states = {} + for root, _, files in os.walk(directory): + for filename in files: + # Ignore hidden files, temporary editor files, and this script itself + # fmt:off + if (filename.startswith('.') or + filename.endswith('~') or filename == os.path.basename(__file__)): # noqa: PTH119 + # fmt:on + continue + + filepath = os.path.join(root, filename) + + # Avoid watching build directories if they are generated inside docs/ + if '_build' in filepath or 'build' in filepath: + continue + + try: + states[filepath] = os.path.getmtime(filepath) # noqa: PTH204 + except OSError: + pass + return states + + +def main(): + # Determine paths relative to this script + script_dir = os.path.dirname(os.path.abspath(__file__)) + project_root: str = Path(__file__).parent.parent.parent + + def _say(msg) -> None: + try: + subprocess.run(['say', msg], cwd=project_root, check=False) # noqa: S603,S607 + except subprocess.CalledProcessError: + pass + + # Watch the directory where the script is located (docs/) + watch_dirs = [script_dir, Path(project_root) / 'docs', Path(project_root) / 'tests' / 'examples'] + + command = ['tox', '-e', 'docs'] + poll_interval = 2 # Seconds + + _log.info(f"Watching '{watch_dirs}' for changes...") + _log.info(f"Project root determined as: '{project_root}'") + _log.info(f"Command to run: {' '.join(command)}") + _log.info('Press Ctrl+C to stop.') + + def _get_file_states() -> dict: + states = {} + for watch_dir in watch_dirs: + states = {**states, **get_file_states(watch_dir)} + + return states + + # Initial state + last_states = _get_file_states() + + try: + while True: + time.sleep(poll_interval) + current_states = _get_file_states() + + if current_states != last_states: + _log.info('\n[Change Detected] Running docs build...') + time.sleep(1) + + try: + # Run tox from the project root so it finds tox.ini + subprocess.run(command, cwd=project_root, check=False) # noqa: S603 + except FileNotFoundError: + _log.error("Error: 'tox' command not found. Please ensure tox is installed.") + except Exception as e: + _log.error(f'An error occurred: {e}') + _say('error rebuilding docs') + + _log.info(f"\nDocs rebuild complete at {time.strftime('%Y-%m-%d %H:%M:%S')}.") + _say('docs rebuilt') + _log.info(f"\nWaiting for further changes in '{watch_dirs}'...") + + # Update state to the current state + last_states = _get_file_states() + + except KeyboardInterrupt: + _log.info('\nWatcher stopped.') + + +if __name__ == '__main__': + main() diff --git a/src/geophires_x/Economics.py b/src/geophires_x/Economics.py index f47b02d5c..ca2edcf97 100644 --- a/src/geophires_x/Economics.py +++ b/src/geophires_x/Economics.py @@ -3585,7 +3585,6 @@ def _calculate_sam_economics(self, model: Model) -> None: self.ProjectMOIC.value = self.sam_economics_calculations.moic.value self.ProjectVIR.value = self.sam_economics_calculations.project_vir.value - # TODO remove or clarify project payback period: https://github.com/NREL/GEOPHIRES-X/issues/413 self.ProjectPaybackPeriod.value = self.sam_economics_calculations.project_payback_period.value # noinspection SpellCheckingInspection diff --git a/src/geophires_x/EconomicsSam.py b/src/geophires_x/EconomicsSam.py index 419d04094..d55062455 100644 --- a/src/geophires_x/EconomicsSam.py +++ b/src/geophires_x/EconomicsSam.py @@ -96,7 +96,6 @@ class SamEconomicsCalculations: project_vir: OutputParameter = field(default_factory=project_vir_parameter) project_payback_period: OutputParameter = field(default_factory=project_payback_period_parameter) - """TODO remove or clarify project payback period: https://github.com/NREL/GEOPHIRES-X/issues/413""" @property def _pre_revenue_years_count(self) -> int: @@ -522,7 +521,7 @@ def _calculate_project_vir(cash_flow: list[list[Any]], model: Model) -> float: def _calculate_project_payback_period(cash_flow: list[list[Any]], model) -> float | None: """ - TODO remove or clarify project payback period: https://github.com/NREL/GEOPHIRES-X/issues/413 + See payback period output parameter's tooltip text for details relevant to this implementation. """ try: diff --git a/src/geophires_x/EconomicsUtils.py b/src/geophires_x/EconomicsUtils.py index 78614f491..25a9913e5 100644 --- a/src/geophires_x/EconomicsUtils.py +++ b/src/geophires_x/EconomicsUtils.py @@ -83,7 +83,10 @@ def project_payback_period_parameter() -> OutputParameter: CurrentUnits=TimeUnit.YEAR, ToolTipText='The time at which cumulative cash flow reaches zero. ' 'For projects that never pay back, the calculated value will be "N/A". ' - 'For SAM Economic Models, after-tax net cash flow is used to calculate the cumulative cash flow.', + 'For SAM Economic Models, this is Simple Payback Period (SPB): the time at which cumulative non-discounted ' + 'cash flow reaches zero, calculated using non-discounted after-tax net cash flow. ' + 'See https://samrepo.nrelcloud.org/help/mtf_payback.html for important considerations regarding the ' + 'limitations of this metric.', ) diff --git a/src/geophires_x/Outputs.py b/src/geophires_x/Outputs.py index 98bc6a680..18ba85f0c 100644 --- a/src/geophires_x/Outputs.py +++ b/src/geophires_x/Outputs.py @@ -346,15 +346,15 @@ def PrintOutputs(self, model: Model): f.write(NL) f.write(' ***RESOURCE CHARACTERISTICS***\n') f.write(NL) - f.write(f' Maximum reservoir temperature: {model.reserv.Tmax.value:10.1f} ' + model.reserv.Tmax.CurrentUnits.value + NL) - f.write(f' Number of segments: {model.reserv.numseg.value:10.0f} ' + NL) + f.write(f' Maximum reservoir temperature: {model.reserv.Tmax.value:10.1f} {model.reserv.Tmax.CurrentUnits.value}\n') + f.write(f' Number of segments: {model.reserv.numseg.value:10.0f}\n') if model.reserv.numseg.value == 1: - f.write(f' Geothermal gradient: {model.reserv.gradient.value[0]:10.4g} ' + model.reserv.gradient.CurrentUnits.value + NL) + f.write(f' Geothermal gradient: {model.reserv.gradient.value[0]:10.4g} {model.reserv.gradient.CurrentUnits.value}\n') else: for i in range(1, model.reserv.numseg.value): - f.write(f' Segment {str(i):s} Geothermal gradient: {model.reserv.gradient.value[i-1]:10.4g} ' + model.reserv.gradient.CurrentUnits.value +NL) + f.write(f' Segment {str(i):s} Geothermal gradient: {model.reserv.gradient.value[i-1]:10.4g} {model.reserv.gradient.CurrentUnits.value}\n') f.write(f' Segment {str(i):s} Thickness: {round(model.reserv.layerthickness.value[i-1], 10)} {model.reserv.layerthickness.CurrentUnits.value}\n') - f.write(f' Segment {str(i+1):s} Geothermal gradient: {model.reserv.gradient.value[i]:10.4g} ' + model.reserv.gradient.CurrentUnits.value + NL) + f.write(f' Segment {str(i+1):s} Geothermal gradient: {model.reserv.gradient.value[i]:10.4g} {model.reserv.gradient.CurrentUnits.value}\n') f.write(NL) f.write(NL) @@ -909,15 +909,15 @@ def get_sam_cash_flow_profile_output(self, model): # number that results in a separator line at least as wide as the table (narrower would be unsightly). spaces_per_tab = 4 - # The tabluate library has native separating line functionality (per https://pypi.org/project/tabulate/) but + # The tabulate library has native separating line functionality (per https://pypi.org/project/tabulate/) but # I wasn't able to get it to replicate the formatting as coded below. - separator_line = len(cfp_o.split('\n')[0].replace('\t',' ' * spaces_per_tab)) * '-' + separator_line = len(cfp_o.split('\n')[0].replace('\t', ' ' * spaces_per_tab)) * '-' ret += separator_line + '\n' ret += cfp_o ret += '\n' + separator_line - ret += '\n\n' + ret += '\n' return ret diff --git a/src/geophires_x/Reservoir.py b/src/geophires_x/Reservoir.py index 3eea04cec..639769d34 100644 --- a/src/geophires_x/Reservoir.py +++ b/src/geophires_x/Reservoir.py @@ -284,7 +284,9 @@ def __init__(self, model: Model): PreferredUnits=LengthUnit.METERS, CurrentUnits=LengthUnit.METERS, ErrMessage="assume default fracture width (500 m)", - ToolTipText="Width of each fracture" + ToolTipText="Total horizontal length of each fracture plane (from tip to tip). " + "Note: In some contexts this is called 'Fracture Length'; it refers to the fracture's lateral " + "extent, not its aperture or thickness." ) fracnumb_allowable_range = list(range(1, _MAX_ALLOWED_FRACTURES + 1, 1)) diff --git a/src/geophires_x/WellBores.py b/src/geophires_x/WellBores.py index 9e6e9249e..c4cddeb07 100644 --- a/src/geophires_x/WellBores.py +++ b/src/geophires_x/WellBores.py @@ -740,6 +740,18 @@ def __init__(self, model: Model): ToolTipText="Pass this parameter to set the Number of Production Wells and Number of Injection Wells to " "same value." ) + # noinspection SpellCheckingInspection + self.ninj_per_production_well = self.ParameterDict[self.ninj_per_production_well.Name] = floatParameter( + "Number of Injection Wells per Production Well", + DefaultValue=1, + Min=0, + Max=max_doublets-1, + UnitType=Units.NONE, + Required=False, + ToolTipText="Number of (identical) injection wells per production well. " + "For example, provide 0.666 to specify a 3:2 production:injection well ratio. " + "The number of injection wells will be rounded up to the nearest integer." + ) # noinspection SpellCheckingInspection self.prodwelldiam = self.ParameterDict[self.prodwelldiam.Name] = floatParameter( @@ -1361,20 +1373,35 @@ def read_parameters(self, model: Model) -> None: coerce_int_params_to_enum_values(self.ParameterDict) - if self.doublets_count.Provided: - def _error(num_wells_param_:intParameter): - msg = f'{num_wells_param_.Name} may not be provided when {self.doublets_count.Name} is provided.' - model.logger.error(msg) - raise ValueError(msg) + self._set_well_counts_from_parameters(model) + + model.logger.info(f"read parameters complete {self.__class__.__name__}: {__name__}") + + def _set_well_counts_from_parameters(self, model: Model): + mutually_exclusive_well_count_params = [self.doublets_count, self.ninj_per_production_well] + provided_well_count_params = [it for it in mutually_exclusive_well_count_params if it.Provided] + if len(provided_well_count_params) > 1: + raise ValueError(f'Only one of [{", ".join([it.Name for it in mutually_exclusive_well_count_params])}] ' + f'may be provided.') + + def _raise_incompatible_param_error(incompatible_param: intParameter, with_param: intParameter): + msg = f'{incompatible_param.Name} may not be provided when {with_param.Name} is provided.' + model.logger.error(msg) + raise ValueError(msg) + if self.doublets_count.Provided: for num_wells_param in [self.ninj, self.nprod]: if num_wells_param.Provided: - _error(num_wells_param) + _raise_incompatible_param_error(num_wells_param, self.doublets_count) self.ninj.value = self.doublets_count.value self.nprod.value = self.doublets_count.value - model.logger.info(f"read parameters complete {self.__class__.__name__}: {__name__}") + if self.ninj_per_production_well.Provided: + if self.ninj.Provided: + _raise_incompatible_param_error(self.ninj, self.ninj_per_production_well) + + self.ninj.value = int(math.ceil(self.nprod.value * self.ninj_per_production_well.value)) def Calculate(self, model: Model) -> None: """ diff --git a/src/geophires_x/__init__.py b/src/geophires_x/__init__.py index 8a9ccd93e..891f6ea2f 100644 --- a/src/geophires_x/__init__.py +++ b/src/geophires_x/__init__.py @@ -1 +1 @@ -__version__ = '3.10.24' +__version__ = '3.10.25' diff --git a/src/geophires_x_client/geophires_x_result.py b/src/geophires_x_client/geophires_x_result.py index 91b14c26c..452416364 100644 --- a/src/geophires_x_client/geophires_x_result.py +++ b/src/geophires_x_client/geophires_x_result.py @@ -717,7 +717,7 @@ def _get_unlabeled_string_field( return None @property - def power_generation_profile(self): + def power_generation_profile(self) -> list[list[str | float]]: return self.result['POWER GENERATION PROFILE'] def _get_power_generation_profile(self): diff --git a/src/geophires_x_schema_generator/geophires-request.json b/src/geophires_x_schema_generator/geophires-request.json index be86aef39..0a0c354a8 100644 --- a/src/geophires_x_schema_generator/geophires-request.json +++ b/src/geophires_x_schema_generator/geophires-request.json @@ -305,7 +305,7 @@ "maximum": 10000 }, "Fracture Width": { - "description": "Width of each fracture", + "description": "Total horizontal length of each fracture plane (from tip to tip). Note: In some contexts this is called 'Fracture Length'; it refers to the fracture's lateral extent, not its aperture or thickness.", "type": "number", "units": "meter", "category": "Reservoir", @@ -664,6 +664,15 @@ "minimum": 0, "maximum": 200 }, + "Number of Injection Wells per Production Well": { + "description": "Number of (identical) injection wells per production well. For example, provide 0.666 to specify a 3:2 production:injection well ratio. The number of injection wells will be rounded up to the nearest integer.", + "type": "number", + "units": null, + "category": "Well Bores", + "default": 1, + "minimum": 0, + "maximum": 199 + }, "Production Well Diameter": { "description": "Inner diameter of production wellbore (assumed constant along the wellbore) to calculate frictional pressure drop and wellbore heat transmission with Rameys model", "type": "number", diff --git a/src/geophires_x_schema_generator/geophires-result.json b/src/geophires_x_schema_generator/geophires-result.json index 9b1e88947..f0db47342 100644 --- a/src/geophires_x_schema_generator/geophires-result.json +++ b/src/geophires_x_schema_generator/geophires-result.json @@ -139,7 +139,7 @@ "Fixed Charge Rate (FCR)": {}, "Project Payback Period": { "type": "number", - "description": "The time at which cumulative cash flow reaches zero. For projects that never pay back, the calculated value will be \"N/A\". For SAM Economic Models, after-tax net cash flow is used to calculate the cumulative cash flow.", + "description": "The time at which cumulative cash flow reaches zero. For projects that never pay back, the calculated value will be \"N/A\". For SAM Economic Models, this is Simple Payback Period (SPB): the time at which cumulative non-discounted cash flow reaches zero, calculated using non-discounted after-tax net cash flow. See https://samrepo.nrelcloud.org/help/mtf_payback.html for important considerations regarding the limitations of this metric.", "units": "yr" }, "CHP: Percent cost allocation for electrical plant": {}, diff --git a/tests/.gitignore b/tests/.gitignore index dd02483df..83ec4ce7b 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,3 +1,5 @@ +*.env + HIP.out *.log MC_*Result.json diff --git a/tests/examples/Fervo_Project_Cape-4.out b/tests/examples/Fervo_Project_Cape-4.out index e85f55118..60d36561c 100644 --- a/tests/examples/Fervo_Project_Cape-4.out +++ b/tests/examples/Fervo_Project_Cape-4.out @@ -4,10 +4,10 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.10.22 - Simulation Date: 2025-12-15 - Simulation Time: 09:15 - Calculation Time: 1.777 sec + GEOPHIRES Version: 3.10.25 + Simulation Date: 2026-01-13 + Simulation Time: 11:08 + Calculation Time: 1.855 sec ***SUMMARY OF RESULTS*** diff --git a/tests/examples/Fervo_Project_Cape-4.txt b/tests/examples/Fervo_Project_Cape-4.txt index d2c1fe956..100882722 100644 --- a/tests/examples/Fervo_Project_Cape-4.txt +++ b/tests/examples/Fervo_Project_Cape-4.txt @@ -1,4 +1,4 @@ -# Case Study: 500 MWe EGS Project Modeled on Fervo Cape Station +# [Deprecated] Case Study: 500 MWe EGS Project Modeled on Fervo Cape Station # 500 MWe EGS Case Study Modeled on Fervo Cape Station after Fervo's April 2025 upsizing announcement: # https://fervoenergy.com/fervo-energy-announces-31-mw-power-purchase-agreement-with-shell-energy/ # See documentation: https://softwareengineerprogrammer.github.io/GEOPHIRES/Fervo_Project_Cape-4.html diff --git a/tests/examples/Fervo_Project_Cape-5.out b/tests/examples/Fervo_Project_Cape-5.out new file mode 100644 index 000000000..691c251a6 --- /dev/null +++ b/tests/examples/Fervo_Project_Cape-5.out @@ -0,0 +1,461 @@ + ***************** + ***CASE REPORT*** + ***************** + +Simulation Metadata +---------------------- + GEOPHIRES Version: 3.10.25 + Simulation Date: 2026-01-14 + Simulation Time: 11:38 + Calculation Time: 1.828 sec + + ***SUMMARY OF RESULTS*** + + End-Use Option: Electricity + Average Net Electricity Production: 536.88 MW + Electricity breakeven price: 7.57 cents/kWh + Total CAPEX: 2859.26 MUSD + Number of production wells: 56 + Number of injection wells: 38 + Flowrate per production well: 107.0 kg/sec + Well depth: 2.7 kilometer + Segment 1 Geothermal gradient: 74 degC/km + Segment 1 Thickness: 2.5 kilometer + Segment 2 Geothermal gradient: 41 degC/km + Segment 2 Thickness: 0.5 kilometer + Segment 3 Geothermal gradient: 39.1 degC/km + + + ***ECONOMIC PARAMETERS*** + + Economic Model = SAM Single Owner PPA + Real Discount Rate: 12.00 % + Nominal Discount Rate: 15.02 % + WACC: 8.31 % + Project lifetime: 30 yr + Capacity factor: 90.0 % + Project NPV: 268.60 MUSD + After-tax IRR: 24.46 % + Project VIR=PI=PIR: 1.45 + Project MOIC: 5.05 + Project Payback Period: 4.93 yr + Estimated Jobs Created: 1269 + + ***ENGINEERING PARAMETERS*** + + Number of Production Wells: 56 + Number of Injection Wells: 38 + Well depth: 2.7 kilometer + Water loss rate: 1.0 % + Pump efficiency: 80.0 % + Injection temperature: 56.6 degC + Production Wellbore heat transmission calculated with Ramey's model + Average production well temperature drop: 0.2 degC + Flowrate per production well: 107.0 kg/sec + Injection well casing ID: 8.535 in + Production well casing ID: 8.535 in + Number of times redrilling: 3 + Power plant type: Supercritical ORC + + + ***RESOURCE CHARACTERISTICS*** + + Maximum reservoir temperature: 500.0 degC + Number of segments: 3 + Segment 1 Geothermal gradient: 74 degC/km + Segment 1 Thickness: 2.5 kilometer + Segment 2 Geothermal gradient: 41 degC/km + Segment 2 Thickness: 0.5 kilometer + Segment 3 Geothermal gradient: 39.1 degC/km + + + ***RESERVOIR PARAMETERS*** + + Reservoir Model = Multiple Parallel Fractures Model (Gringarten) + Bottom-hole temperature: 205.38 degC + Fracture model = Rectangular + Well separation: fracture height: 95.00 meter + Fracture width: 305.00 meter + Fracture area: 28975.00 m**2 + Reservoir volume calculated with fracture separation and number of fractures as input + Number of fractures: 14100 + Fracture separation: 9.83 meter + Reservoir volume: 4013898767 m**3 + Reservoir hydrostatic pressure: 25324.54 kPa + Plant outlet pressure: 13789.51 kPa + Production wellhead pressure: 2082.43 kPa + Productivity Index: 1.75 kg/sec/bar + Injectivity Index: 2.11 kg/sec/bar + Reservoir density: 2800.00 kg/m**3 + Reservoir thermal conductivity: 3.05 W/m/K + Reservoir heat capacity: 790.00 J/kg/K + + + ***RESERVOIR SIMULATION RESULTS*** + + Maximum Production Temperature: 203.3 degC + Average Production Temperature: 202.7 degC + Minimum Production Temperature: 197.9 degC + Initial Production Temperature: 201.9 degC + Average Reservoir Heat Extraction: 3664.25 MW + Production Wellbore Heat Transmission Model = Ramey Model + Average Production Well Temperature Drop: 0.2 degC + Average Injection Well Pump Pressure Drop: -5176.1 kPa + Average Production Well Pump Pressure Drop: 6917.1 kPa + + + ***CAPITAL COSTS (M$)*** + + Drilling and completion costs: 436.98 MUSD + Drilling and completion costs per well: 4.65 MUSD + Stimulation costs: 454.02 MUSD + Surface power plant costs: 1467.52 MUSD + Field gathering system costs: 43.13 MUSD + Total surface equipment costs: 1510.64 MUSD + Exploration costs: 30.00 MUSD + Overnight Capital Cost: 2431.65 MUSD + Interest during construction: 142.34 MUSD + Inflation costs during construction: 285.27 MUSD + Total CAPEX: 2859.26 MUSD + + + ***OPERATING AND MAINTENANCE COSTS (M$/yr)*** + + Wellfield maintenance costs: 5.75 MUSD/yr + Power plant maintenance costs: 24.87 MUSD/yr + Water costs: 3.15 MUSD/yr + Redrilling costs: 89.10 MUSD/yr + Total operating and maintenance costs: 122.87 MUSD/yr + + + ***SURFACE EQUIPMENT SIMULATION RESULTS*** + + Initial geofluid availability: 0.19 MW/(kg/s) + Maximum Total Electricity Generation: 599.67 MW + Average Total Electricity Generation: 595.97 MW + Minimum Total Electricity Generation: 561.22 MW + Initial Total Electricity Generation: 589.90 MW + Maximum Net Electricity Generation: 540.69 MW + Average Net Electricity Generation: 536.88 MW + Minimum Net Electricity Generation: 501.23 MW + Initial Net Electricity Generation: 530.73 MW + Average Annual Total Electricity Generation: 4698.75 GWh + Average Annual Net Electricity Generation: 4232.89 GWh + Initial pumping power/net installed power: 11.15 % + Average Pumping Power: 59.09 MW + Heat to Power Conversion Efficiency: 14.65 % + + ************************************************************ + * HEATING, COOLING AND/OR ELECTRICITY PRODUCTION PROFILE * + ************************************************************ + YEAR THERMAL GEOFLUID PUMP NET FIRST LAW + DRAWDOWN TEMPERATURE POWER POWER EFFICIENCY + (degC) (MW) (MW) (%) + 1 1.0000 201.89 59.1637 530.7347 14.5683 + 2 1.0049 202.87 59.1237 537.8447 14.6641 + 3 1.0058 203.06 59.1163 539.1668 14.6818 + 4 1.0063 203.15 59.1124 539.8546 14.6910 + 5 1.0066 203.21 59.1099 540.3081 14.6970 + 6 1.0067 203.25 59.1098 540.5619 14.7004 + 7 1.0060 203.10 59.1388 539.4687 14.6855 + 8 0.9995 201.80 59.3570 529.8806 14.5541 + 9 1.0000 201.89 59.0983 530.8001 14.5701 + 10 1.0050 202.90 59.0862 538.0445 14.6673 + 11 1.0058 203.07 59.0664 539.2881 14.6841 + 12 1.0063 203.16 59.0422 539.9690 14.6935 + 13 1.0066 203.22 59.0191 540.4296 14.6999 + 14 1.0067 203.25 59.0038 540.6662 14.7033 + 15 1.0058 203.06 59.0298 539.2898 14.6846 + 16 0.9984 201.56 59.2774 528.2830 14.5337 + 17 1.0017 202.23 58.9814 533.3905 14.6066 + 18 1.0051 202.92 58.9804 538.2979 14.6721 + 19 1.0059 203.08 58.9802 539.4425 14.6873 + 20 1.0063 203.16 58.9802 540.0738 14.6957 + 21 1.0066 203.22 58.9804 540.4980 14.7014 + 22 1.0067 203.25 58.9838 540.6768 14.7037 + 23 1.0056 203.01 59.0289 538.9462 14.6801 + 24 0.9971 201.30 59.3179 526.3599 14.5071 + 25 1.0026 202.41 58.9810 534.6246 14.6231 + 26 1.0052 202.94 58.9811 538.4325 14.6739 + 27 1.0059 203.09 58.9812 539.5064 14.6882 + 28 1.0063 203.17 58.9814 540.1142 14.6962 + 29 1.0066 203.23 58.9816 540.5254 14.7017 + 30 1.0067 203.25 58.9858 540.6561 14.7034 + + + ******************************************************************* + * ANNUAL HEATING, COOLING AND/OR ELECTRICITY PRODUCTION PROFILE * + ******************************************************************* + YEAR ELECTRICITY HEAT RESERVOIR PERCENTAGE OF + PROVIDED EXTRACTED HEAT CONTENT TOTAL HEAT MINED + (GWh/year) (GWh/year) (10^15 J) (%) + 1 4222.0 28852.7 1217.11 7.86 + 2 4246.3 28937.3 1112.94 15.75 + 3 4253.7 28963.0 1008.67 23.64 + 4 4258.1 28978.2 904.35 31.54 + 5 4261.0 28988.2 799.99 39.44 + 6 4259.5 28983.2 695.65 47.34 + 7 4225.7 28868.2 591.72 55.21 + 8 4090.0 28402.1 489.48 62.95 + 9 4227.0 28869.2 385.55 70.81 + 10 4247.5 28940.2 281.36 78.70 + 11 4254.6 28964.5 177.09 86.59 + 12 4259.0 28979.2 72.77 94.49 + 13 4261.9 28988.8 -31.59 102.39 + 14 4259.5 28980.4 -135.92 110.29 + 15 4219.9 28845.1 -239.77 118.15 + 16 4092.9 28408.4 -342.04 125.89 + 17 4231.9 28883.1 -446.01 133.76 + 18 4249.0 28942.9 -550.21 141.65 + 19 4255.6 28966.0 -654.49 149.55 + 20 4259.7 28980.2 -758.82 157.44 + 21 4262.3 28989.2 -863.18 165.34 + 22 4258.6 28976.9 -967.49 173.24 + 23 4212.2 28818.7 -1071.24 181.09 + 24 4097.3 28423.0 -1173.56 188.84 + 25 4234.7 28893.1 -1277.58 196.71 + 26 4249.7 28945.4 -1381.78 204.60 + 27 4256.0 28967.4 -1486.07 212.50 + 28 4260.0 28981.1 -1590.40 220.40 + 29 4262.4 28989.6 -1694.76 228.30 + 30 4258.3 28975.7 -1799.07 236.19 + + *************************** + * SAM CASH FLOW PROFILE * + *************************** +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Year -4 Year -3 Year -2 Year -1 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 +CONSTRUCTION +Capital expenditure schedule [construction] (%) 1.40 2.70 13.90 43.10 38.90 +Overnight capital expenditure [construction] ($) -34,043,049 -65,654,451 -337,998,843 -1,048,039,577 -945,910,430 +plus: +Inflation cost [construction] ($) -919,162 -3,593,202 -28,123,763 -117,855,471 -134,782,306 +equals: +Nominal capital expenditure [construction] ($) -34,962,211 -69,247,654 -366,122,605 -1,165,895,048 -1,080,692,736 + +Issuance of equity [construction] ($) 34,962,211 69,247,654 109,836,782 349,768,514 324,207,821 +Issuance of debt [construction] ($) 0 0 256,285,824 816,126,533 756,484,915 +Debt balance [construction] ($) 0 0 256,285,824 1,099,322,368 1,971,236,132 +Debt interest payment [construction] ($) 0 0 0 26,910,011 115,428,849 + +Installed cost [construction] ($) -34,962,211 -69,247,654 -366,122,605 -1,192,805,059 -1,196,121,585 +After-tax net cash flow [construction] ($) -34,962,211 -69,247,654 -109,836,782 -349,768,514 -324,207,821 + +ENERGY +Electricity to grid (kWh) 0.0 4,222,308,474 4,246,656,805 4,254,077,539 4,258,464,138 4,261,361,983 4,259,838,404 4,226,101,564 4,090,369,692 4,227,353,669 4,247,826,578 4,254,988,701 4,259,392,408 4,262,298,892 4,259,905,427 4,220,263,261 4,093,268,367 4,232,225,414 4,249,366,482 4,255,997,698 4,260,070,210 4,262,659,891 4,258,995,778 4,212,556,835 4,097,673,094 4,235,086,416 4,250,088,054 4,256,396,046 4,260,335,780 4,262,752,890 4,258,625,740 +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,222,308,474 4,246,656,805 4,254,077,539 4,258,464,138 4,261,361,983 4,259,838,404 4,226,101,564 4,090,369,692 4,227,353,669 4,247,826,578 4,254,988,701 4,259,392,408 4,262,298,892 4,259,905,427 4,220,263,261 4,093,268,367 4,232,225,414 4,249,366,482 4,255,997,698 4,260,070,210 4,262,659,891 4,258,995,778 4,212,556,835 4,097,673,094 4,235,086,416 4,250,088,054 4,256,396,046 4,260,335,780 4,262,752,890 4,258,625,740 + +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 401,119,305 403,432,396 406,562,190 409,408,742 412,116,317 414,397,080 413,524,038 402,574,185 418,465,740 422,913,614 426,052,019 428,920,815 431,643,009 433,828,769 432,197,161 421,524,776 438,246,942 442,444,038 445,560,399 448,414,990 451,117,296 453,157,151 450,617,205 440,663,765 457,855,192 461,899,570 465,011,268 467,870,075 470,565,292 472,537,112 +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,429,629,557 +Total revenue ($) 0 401,119,305 403,432,396 406,562,190 409,408,742 412,116,317 414,397,080 413,524,038 402,574,185 418,465,740 422,913,614 426,052,019 428,920,815 431,643,009 433,828,769 432,197,161 421,524,776 438,246,942 442,444,038 445,560,399 448,414,990 451,117,296 453,157,151 450,617,205 440,663,765 457,855,192 461,899,570 465,011,268 467,870,075 470,565,292 1,902,166,669 + +Property tax net assessed value ($) 0 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 2,859,259,114 + +OPERATING EXPENSES +O&M fixed expense ($) 0 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 122,870,223 +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 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 6,290,370 +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 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 129,160,593 + +EBITDA ($) 0 271,958,712 274,271,803 277,401,597 280,248,149 282,955,724 285,236,487 284,363,445 273,413,592 289,305,147 293,753,021 296,891,426 299,760,222 302,482,416 304,668,176 303,036,568 292,364,183 309,086,349 313,283,445 316,399,806 319,254,397 321,956,703 323,996,558 321,456,612 311,503,172 328,694,599 332,738,977 335,850,675 338,709,482 341,404,699 1,773,006,076 + +OPERATING ACTIVITIES +EBITDA ($) 0 271,958,712 274,271,803 277,401,597 280,248,149 282,955,724 285,236,487 284,363,445 273,413,592 289,305,147 293,753,021 296,891,426 299,760,222 302,482,416 304,668,176 303,036,568 292,364,183 309,086,349 313,283,445 316,399,806 319,254,397 321,956,703 323,996,558 321,456,612 311,503,172 328,694,599 332,738,977 335,850,675 338,709,482 341,404,699 1,773,006,076 +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 137,986,529 136,525,748 134,962,712 133,290,264 131,500,745 129,585,959 127,537,138 125,344,899 122,999,204 120,489,310 117,803,724 114,930,146 111,855,418 108,565,459 105,045,203 101,278,529 97,248,188 92,935,723 88,321,386 83,384,045 78,101,090 72,448,328 66,399,873 59,928,026 53,003,149 45,593,532 37,665,241 29,181,970 20,104,869 10,392,372 +Cash flow from operating activities ($) 0 133,972,183 137,746,055 142,438,885 146,957,885 151,454,980 155,650,528 156,826,307 148,068,693 166,305,943 173,263,711 179,087,702 184,830,076 190,626,998 196,102,716 197,991,364 191,085,654 211,838,160 220,347,722 228,078,420 235,870,352 243,855,613 251,548,230 255,056,739 251,575,146 275,691,450 287,145,445 298,185,434 309,527,513 321,299,829 1,762,613,704 + +INVESTING ACTIVITIES +Total installed cost ($) -2,859,259,114 +Debt closing costs ($) 0 +Debt up-front fee ($) 0 +minus: +Total IBI income ($) 0 +Total CBI income ($) 0 +equals: +Purchase of property ($) -2,859,259,114 +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 +Reserve (increase)/decrease receivables ($) 0 0 0 0 0 0 0 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 major equipment 1 ($) 0 0 0 0 0 0 0 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 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 (increase)/decrease 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 +Reserve capital spending major equipment 1 ($) 0 0 0 0 0 0 0 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 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,859,259,114 0 0 0 0 0 0 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 ($) 888,022,982 +Size of debt ($) 1,971,236,132 +minus: +Debt principal payment ($) 0 20,868,301 22,329,082 23,892,118 25,564,566 27,354,086 29,268,872 31,317,693 33,509,931 35,855,627 38,365,520 41,051,107 43,924,684 46,999,412 50,289,371 53,809,627 57,576,301 61,606,642 65,919,107 70,533,444 75,470,786 80,753,741 86,406,502 92,454,958 98,926,805 105,851,681 113,261,299 121,189,590 129,672,861 138,749,961 148,462,458 +equals: +Cash flow from financing activities ($) 2,859,259,114 -20,868,301 -22,329,082 -23,892,118 -25,564,566 -27,354,086 -29,268,872 -31,317,693 -33,509,931 -35,855,627 -38,365,520 -41,051,107 -43,924,684 -46,999,412 -50,289,371 -53,809,627 -57,576,301 -61,606,642 -65,919,107 -70,533,444 -75,470,786 -80,753,741 -86,406,502 -92,454,958 -98,926,805 -105,851,681 -113,261,299 -121,189,590 -129,672,861 -138,749,961 -148,462,458 + +PROJECT RETURNS +Pre-tax Cash Flow: +Cash flow from operating activities ($) 0 133,972,183 137,746,055 142,438,885 146,957,885 151,454,980 155,650,528 156,826,307 148,068,693 166,305,943 173,263,711 179,087,702 184,830,076 190,626,998 196,102,716 197,991,364 191,085,654 211,838,160 220,347,722 228,078,420 235,870,352 243,855,613 251,548,230 255,056,739 251,575,146 275,691,450 287,145,445 298,185,434 309,527,513 321,299,829 1,762,613,704 +Cash flow from investing activities ($) -2,859,259,114 0 0 0 0 0 0 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,859,259,114 -20,868,301 -22,329,082 -23,892,118 -25,564,566 -27,354,086 -29,268,872 -31,317,693 -33,509,931 -35,855,627 -38,365,520 -41,051,107 -43,924,684 -46,999,412 -50,289,371 -53,809,627 -57,576,301 -61,606,642 -65,919,107 -70,533,444 -75,470,786 -80,753,741 -86,406,502 -92,454,958 -98,926,805 -105,851,681 -113,261,299 -121,189,590 -129,672,861 -138,749,961 -148,462,458 +Total pre-tax cash flow ($) 0 113,103,882 115,416,973 118,546,767 121,393,319 124,100,894 126,381,657 125,508,615 114,558,762 130,450,316 134,898,191 138,036,595 140,905,392 143,627,585 145,813,345 144,181,737 133,509,353 150,231,518 154,428,615 157,544,976 160,399,567 163,101,873 165,141,727 162,601,781 152,648,341 169,839,769 173,884,146 176,995,845 179,854,652 182,549,868 1,614,151,246 + +Pre-tax Returns: +Issuance of equity ($) 888,022,982 +Total pre-tax cash flow ($) 0 113,103,882 115,416,973 118,546,767 121,393,319 124,100,894 126,381,657 125,508,615 114,558,762 130,450,316 134,898,191 138,036,595 140,905,392 143,627,585 145,813,345 144,181,737 133,509,353 150,231,518 154,428,615 157,544,976 160,399,567 163,101,873 165,141,727 162,601,781 152,648,341 169,839,769 173,884,146 176,995,845 179,854,652 182,549,868 1,614,151,246 +Total pre-tax returns ($) -888,022,982 113,103,882 115,416,973 118,546,767 121,393,319 124,100,894 126,381,657 125,508,615 114,558,762 130,450,316 134,898,191 138,036,595 140,905,392 143,627,585 145,813,345 144,181,737 133,509,353 150,231,518 154,428,615 157,544,976 160,399,567 163,101,873 165,141,727 162,601,781 152,648,341 169,839,769 173,884,146 176,995,845 179,854,652 182,549,868 1,614,151,246 + +After-tax Returns: +Total pre-tax returns ($) -888,022,982 113,103,882 115,416,973 118,546,767 121,393,319 124,100,894 126,381,657 125,508,615 114,558,762 130,450,316 134,898,191 138,036,595 140,905,392 143,627,585 145,813,345 144,181,737 133,509,353 150,231,518 154,428,615 157,544,976 160,399,567 163,101,873 165,141,727 162,601,781 152,648,341 169,839,769 173,884,146 176,995,845 179,854,652 182,549,868 1,614,151,246 +Federal ITC total income ($) 0 857,777,734 0 0 0 0 0 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 -14,675,165 -3,252,730 -4,193,384 -5,099,195 -6,000,615 -6,841,592 -7,077,271 -5,321,851 -8,977,417 -10,372,066 -11,539,456 -12,690,486 -13,852,450 -14,950,031 -15,328,601 -13,944,386 -18,104,122 -19,809,821 -21,359,401 -22,921,255 -36,700,749 -50,421,585 -51,124,848 -50,426,980 -55,260,973 -57,556,869 -59,769,779 -62,043,242 -64,402,944 -353,307,104 +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 -3,331,188 -738,353 -951,877 -1,157,491 -1,362,109 -1,553,007 -1,606,505 -1,208,033 -2,037,828 -2,354,407 -2,619,398 -2,880,676 -3,144,436 -3,393,581 -3,479,515 -3,165,305 -4,109,544 -4,496,729 -4,848,476 -5,203,009 -8,330,884 -11,445,444 -11,605,082 -11,446,669 -12,543,961 -13,065,118 -13,567,437 -14,083,502 -14,619,142 -80,198,924 +Total after-tax returns ($) -888,022,982 952,875,263 111,425,890 113,401,506 115,136,632 116,738,169 117,987,058 116,824,839 108,028,877 119,435,072 122,171,718 123,877,741 125,334,229 126,630,699 127,469,733 125,373,622 116,399,662 128,017,852 130,122,065 131,337,099 132,275,304 118,070,239 103,274,698 99,871,852 90,774,692 102,034,835 103,262,160 103,658,628 103,727,908 103,527,782 1,180,645,218 + +After-tax net cash flow ($) -34,962,211 -69,247,654 -109,836,782 -349,768,514 -324,207,821 952,875,263 111,425,890 113,401,506 115,136,632 116,738,169 117,987,058 116,824,839 108,028,877 119,435,072 122,171,718 123,877,741 125,334,229 126,630,699 127,469,733 125,373,622 116,399,662 128,017,852 130,122,065 131,337,099 132,275,304 118,070,239 103,274,698 99,871,852 90,774,692 102,034,835 103,262,160 103,658,628 103,727,908 103,527,782 1,180,645,218 +After-tax cumulative IRR (%) NaN NaN NaN NaN NaN 3.49 8.65 12.65 15.63 17.82 19.44 20.61 21.42 22.09 22.61 23.01 23.32 23.57 23.76 23.90 24.01 24.10 24.18 24.24 24.28 24.32 24.34 24.36 24.37 24.38 24.39 24.40 24.41 24.41 24.46 +After-tax cumulative NPV ($) -34,962,211 -95,164,998 -178,182,731 -408,017,280 -593,229,494 -119,976,117 -71,863,906 -29,294,307 8,281,281 41,403,291 70,507,092 95,560,226 115,701,098 135,060,049 152,276,045 167,452,358 180,801,527 192,527,127 202,788,718 211,563,277 218,645,709 225,417,643 231,401,824 236,652,953 241,250,811 244,818,843 247,532,118 249,813,272 251,615,823 253,377,325 254,927,166 256,279,746 257,456,443 258,477,471 268,600,525 + +AFTER-TAX LCOE AND PPA PRICE +Annual costs ($) -888,022,982 551,755,958 -292,006,506 -293,160,684 -294,272,110 -295,378,148 -296,410,022 -296,699,199 -294,545,308 -299,030,668 -300,741,896 -302,174,278 -303,586,586 -305,012,310 -306,359,035 -306,823,539 -305,125,114 -310,229,089 -312,321,973 -314,223,300 -316,139,687 -333,047,057 -349,882,453 -350,745,353 -349,889,073 -355,820,357 -358,637,410 -361,352,640 -364,142,168 -367,037,510 708,108,106 +PPA revenue ($) 0 401,119,305 403,432,396 406,562,190 409,408,742 412,116,317 414,397,080 413,524,038 402,574,185 418,465,740 422,913,614 426,052,019 428,920,815 431,643,009 433,828,769 432,197,161 421,524,776 438,246,942 442,444,038 445,560,399 448,414,990 451,117,296 453,157,151 450,617,205 440,663,765 457,855,192 461,899,570 465,011,268 467,870,075 470,565,292 472,537,112 +Electricity to grid (kWh) 0.0 4,222,308,474 4,246,656,805 4,254,077,539 4,258,464,138 4,261,361,983 4,259,838,404 4,226,101,564 4,090,369,692 4,227,353,669 4,247,826,578 4,254,988,701 4,259,392,408 4,262,298,892 4,259,905,427 4,220,263,261 4,093,268,367 4,232,225,414 4,249,366,482 4,255,997,698 4,260,070,210 4,262,659,891 4,258,995,778 4,212,556,835 4,097,673,094 4,235,086,416 4,250,088,054 4,256,396,046 4,260,335,780 4,262,752,890 4,258,625,740 + +Present value of annual costs ($) 2,101,467,107 +Present value of annual energy nominal (kWh) 27,766,031,299 +LCOE Levelized cost of energy nominal (cents/kWh) 7.57 + +Present value of PPA revenue ($) 2,722,048,915 +Present value of annual energy nominal (kWh) 27,766,031,299 +LPPA Levelized PPA price nominal (cents/kWh) 9.80 + +PROJECT STATE INCOME TAXES +EBITDA ($) 0 271,958,712 274,271,803 277,401,597 280,248,149 282,955,724 285,236,487 284,363,445 273,413,592 289,305,147 293,753,021 296,891,426 299,760,222 302,482,416 304,668,176 303,036,568 292,364,183 309,086,349 313,283,445 316,399,806 319,254,397 321,956,703 323,996,558 321,456,612 311,503,172 328,694,599 332,738,977 335,850,675 338,709,482 341,404,699 1,773,006,076 +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 137,986,529 136,525,748 134,962,712 133,290,264 131,500,745 129,585,959 127,537,138 125,344,899 122,999,204 120,489,310 117,803,724 114,930,146 111,855,418 108,565,459 105,045,203 101,278,529 97,248,188 92,935,723 88,321,386 83,384,045 78,101,090 72,448,328 66,399,873 59,928,026 53,003,149 45,593,532 37,665,241 29,181,970 20,104,869 10,392,372 +Total state tax depreciation ($) 0 60,759,256 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 60,759,256 0 0 0 0 0 0 0 0 0 +equals: +State taxable income ($) 0 73,212,927 16,227,543 20,920,373 25,439,373 29,936,467 34,132,016 35,307,795 26,550,181 44,787,431 51,745,199 57,569,190 63,311,564 69,108,485 74,584,204 76,472,852 69,567,142 90,319,648 98,829,209 106,559,908 114,351,840 183,096,357 251,548,230 255,056,739 251,575,146 275,691,450 287,145,445 298,185,434 309,527,513 321,299,829 1,762,613,704 + +State income tax rate (frac) 0.0 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 +State tax benefit (liability) ($) 0 -3,331,188 -738,353 -951,877 -1,157,491 -1,362,109 -1,553,007 -1,606,505 -1,208,033 -2,037,828 -2,354,407 -2,619,398 -2,880,676 -3,144,436 -3,393,581 -3,479,515 -3,165,305 -4,109,544 -4,496,729 -4,848,476 -5,203,009 -8,330,884 -11,445,444 -11,605,082 -11,446,669 -12,543,961 -13,065,118 -13,567,437 -14,083,502 -14,619,142 -80,198,924 + +PROJECT FEDERAL INCOME TAXES +EBITDA ($) 0 271,958,712 274,271,803 277,401,597 280,248,149 282,955,724 285,236,487 284,363,445 273,413,592 289,305,147 293,753,021 296,891,426 299,760,222 302,482,416 304,668,176 303,036,568 292,364,183 309,086,349 313,283,445 316,399,806 319,254,397 321,956,703 323,996,558 321,456,612 311,503,172 328,694,599 332,738,977 335,850,675 338,709,482 341,404,699 1,773,006,076 +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 -3,331,188 -738,353 -951,877 -1,157,491 -1,362,109 -1,553,007 -1,606,505 -1,208,033 -2,037,828 -2,354,407 -2,619,398 -2,880,676 -3,144,436 -3,393,581 -3,479,515 -3,165,305 -4,109,544 -4,496,729 -4,848,476 -5,203,009 -8,330,884 -11,445,444 -11,605,082 -11,446,669 -12,543,961 -13,065,118 -13,567,437 -14,083,502 -14,619,142 -80,198,924 +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 137,986,529 136,525,748 134,962,712 133,290,264 131,500,745 129,585,959 127,537,138 125,344,899 122,999,204 120,489,310 117,803,724 114,930,146 111,855,418 108,565,459 105,045,203 101,278,529 97,248,188 92,935,723 88,321,386 83,384,045 78,101,090 72,448,328 66,399,873 59,928,026 53,003,149 45,593,532 37,665,241 29,181,970 20,104,869 10,392,372 +Total federal tax depreciation ($) 0 60,759,256 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 121,518,512 60,759,256 0 0 0 0 0 0 0 0 0 +equals: +Federal taxable income ($) 0 69,881,738 15,489,190 19,968,496 24,281,881 28,574,358 32,579,009 33,701,290 25,342,147 42,749,602 49,390,792 54,949,792 60,430,888 65,964,049 71,190,623 72,993,337 66,401,837 86,210,104 94,332,480 101,711,432 109,148,831 174,765,473 240,102,785 243,451,657 240,128,477 263,147,489 274,080,327 284,617,997 295,444,011 306,680,687 1,682,414,780 + +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 -14,675,165 -3,252,730 -4,193,384 -5,099,195 -6,000,615 -6,841,592 -7,077,271 -5,321,851 -8,977,417 -10,372,066 -11,539,456 -12,690,486 -13,852,450 -14,950,031 -15,328,601 -13,944,386 -18,104,122 -19,809,821 -21,359,401 -22,921,255 -36,700,749 -50,421,585 -51,124,848 -50,426,980 -55,260,973 -57,556,869 -59,769,779 -62,043,242 -64,402,944 -353,307,104 + +CASH INCENTIVES +Federal IBI income ($) 0 +State IBI income ($) 0 +Utility IBI income ($) 0 +Other IBI income ($) 0 +Total IBI income ($) 0 + +Federal CBI income ($) 0 +State CBI income ($) 0 +Utility CBI income ($) 0 +Other CBI income ($) 0 +Total CBI income ($) 0 + +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 +Total 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 + +TAX CREDITS +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 +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 857,777,734 0 0 0 0 0 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 857,777,734 0 0 0 0 0 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,971,236,132 1,950,367,831 1,928,038,749 1,904,146,631 1,878,582,065 1,851,227,979 1,821,959,107 1,790,641,414 1,757,131,483 1,721,275,856 1,682,910,336 1,641,859,229 1,597,934,545 1,550,935,133 1,500,645,761 1,446,836,134 1,389,259,833 1,327,653,191 1,261,734,084 1,191,200,640 1,115,729,854 1,034,976,114 948,569,611 856,114,654 757,187,849 651,336,168 538,074,870 416,885,280 287,212,419 148,462,458 0 +Debt interest payment ($) 0 137,986,529 136,525,748 134,962,712 133,290,264 131,500,745 129,585,959 127,537,138 125,344,899 122,999,204 120,489,310 117,803,724 114,930,146 111,855,418 108,565,459 105,045,203 101,278,529 97,248,188 92,935,723 88,321,386 83,384,045 78,101,090 72,448,328 66,399,873 59,928,026 53,003,149 45,593,532 37,665,241 29,181,970 20,104,869 10,392,372 +Debt principal payment ($) 0 20,868,301 22,329,082 23,892,118 25,564,566 27,354,086 29,268,872 31,317,693 33,509,931 35,855,627 38,365,520 41,051,107 43,924,684 46,999,412 50,289,371 53,809,627 57,576,301 61,606,642 65,919,107 70,533,444 75,470,786 80,753,741 86,406,502 92,454,958 98,926,805 105,851,681 113,261,299 121,189,590 129,672,861 138,749,961 148,462,458 +Debt total payment ($) 0 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 + +DSCR (DEBT FRACTION) +EBITDA ($) 0 271,958,712 274,271,803 277,401,597 280,248,149 282,955,724 285,236,487 284,363,445 273,413,592 289,305,147 293,753,021 296,891,426 299,760,222 302,482,416 304,668,176 303,036,568 292,364,183 309,086,349 313,283,445 316,399,806 319,254,397 321,956,703 323,996,558 321,456,612 311,503,172 328,694,599 332,738,977 335,850,675 338,709,482 341,404,699 1,773,006,076 +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 271,958,712 274,271,803 277,401,597 280,248,149 282,955,724 285,236,487 284,363,445 273,413,592 289,305,147 293,753,021 296,891,426 299,760,222 302,482,416 304,668,176 303,036,568 292,364,183 309,086,349 313,283,445 316,399,806 319,254,397 321,956,703 323,996,558 321,456,612 311,503,172 328,694,599 332,738,977 335,850,675 338,709,482 341,404,699 1,773,006,076 +Debt total payment ($) 0 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 158,854,830 +DSCR (pre-tax) 0.0 1.71 1.73 1.75 1.76 1.78 1.80 1.79 1.72 1.82 1.85 1.87 1.89 1.90 1.92 1.91 1.84 1.95 1.97 1.99 2.01 2.03 2.04 2.02 1.96 2.07 2.09 2.11 2.13 2.15 11.16 + +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 +Reserves working capital disbursement ($) 0 0 0 0 0 0 0 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 working capital balance ($) 0 0 0 0 0 0 0 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 debt service 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 debt service disbursement ($) 0 0 0 0 0 0 0 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 debt service balance ($) 0 0 0 0 0 0 0 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 +Reserves receivables disbursement ($) 0 0 0 0 0 0 0 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 balance ($) 0 0 0 0 0 0 0 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 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 1 disbursement ($) 0 0 0 0 0 0 0 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 1 balance ($) 0 0 0 0 0 0 0 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 2 disbursement ($) 0 0 0 0 0 0 0 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 balance ($) 0 0 0 0 0 0 0 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 major equipment 3 disbursement ($) 0 0 0 0 0 0 0 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 balance ($) 0 0 0 0 0 0 0 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 total reserves balance ($) 0 0 0 0 0 0 0 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 on reserves (%/year) 1.75 +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 +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/tests/examples/Fervo_Project_Cape-5.txt b/tests/examples/Fervo_Project_Cape-5.txt new file mode 100644 index 000000000..eb62e249b --- /dev/null +++ b/tests/examples/Fervo_Project_Cape-5.txt @@ -0,0 +1,116 @@ +# Case Study: 500 MWe EGS Project Modeled on Fervo Cape Station Phase II +# See documentation: https://softwareengineerprogrammer.github.io/GEOPHIRES/Fervo_Project_Cape-5.html + +# *** ECONOMIC/FINANCIAL PARAMETERS *** +# ************************************* +Economic Model, 5, -- 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, .027, -- US inflation as of December 2025 + +Starting Electricity Sale Price, 0.095, -- Aligns with Geysers - Sacramento pricing in [2024b ATB](https://atb.nrel.gov/electricity/2024/geothermal) (NREL, 2025). See Sensitivity Analysis for effect of different prices on results. +Electricity Escalation Rate Per Year, 0.00057, -- Calibrated to reach 10¢/kWh at project year 11 +Ending Electricity Sale Price, 1, -- Note that this value does not directly determine price at the end of the project life, but rather as a cap as the maximum price to which the starting price can escalate. +Electricity Escalation Start Year, 1 + +Fraction of Investment in Bonds, .7, -- Approximate debt required to cover CAPEX after $1 billion sponsor equity per [Matson, 2024](https://www.linkedin.com/pulse/fervo-energy-technology-day-2024-entering-geothermal-decade-matson-n4stc/). 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 precisely. +Discount Rate, 0.12, -- Typical discount rates for higher-risk projects may be 12–15%. +Inflated Bond Interest Rate, .07, -- 2024b ATB (NREL, 2025) + +Inflated Bond Interest Rate During Construction, 0.105, -- Higher than interest rate during normal operation to account for increased risk of default prior to COD. Value aligns with ATB discount rate (NREL, 2025). +Bond Financing Start Year, -2, -- Equity-only for first 2 construction years (ATB) + +Construction Years, 5, -- Ground broken in 2023 (Fervo Energy, 2023). Expected to reach full scale production in 2028 (Fervo Energy, 2025). See [GEOPHIRES documentation](SAM-EM_Multiple-Construction-Years.html) for details on how construction years affect CAPEX, IRR, and other calculations. + +# ATB advanced scenario +# Construction CAPEX Schedule, 0.09,0.28,0.1,0.34,0.28 + +# DOE scenario (alternative) +# Construction CAPEX Schedule, 0.014,0.027,0.137,0.274,0.548 + +# DOE-ATB hybrid scenario +Construction CAPEX Schedule, 0.014,0.027,0.139,0.431,0.389 + +Investment Tax Credit Rate, 0.3, -- Geothermal Drilling and Completions Apprenticeship Program ensures compliance with ITC labor requirements (Southern Utah University, 2024). +Combined Income Tax Rate, .2555, -- Federal Corporate Income Tax Rate of 21% plus Utah Corporate Franchise and Income Tax Rate of 4.55%. (Note: This input uses a simple summation of statutory rates; the effective combined rate calculated in the model may differ due to standard federal-state tax interactions.) +Property Tax Rate, 0.0022, -- Utah Inland Port Authority (UIPA) tax differential incentive + +Capital Cost for Power Plant for Electricity Generation, 1900, -- [US DOE, 2021](https://betterbuildingssolutioncenter.energy.gov/sites/default/files/attachments/Waste_Heat_to_Power_Fact_Sheet.pdf). Pricing information not publicly available for Turboden or Baker Hughes Gen 2 ORC units (Turboden, 2025; Jacobs, 2025). +Exploration Capital Cost, 30, -- Equivalent to 2024b ATB NF-EGS conservative scenario exploration assumption of 5 full-size wells (NREL, 2025), plus $1M for geophysical and field work, plus 15% contingency, plus 12% indirect costs. + +Well Drilling Cost Correlation, 3, -- 2025 NREL Geothermal Drilling Cost Curve Update (Akindipe and Witter, 2025). +Well Drilling and Completion Capital Cost Adjustment Factor, 0.9, -- 2024b Geothermal ATB ([NREL, 2025](https://atb.nrel.gov/electricity/2024b/geothermal)). Note: Fervo has claimed lower drilling costs equivalent to an adjustment factor of 0.8 (Latimer, 2025); the case study conservatively uses the higher ATB-aligned value. + +Reservoir Stimulation Capital Cost per Injection Well, 4, -- The baseline stimulation cost is calibrated from costs of high-intensity U.S. shale wells (Baytex Energy, 2024; Quantum Proppant Technologies, 2020), which are the closest technological analogue for multi-stage EGS (Gradl, 2018). Costs are also driven by the requirement for high-strength ceramic proppant rather than standard sand, which would crush or chemically degrade (diagenesis) over a 30-year lifecycle at 200℃ (Ko et al., 2023; Shiozawa & McClure, 2014) and the premium for ultra-high-temperature (HT) downhole tools. Note that all-in costs per well are higher than the baseline cost because they include additional indirect costs and contingency. +Reservoir Stimulation Capital Cost per Production Well, 4, -- See Reservoir Stimulation Capital Cost per Injection Well + +Field Gathering System Capital Cost Adjustment Factor, 0.54, -- Gathering costs represent 2% of facilities CAPEX per [Matson, 2024](https://www.linkedin.com/pulse/fervo-energy-technology-day-2024-entering-geothermal-decade-matson-n4stc/). + +# *** SURFACE & SUBSURFACE TECHNICAL PARAMETERS *** +# ************************************************* +End-Use Option, 1, -- Electricity +Power Plant Type, 2, -- Supercritical ORC +Plant Lifetime, 30, -- 30-year well life per [Geothermal Mythbusting: Water Use and Impacts](https://fervoenergy.com/geothermal-mythbusting-water-use-and-impacts/) (Fervo Energy, 2025). + +Reservoir Model, 1 + +Surface Temperature, 13, -- Surface temperature near Milford, UT (38.4987670, -112.9163432) ([Project InnerSpace, 2025](https://geomap.projectinnerspace.org/test/)). + +Number of Segments, 3 +Gradient 1, 74, -- Sedimentary overburden. 200℃ at 8500 ft depth (Fercho et al. 2024); 228.89℃ at 9824 ft (Norbeck et al. 2024). +Thickness 1, 2.5 +Gradient 2, 41, -- Crystalline reservoir +Thickness 2, 0.5 +Gradient 3, 39.1, -- Sugarloaf appraisal + +Reservoir Depth, 2.68, -- Extrapolated from surface temperature, gradient, and average production temperature of shallower and deeper producers in Singh et al., 2025. + +Reservoir Density, 2800, -- phyllite + quartzite + diorite + granodiorite ([Norbeck et al., 2023](https://doi.org/10.31223/X52X0B)) +Reservoir Heat Capacity, 790 +Reservoir Thermal Conductivity, 3.05 +Reservoir Porosity, 0.0118 + +Reservoir Volume Option, 1, -- FRAC_NUM_SEP: Reservoir volume calculated with fracture separation and number of fractures as input + +Number of Fractures per Stimulated Well, 150, -- The model assumes an Extreme Limited Entry stimulation design (Fervo Energy, 2023) utilizing 12 stages with 15 clusters per stage (derived from Singh et al., 2025) and 81–85% stimulation success rate per 2024b ATB Moderate Scenario (NREL, 2025). +Fracture Separation, 9.8255, -- Based on 30 foot cluster spacing (Singh et al., 2025) marginally uprated to align with long-term thermal decline behavior trend towards wider fracture spacing (Fercho et al., 2025). + +Fracture Shape, 4, -- Bench design and fracture geometry Singh et al., 2025 are given in rectangular dimensions. +Fracture Width, 305, -- Matches intra-bench well spacing of 500 ft (corresponding to fracture length of 1000 ft) (Singh. et al., 2025) +Fracture Height, 95, -- Actual fracture geometry is irregular and heterogeneous; this height complies with the minimum height required by the implemented bench design (200 ft; 60.96 meters) and yields an effective fracture surface area consistent with simulation results in Singh. et al., 2025. + +Water Loss Fraction, 0.01, -- "Long-term modeling, calibrated to early field data, predicts circulation recapture rates exceeding 99%" ([Geothermal Mythbusting: Water Use and Impacts](https://fervoenergy.com/geothermal-mythbusting-water-use-and-impacts/); Fervo Energy, 2025). Modeling in Singh et al., 2025 predicts fluid loss of 0.36% to 0.49%. +Water Cost Adjustment Factor, 2, -- Local scarcity may increase procurement costs. Development near/on land with active/shut-in oil and gas wells could potentially utilize waste water to recover losses and offset costs. + +Ambient Temperature, 11.17, -- Average annual temperature of Milford, Utah ([NCEI](https://www.ncei.noaa.gov/access/us-climate-normals/#dataset=normals-annualseasonal&timeframe=30&station=USC00425654)). Note that this value affects heat to power conversion efficiency. The effects of hourly and seasonal ambient temperature fluctuations on efficiency and power generation are not modeled in this version of the case study. + +Utilization Factor, .9 +Plant Outlet Pressure, 2000 psi, -- McClure, 2024; Singh et al., 2025. +Circulation Pump Efficiency, 0.80 + +# *** Well Bores Parameters *** + +Number of Production Wells, 56, -- Number of production wells required to produce net generation greater than 500 MW (PPA minimum) and total generation less than 600 MW (Gen 2 ORCs gross capacity). +Number of Injection Wells per Production Well, 0.666, -- Modeled on the reference case 5-well bench pattern (3 producers : 2 injectors) described in Singh et al., 2025. + +Nonvertical Length per Multilateral Section, 5000 feet, -- Target lateral length given in environmental assessment (BLM, 2024). Note that lateral length is assumed to be an upper bound constraining the number of fractures per well for a given cluster spacing. +Number of Multilateral Sections, 0, -- This parameter is set to 0 because, for this case study, the cost of horizontal drilling is included within the 'vertical drilling cost.' This approach allows us to more directly convey the overall well drilling and completion cost. + +Production Flow Rate per Well, 107, -- Cape Station pilot testing reported a sustained flow rate of 95–100 kg/s and maximum flow rate of 107 kg/s (Fervo Energy, 2024). Modeling by Singh et al. suggests initial flow rates of 120–130 kg/sec that gradually decrease over time (Singh et al., 2025). The ATB Advanced Scenario models sustained flow rates of 110 kg/s (NREL, 2024). +Production Well Diameter, 8.535, -- Inner diameter of 9⅝ inch casing size, the next standard casing size up from 7 inches, implied by announcement of “increasing casing diameter” (Fervo Energy, 2025). +Injection Well Diameter, 8.535, -- See Production Well Diameter + +Production Wellhead Pressure, 300 psi, -- Set constant in Singh et al., 2025. Actual production WHP may gradually increase over time if flow rates are kept constant. + +Productivity Index, 1.7458, -- Based on ATB Conservative Scenario (NREL, 2025) derated by 30% per analyses that suggest lower productivity/injectivitity (Xing et al., 2025; Yearsley and Kombrink, 2024). +Injectivity Index, 2.1105, -- See Productivity Index + +Injection Temperature, 53.6, -- Calibrated with GEOPHIRES model-calculated reinjection temperature (Beckers and McCabe, 2019). Close to upper bound of Project Red injection temperatures (75–125℉; 23.89–51.67℃) (Norbeck and Latimer, 2023). +Ramey Production Wellbore Model, True, -- Ramey's model estimates the geofluid temperature drop in production wells +Injection Wellbore Temperature Gain, 3 + + +Maximum Drawdown, 0.023, -- This value represents the drop in production temperature compared to the initial temperature that is allowed before the wellfield is redrilled. It is tuned to keep minimum net electricity generation over the project lifetime ≥500 MWe. + +# *** SIMULATION PARAMETERS *** +# ***************************** +Maximum Temperature, 500 +Time steps per year, 12 diff --git a/tests/examples/Fervo_Project_Cape-6.out b/tests/examples/Fervo_Project_Cape-6.out new file mode 100644 index 000000000..66a868fa2 --- /dev/null +++ b/tests/examples/Fervo_Project_Cape-6.out @@ -0,0 +1,461 @@ + ***************** + ***CASE REPORT*** + ***************** + +Simulation Metadata +---------------------- + GEOPHIRES Version: 3.10.25 + Simulation Date: 2026-01-14 + Simulation Time: 11:31 + Calculation Time: 1.801 sec + + ***SUMMARY OF RESULTS*** + + End-Use Option: Electricity + Average Net Electricity Production: 124.63 MW + Electricity breakeven price: 7.56 cents/kWh + Total CAPEX: 647.28 MUSD + Number of production wells: 13 + Number of injection wells: 9 + Flowrate per production well: 107.0 kg/sec + Well depth: 2.7 kilometer + Segment 1 Geothermal gradient: 74 degC/km + Segment 1 Thickness: 2.5 kilometer + Segment 2 Geothermal gradient: 41 degC/km + Segment 2 Thickness: 0.5 kilometer + Segment 3 Geothermal gradient: 39.1 degC/km + + + ***ECONOMIC PARAMETERS*** + + Economic Model = SAM Single Owner PPA + Real Discount Rate: 12.00 % + Nominal Discount Rate: 15.02 % + WACC: 8.14 % + Project lifetime: 30 yr + Capacity factor: 90.0 % + Project NPV: 102.68 MUSD + After-tax IRR: 35.33 % + Project VIR=PI=PIR: 1.68 + Project MOIC: 5.24 + Project Payback Period: 2.89 yr + Estimated Jobs Created: 295 + + ***ENGINEERING PARAMETERS*** + + Number of Production Wells: 13 + Number of Injection Wells: 9 + Well depth: 2.7 kilometer + Water loss rate: 1.0 % + Pump efficiency: 80.0 % + Injection temperature: 56.6 degC + Production Wellbore heat transmission calculated with Ramey's model + Average production well temperature drop: 0.2 degC + Flowrate per production well: 107.0 kg/sec + Injection well casing ID: 8.535 in + Production well casing ID: 8.535 in + Number of times redrilling: 3 + Power plant type: Supercritical ORC + + + ***RESOURCE CHARACTERISTICS*** + + Maximum reservoir temperature: 500.0 degC + Number of segments: 3 + Segment 1 Geothermal gradient: 74 degC/km + Segment 1 Thickness: 2.5 kilometer + Segment 2 Geothermal gradient: 41 degC/km + Segment 2 Thickness: 0.5 kilometer + Segment 3 Geothermal gradient: 39.1 degC/km + + + ***RESERVOIR PARAMETERS*** + + Reservoir Model = Multiple Parallel Fractures Model (Gringarten) + Bottom-hole temperature: 205.38 degC + Fracture model = Rectangular + Well separation: fracture height: 95.00 meter + Fracture width: 305.00 meter + Fracture area: 28975.00 m**2 + Reservoir volume calculated with fracture separation and number of fractures as input + Number of fractures: 3300 + Fracture separation: 9.83 meter + Reservoir volume: 939205052 m**3 + Reservoir hydrostatic pressure: 25324.54 kPa + Plant outlet pressure: 13789.51 kPa + Production wellhead pressure: 2082.43 kPa + Productivity Index: 1.75 kg/sec/bar + Injectivity Index: 2.11 kg/sec/bar + Reservoir density: 2800.00 kg/m**3 + Reservoir thermal conductivity: 3.05 W/m/K + Reservoir heat capacity: 790.00 J/kg/K + + + ***RESERVOIR SIMULATION RESULTS*** + + Maximum Production Temperature: 203.3 degC + Average Production Temperature: 202.7 degC + Minimum Production Temperature: 197.8 degC + Initial Production Temperature: 201.9 degC + Average Reservoir Heat Extraction: 850.61 MW + Production Wellbore Heat Transmission Model = Ramey Model + Average Production Well Temperature Drop: 0.2 degC + Average Injection Well Pump Pressure Drop: -5401.0 kPa + Average Production Well Pump Pressure Drop: 6917.3 kPa + + + ***CAPITAL COSTS (M$)*** + + Drilling and completion costs: 102.27 MUSD + Drilling and completion costs per well: 4.65 MUSD + Stimulation costs: 106.26 MUSD + Surface power plant costs: 340.68 MUSD + Field gathering system costs: 10.06 MUSD + Total surface equipment costs: 350.74 MUSD + Exploration costs: 30.00 MUSD + Overnight Capital Cost: 589.27 MUSD + Interest during construction: 14.13 MUSD + Inflation costs during construction: 43.87 MUSD + Total CAPEX: 647.28 MUSD + + + ***OPERATING AND MAINTENANCE COSTS (M$/yr)*** + + Wellfield maintenance costs: 1.84 MUSD/yr + Power plant maintenance costs: 7.26 MUSD/yr + Water costs: 0.73 MUSD/yr + Redrilling costs: 20.85 MUSD/yr + Total operating and maintenance costs: 30.68 MUSD/yr + + + ***SURFACE EQUIPMENT SIMULATION RESULTS*** + + Initial geofluid availability: 0.19 MW/(kg/s) + Maximum Total Electricity Generation: 139.21 MW + Average Total Electricity Generation: 138.35 MW + Minimum Total Electricity Generation: 130.21 MW + Initial Total Electricity Generation: 136.94 MW + Maximum Net Electricity Generation: 125.52 MW + Average Net Electricity Generation: 124.63 MW + Minimum Net Electricity Generation: 116.28 MW + Initial Net Electricity Generation: 123.21 MW + Average Annual Total Electricity Generation: 1090.74 GWh + Average Annual Net Electricity Generation: 982.60 GWh + Initial pumping power/net installed power: 11.15 % + Average Pumping Power: 13.72 MW + Heat to Power Conversion Efficiency: 14.65 % + + ************************************************************ + * HEATING, COOLING AND/OR ELECTRICITY PRODUCTION PROFILE * + ************************************************************ + YEAR THERMAL GEOFLUID PUMP NET FIRST LAW + DRAWDOWN TEMPERATURE POWER POWER EFFICIENCY + (degC) (MW) (MW) (%) + 1 1.0000 201.89 13.7344 123.2063 14.5683 + 2 1.0049 202.87 13.7251 124.8568 14.6641 + 3 1.0058 203.06 13.7234 125.1637 14.6818 + 4 1.0063 203.15 13.7225 125.3234 14.6910 + 5 1.0066 203.21 13.7219 125.4287 14.6970 + 6 1.0068 203.25 13.7218 125.4911 14.7006 + 7 1.0062 203.13 13.7275 125.2835 14.6884 + 8 1.0004 201.98 13.7724 123.3192 14.5726 + 9 1.0000 201.89 13.7194 123.2213 14.5701 + 10 1.0049 202.87 13.7167 124.8652 14.6651 + 11 1.0058 203.06 13.7123 125.1748 14.6831 + 12 1.0063 203.15 13.7067 125.3392 14.6928 + 13 1.0066 203.21 13.7013 125.4494 14.6994 + 14 1.0068 203.25 13.6974 125.5156 14.7035 + 15 1.0062 203.13 13.7008 125.3102 14.6915 + 16 1.0004 201.98 13.7448 123.3468 14.5759 + 17 1.0000 201.89 13.6921 123.2485 14.5733 + 18 1.0049 202.87 13.6919 124.8901 14.6680 + 19 1.0058 203.06 13.6918 125.1953 14.6855 + 20 1.0063 203.15 13.6918 125.3541 14.6946 + 21 1.0066 203.21 13.6919 125.4588 14.7006 + 22 1.0068 203.25 13.6923 125.5207 14.7041 + 23 1.0062 203.13 13.6984 125.3126 14.6918 + 24 1.0004 201.98 13.7440 123.3476 14.5760 + 25 1.0000 201.89 13.6920 123.2487 14.5734 + 26 1.0049 202.87 13.6920 124.8899 14.6680 + 27 1.0058 203.06 13.6921 125.1951 14.6855 + 28 1.0063 203.15 13.6921 125.3538 14.6945 + 29 1.0066 203.21 13.6921 125.4585 14.7005 + 30 1.0068 203.25 13.6925 125.5205 14.7040 + + + ******************************************************************* + * ANNUAL HEATING, COOLING AND/OR ELECTRICITY PRODUCTION PROFILE * + ******************************************************************* + YEAR ELECTRICITY HEAT RESERVOIR PERCENTAGE OF + PROVIDED EXTRACTED HEAT CONTENT TOTAL HEAT MINED + (GWh/year) (GWh/year) (10^15 J) (%) + 1 980.1 6698.0 284.98 7.80 + 2 985.7 6717.6 260.80 15.63 + 3 987.5 6723.5 236.59 23.46 + 4 988.5 6727.1 212.38 31.29 + 5 989.2 6729.4 188.15 39.13 + 6 989.0 6728.7 163.93 46.97 + 7 982.2 6705.6 139.79 54.78 + 8 949.0 6591.8 116.06 62.45 + 9 980.2 6698.0 91.94 70.25 + 10 985.8 6717.6 67.76 78.08 + 11 987.6 6723.5 43.55 85.91 + 12 988.6 6727.1 19.34 93.74 + 13 989.3 6729.4 -4.89 101.58 + 14 989.2 6728.7 -29.11 109.42 + 15 982.4 6705.6 -53.25 117.23 + 16 949.2 6591.8 -76.98 124.91 + 17 980.4 6698.0 -101.10 132.71 + 18 986.0 6717.6 -125.28 140.53 + 19 987.7 6723.5 -149.48 148.36 + 20 988.7 6727.1 -173.70 156.20 + 21 989.4 6729.4 -197.93 164.03 + 22 989.2 6728.7 -222.15 171.87 + 23 982.4 6705.6 -246.29 179.68 + 24 949.2 6591.8 -270.02 187.36 + 25 980.4 6698.0 -294.13 195.16 + 26 986.0 6717.6 -318.32 202.98 + 27 987.7 6723.5 -342.52 210.81 + 28 988.7 6727.1 -366.74 218.65 + 29 989.4 6729.4 -390.97 226.49 + 30 989.3 6729.1 -415.19 234.32 + + *************************** + * SAM CASH FLOW PROFILE * + *************************** +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Year -2 Year -1 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 +CONSTRUCTION +Capital expenditure schedule [construction] (%) 2.58 25.60 71.80 +Overnight capital expenditure [construction] ($) -15,221,115 -151,123,932 -422,929,564 +plus: +Inflation cost [construction] ($) -410,970 -8,270,862 -35,190,566 +equals: +Nominal capital expenditure [construction] ($) -15,632,086 -159,394,793 -458,120,130 + +Issuance of equity [construction] ($) 4,689,626 47,818,438 137,436,039 +Issuance of debt [construction] ($) 10,942,460 111,576,355 320,684,091 +Debt balance [construction] ($) 10,942,460 123,667,774 457,336,981 +Debt interest payment [construction] ($) 0 1,148,958 12,985,116 + +Installed cost [construction] ($) -15,632,086 -160,543,752 -471,105,247 +After-tax net cash flow [construction] ($) -4,689,626 -47,818,438 -137,436,039 + +ENERGY +Electricity to grid (kWh) 0.0 980,282,023 985,934,910 987,657,762 988,676,310 989,356,923 989,143,780 982,350,011 949,154,736 980,360,848 986,009,946 987,763,168 988,820,117 989,535,331 989,346,569 982,565,161.0 949,368,348 980,567,413 986,189,659 987,903,017 988,915,689 989,592,029 989,375,367 982,577,186 949,371,815 980,567,212 986,188,072 987,901,013 988,913,616 989,590,004 989,467,326 +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 980,282,023 985,934,910 987,657,762 988,676,310 989,356,923 989,143,780 982,350,011 949,154,736 980,360,848 986,009,946 987,763,168 988,820,117 989,535,331 989,346,569 982,565,161.0 949,368,348 980,567,413 986,189,659 987,903,017 988,915,689 989,592,029 989,375,367 982,577,186 949,371,815 980,567,212 986,188,072 987,901,013 988,913,616 989,590,004 989,467,326 + +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 93,126,792 93,663,816 94,390,452 95,051,340 95,680,708 96,223,907 96,122,949 93,415,809 97,045,920 98,167,150 98,904,726 99,574,186 100,210,243 100,755,055 100,624,498 97,765,952 101,537,756 102,682,067 103,423,567 104,093,265 104,728,524 105,269,539 105,106,282 102,095,445 106,009,121 107,178,920 107,928,186 108,602,493 109,240,841 109,791,295 +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 323,640,542 +Total revenue ($) 0 93,126,792 93,663,816 94,390,452 95,051,340 95,680,708 96,223,907 96,122,949 93,415,809 97,045,920 98,167,150 98,904,726 99,574,186 100,210,243 100,755,055 100,624,498 97,765,952 101,537,756 102,682,067 103,423,567 104,093,265 104,728,524 105,269,539 105,106,282 102,095,445 106,009,121 107,178,920 107,928,186 108,602,493 109,240,841 433,431,836 + +Property tax net assessed value ($) 0 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 647,281,084 + +OPERATING EXPENSES +O&M fixed expense ($) 0 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 30,680,848 +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 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 1,424,018 +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 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 32,104,866 + +EBITDA ($) 0 61,021,926 61,558,950 62,285,586 62,946,474 63,575,842 64,119,041 64,018,082 61,310,943 64,941,054 66,062,284 66,799,860 67,469,320 68,105,377 68,650,188 68,519,632 65,661,086 69,432,889 70,577,201 71,318,701 71,988,399 72,623,658 73,164,673 73,001,415 69,990,579 73,904,255 75,074,054 75,823,319 76,497,627 77,135,974 401,326,970 + +OPERATING ACTIVITIES +EBITDA ($) 0 61,021,926 61,558,950 62,285,586 62,946,474 63,575,842 64,119,041 64,018,082 61,310,943 64,941,054 66,062,284 66,799,860 67,469,320 68,105,377 68,650,188 68,519,632 65,661,086 69,432,889 70,577,201 71,318,701 71,988,399 72,623,658 73,164,673 73,001,415 69,990,579 73,904,255 75,074,054 75,823,319 76,497,627 77,135,974 401,326,970 +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 32,013,589 31,674,680 31,312,048 30,924,031 30,508,853 30,064,613 29,589,276 29,080,665 28,536,452 27,954,143 27,331,073 26,664,388 25,951,036 25,187,748 24,371,031 23,497,143 22,562,083 21,561,569 20,491,018 19,345,530 18,119,857 16,808,387 15,405,114 13,903,612 12,297,005 10,577,935 8,738,531 6,770,368 4,664,434 2,411,084 +Cash flow from operating activities ($) 0 29,008,337 29,884,270 30,973,539 32,022,443 33,066,989 34,054,428 34,428,807 32,230,278 36,404,603 38,108,141 39,468,787 40,804,931 42,154,341 43,462,440 44,148,601 42,163,944 46,870,807 49,015,633 50,827,682 52,642,870 54,503,801 56,356,286 57,596,301 56,086,967 61,607,250 64,496,118 67,084,789 69,727,259 72,471,541 398,915,886 + +INVESTING ACTIVITIES +Total installed cost ($) -647,281,084 +Debt closing costs ($) 0 +Debt up-front fee ($) 0 +minus: +Total IBI income ($) 0 +Total CBI income ($) 0 +equals: +Purchase of property ($) -647,281,084 +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 +Reserve (increase)/decrease receivables ($) 0 0 0 0 0 0 0 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 major equipment 1 ($) 0 0 0 0 0 0 0 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 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 (increase)/decrease 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 +Reserve capital spending major equipment 1 ($) 0 0 0 0 0 0 0 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 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 ($) -647,281,084 0 0 0 0 0 0 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 ($) 189,944,103 +Size of debt ($) 457,336,981 +minus: +Debt principal payment ($) 0 4,841,554 5,180,463 5,543,095 5,931,112 6,346,289 6,790,530 7,265,867 7,774,477 8,318,691 8,900,999 9,524,069 10,190,754 10,904,107 11,667,394 12,484,112 13,358,000 14,293,060 15,293,574 16,364,124 17,509,613 18,735,286 20,046,756 21,450,028 22,951,530 24,558,138 26,277,207 28,116,612 30,084,775 32,190,709 34,444,058 +equals: +Cash flow from financing activities ($) 647,281,084 -4,841,554 -5,180,463 -5,543,095 -5,931,112 -6,346,289 -6,790,530 -7,265,867 -7,774,477 -8,318,691 -8,900,999 -9,524,069 -10,190,754 -10,904,107 -11,667,394 -12,484,112 -13,358,000 -14,293,060 -15,293,574 -16,364,124 -17,509,613 -18,735,286 -20,046,756 -21,450,028 -22,951,530 -24,558,138 -26,277,207 -28,116,612 -30,084,775 -32,190,709 -34,444,058 + +PROJECT RETURNS +Pre-tax Cash Flow: +Cash flow from operating activities ($) 0 29,008,337 29,884,270 30,973,539 32,022,443 33,066,989 34,054,428 34,428,807 32,230,278 36,404,603 38,108,141 39,468,787 40,804,931 42,154,341 43,462,440 44,148,601 42,163,944 46,870,807 49,015,633 50,827,682 52,642,870 54,503,801 56,356,286 57,596,301 56,086,967 61,607,250 64,496,118 67,084,789 69,727,259 72,471,541 398,915,886 +Cash flow from investing activities ($) -647,281,084 0 0 0 0 0 0 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 ($) 647,281,084 -4,841,554 -5,180,463 -5,543,095 -5,931,112 -6,346,289 -6,790,530 -7,265,867 -7,774,477 -8,318,691 -8,900,999 -9,524,069 -10,190,754 -10,904,107 -11,667,394 -12,484,112 -13,358,000 -14,293,060 -15,293,574 -16,364,124 -17,509,613 -18,735,286 -20,046,756 -21,450,028 -22,951,530 -24,558,138 -26,277,207 -28,116,612 -30,084,775 -32,190,709 -34,444,058 +Total pre-tax cash flow ($) 0 24,166,784 24,703,808 25,430,444 26,091,332 26,720,699 27,263,898 27,162,940 24,455,800 28,085,912 29,207,142 29,944,717 30,614,177 31,250,234 31,795,046 31,664,490 28,805,944 32,577,747 33,722,059 34,463,558 35,133,257 35,768,516 36,309,530 36,146,273 33,135,436 37,049,113 38,218,911 38,968,177 39,642,485 40,280,832 364,471,828 + +Pre-tax Returns: +Issuance of equity ($) 189,944,103 +Total pre-tax cash flow ($) 0 24,166,784 24,703,808 25,430,444 26,091,332 26,720,699 27,263,898 27,162,940 24,455,800 28,085,912 29,207,142 29,944,717 30,614,177 31,250,234 31,795,046 31,664,490 28,805,944 32,577,747 33,722,059 34,463,558 35,133,257 35,768,516 36,309,530 36,146,273 33,135,436 37,049,113 38,218,911 38,968,177 39,642,485 40,280,832 364,471,828 +Total pre-tax returns ($) -189,944,103 24,166,784 24,703,808 25,430,444 26,091,332 26,720,699 27,263,898 27,162,940 24,455,800 28,085,912 29,207,142 29,944,717 30,614,177 31,250,234 31,795,046 31,664,490 28,805,944 32,577,747 33,722,059 34,463,558 35,133,257 35,768,516 36,309,530 36,146,273 33,135,436 37,049,113 38,218,911 38,968,177 39,642,485 40,280,832 364,471,828 + +After-tax Returns: +Total pre-tax returns ($) -189,944,103 24,166,784 24,703,808 25,430,444 26,091,332 26,720,699 27,263,898 27,162,940 24,455,800 28,085,912 29,207,142 29,944,717 30,614,177 31,250,234 31,795,046 31,664,490 28,805,944 32,577,747 33,722,059 34,463,558 35,133,257 35,768,516 36,309,530 36,146,273 33,135,436 37,049,113 38,218,911 38,968,177 39,642,485 40,280,832 364,471,828 +Federal ITC total income ($) 0 194,184,325 0 0 0 0 0 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 -3,057,511 -476,022 -694,360 -904,608 -1,113,982 -1,311,909 -1,386,951 -946,267 -1,782,990 -2,124,455 -2,397,190 -2,665,014 -2,935,496 -3,197,698 -3,335,235 -2,937,421 -3,880,888 -4,310,808 -4,674,024 -5,037,869 -8,167,949 -11,296,336 -11,544,891 -11,242,352 -12,348,865 -12,927,924 -13,446,810 -13,976,480 -14,526,558 -79,960,695 +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 -694,039 -108,055 -157,616 -205,341 -252,868 -297,797 -314,831 -214,798 -404,730 -482,241 -544,150 -604,945 -666,343 -725,861 -757,082 -666,780 -880,942 -978,531 -1,060,980 -1,143,571 -1,854,083 -2,564,211 -2,620,632 -2,551,957 -2,803,130 -2,934,573 -3,052,358 -3,172,590 -3,297,455 -18,150,673 +Total after-tax returns ($) -189,944,103 214,599,559 24,119,732 24,578,467 24,981,383 25,353,850 25,654,193 25,461,158 23,294,735 25,898,192 26,600,446 27,003,377 27,344,219 27,648,396 27,871,487 27,572,172 25,201,743 27,815,917 28,432,720 28,728,555 28,951,817 25,746,484 22,448,984 21,980,751 19,341,127 21,897,117 22,356,413 22,469,009 22,493,414 22,456,819 266,360,460 + +After-tax net cash flow ($) -4,689,626 -47,818,438 -137,436,039 214,599,559 24,119,732 24,578,467 24,981,383 25,353,850 25,654,193 25,461,158 23,294,735 25,898,192 26,600,446 27,003,377 27,344,219 27,648,396 27,871,487 27,572,172 25,201,743 27,815,917 28,432,720 28,728,555 28,951,817 25,746,484 22,448,984 21,980,751 19,341,127 21,897,117 22,356,413 22,469,009 22,493,414 22,456,819 266,360,460 +After-tax cumulative IRR (%) NaN NaN NaN 9.74 17.50 23.11 26.93 29.51 31.27 32.45 33.20 33.79 34.21 34.52 34.74 34.90 35.02 35.10 35.16 35.21 35.24 35.27 35.29 35.30 35.31 35.31 35.31 35.32 35.32 35.32 35.32 35.32 35.33 +After-tax cumulative NPV ($) -4,689,626 -46,262,200 -150,140,229 -9,125,841 4,653,188 16,860,287 27,646,914 37,164,453 45,536,866 52,760,936 58,507,035 64,060,913 69,020,292 73,397,206 77,250,455 80,637,671 83,606,223 86,159,319 88,188,114 90,134,874 91,864,886 93,384,579 94,716,044 95,745,442 96,525,763 97,190,012 97,698,150 98,198,298 98,642,238 99,030,137 99,367,736 99,660,762 102,682,375 + +AFTER-TAX LCOE AND PPA PRICE +Annual costs ($) -189,944,103 121,472,766 -69,544,085 -69,811,985 -70,069,958 -70,326,858 -70,569,714 -70,661,791 -70,121,074 -71,147,728 -71,566,705 -71,901,349 -72,229,967 -72,561,847 -72,883,568 -73,052,326 -72,564,209 -73,721,838 -74,249,348 -74,695,012 -75,141,448 -78,982,041 -82,820,555 -83,125,531 -82,754,318 -84,112,004 -84,822,506 -85,459,177 -86,109,079 -86,784,022 156,569,166 +PPA revenue ($) 0 93,126,792 93,663,816 94,390,452 95,051,340 95,680,708 96,223,907 96,122,949 93,415,809 97,045,920 98,167,150 98,904,726 99,574,186 100,210,243 100,755,055 100,624,498 97,765,952 101,537,756 102,682,067 103,423,567 104,093,265 104,728,524 105,269,539 105,106,282 102,095,445 106,009,121 107,178,920 107,928,186 108,602,493 109,240,841 109,791,295 +Electricity to grid (kWh) 0.0 980,282,023 985,934,910 987,657,762 988,676,310 989,356,923 989,143,780 982,350,011 949,154,736 980,360,848 986,009,946 987,763,168 988,820,117 989,535,331 989,346,569 982,565,161.0 949,368,348 980,567,413 986,189,659 987,903,017 988,915,689 989,592,029 989,375,367 982,577,186 949,371,815 980,567,212 986,188,072 987,901,013 988,913,616 989,590,004 989,467,326 + +Present value of annual costs ($) 487,420,489 +Present value of annual energy nominal (kWh) 6,446,402,973 +LCOE Levelized cost of energy nominal (cents/kWh) 7.56 + +Present value of PPA revenue ($) 631,973,851 +Present value of annual energy nominal (kWh) 6,446,402,973 +LPPA Levelized PPA price nominal (cents/kWh) 9.80 + +PROJECT STATE INCOME TAXES +EBITDA ($) 0 61,021,926 61,558,950 62,285,586 62,946,474 63,575,842 64,119,041 64,018,082 61,310,943 64,941,054 66,062,284 66,799,860 67,469,320 68,105,377 68,650,188 68,519,632 65,661,086 69,432,889 70,577,201 71,318,701 71,988,399 72,623,658 73,164,673 73,001,415 69,990,579 73,904,255 75,074,054 75,823,319 76,497,627 77,135,974 401,326,970 +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 32,013,589 31,674,680 31,312,048 30,924,031 30,508,853 30,064,613 29,589,276 29,080,665 28,536,452 27,954,143 27,331,073 26,664,388 25,951,036 25,187,748 24,371,031 23,497,143 22,562,083 21,561,569 20,491,018 19,345,530 18,119,857 16,808,387 15,405,114 13,903,612 12,297,005 10,577,935 8,738,531 6,770,368 4,664,434 2,411,084 +Total state tax depreciation ($) 0 13,754,723 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 13,754,723 0 0 0 0 0 0 0 0 0 +equals: +State taxable income ($) 0 15,253,614 2,374,824 3,464,093 4,512,997 5,557,543 6,544,982 6,919,361 4,720,832 8,895,157 10,598,695 11,959,341 13,295,485 14,644,895 15,952,994 16,639,155 14,654,497 19,361,361 21,506,186 23,318,236 25,133,423 40,749,078 56,356,286 57,596,301 56,086,967 61,607,250 64,496,118 67,084,789 69,727,259 72,471,541 398,915,886 + +State income tax rate (frac) 0.0 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 +State tax benefit (liability) ($) 0 -694,039 -108,055 -157,616 -205,341 -252,868 -297,797 -314,831 -214,798 -404,730 -482,241 -544,150 -604,945 -666,343 -725,861 -757,082 -666,780 -880,942 -978,531 -1,060,980 -1,143,571 -1,854,083 -2,564,211 -2,620,632 -2,551,957 -2,803,130 -2,934,573 -3,052,358 -3,172,590 -3,297,455 -18,150,673 + +PROJECT FEDERAL INCOME TAXES +EBITDA ($) 0 61,021,926 61,558,950 62,285,586 62,946,474 63,575,842 64,119,041 64,018,082 61,310,943 64,941,054 66,062,284 66,799,860 67,469,320 68,105,377 68,650,188 68,519,632 65,661,086 69,432,889 70,577,201 71,318,701 71,988,399 72,623,658 73,164,673 73,001,415 69,990,579 73,904,255 75,074,054 75,823,319 76,497,627 77,135,974 401,326,970 +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 -694,039 -108,055 -157,616 -205,341 -252,868 -297,797 -314,831 -214,798 -404,730 -482,241 -544,150 -604,945 -666,343 -725,861 -757,082 -666,780 -880,942 -978,531 -1,060,980 -1,143,571 -1,854,083 -2,564,211 -2,620,632 -2,551,957 -2,803,130 -2,934,573 -3,052,358 -3,172,590 -3,297,455 -18,150,673 +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 32,013,589 31,674,680 31,312,048 30,924,031 30,508,853 30,064,613 29,589,276 29,080,665 28,536,452 27,954,143 27,331,073 26,664,388 25,951,036 25,187,748 24,371,031 23,497,143 22,562,083 21,561,569 20,491,018 19,345,530 18,119,857 16,808,387 15,405,114 13,903,612 12,297,005 10,577,935 8,738,531 6,770,368 4,664,434 2,411,084 +Total federal tax depreciation ($) 0 13,754,723 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 27,509,446 13,754,723 0 0 0 0 0 0 0 0 0 +equals: +Federal taxable income ($) 0 14,559,575 2,266,770 3,306,476 4,307,656 5,304,675 6,247,185 6,604,530 4,506,034 8,490,427 10,116,454 11,415,191 12,690,541 13,978,552 15,227,133 15,882,074 13,987,718 18,480,419 20,527,655 22,257,256 23,989,853 38,894,995 53,792,075 54,975,670 53,535,010 58,804,120 61,561,545 64,032,431 66,554,669 69,174,086 380,765,213 + +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 -3,057,511 -476,022 -694,360 -904,608 -1,113,982 -1,311,909 -1,386,951 -946,267 -1,782,990 -2,124,455 -2,397,190 -2,665,014 -2,935,496 -3,197,698 -3,335,235 -2,937,421 -3,880,888 -4,310,808 -4,674,024 -5,037,869 -8,167,949 -11,296,336 -11,544,891 -11,242,352 -12,348,865 -12,927,924 -13,446,810 -13,976,480 -14,526,558 -79,960,695 + +CASH INCENTIVES +Federal IBI income ($) 0 +State IBI income ($) 0 +Utility IBI income ($) 0 +Other IBI income ($) 0 +Total IBI income ($) 0 + +Federal CBI income ($) 0 +State CBI income ($) 0 +Utility CBI income ($) 0 +Other CBI income ($) 0 +Total CBI income ($) 0 + +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 +Total 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 + +TAX CREDITS +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 +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 194,184,325 0 0 0 0 0 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 194,184,325 0 0 0 0 0 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 ($) 457,336,981 452,495,427 447,314,965 441,771,870 435,840,758 429,494,469 422,703,939 415,438,072 407,663,595 399,344,904 390,443,905 380,919,835 370,729,081 359,824,975 348,157,580 335,673,468 322,315,469 308,022,409 292,728,835 276,364,711 258,855,099 240,119,813 220,073,057 198,623,029 175,671,498 151,113,361 124,836,154 96,719,542 66,634,767 34,444,058 0 +Debt interest payment ($) 0 32,013,589 31,674,680 31,312,048 30,924,031 30,508,853 30,064,613 29,589,276 29,080,665 28,536,452 27,954,143 27,331,073 26,664,388 25,951,036 25,187,748 24,371,031 23,497,143 22,562,083 21,561,569 20,491,018 19,345,530 18,119,857 16,808,387 15,405,114 13,903,612 12,297,005 10,577,935 8,738,531 6,770,368 4,664,434 2,411,084 +Debt principal payment ($) 0 4,841,554 5,180,463 5,543,095 5,931,112 6,346,289 6,790,530 7,265,867 7,774,477 8,318,691 8,900,999 9,524,069 10,190,754 10,904,107 11,667,394 12,484,112 13,358,000 14,293,060 15,293,574 16,364,124 17,509,613 18,735,286 20,046,756 21,450,028 22,951,530 24,558,138 26,277,207 28,116,612 30,084,775 32,190,709 34,444,058 +Debt total payment ($) 0 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 + +DSCR (DEBT FRACTION) +EBITDA ($) 0 61,021,926 61,558,950 62,285,586 62,946,474 63,575,842 64,119,041 64,018,082 61,310,943 64,941,054 66,062,284 66,799,860 67,469,320 68,105,377 68,650,188 68,519,632 65,661,086 69,432,889 70,577,201 71,318,701 71,988,399 72,623,658 73,164,673 73,001,415 69,990,579 73,904,255 75,074,054 75,823,319 76,497,627 77,135,974 401,326,970 +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 61,021,926 61,558,950 62,285,586 62,946,474 63,575,842 64,119,041 64,018,082 61,310,943 64,941,054 66,062,284 66,799,860 67,469,320 68,105,377 68,650,188 68,519,632 65,661,086 69,432,889 70,577,201 71,318,701 71,988,399 72,623,658 73,164,673 73,001,415 69,990,579 73,904,255 75,074,054 75,823,319 76,497,627 77,135,974 401,326,970 +Debt total payment ($) 0 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 36,855,142 +DSCR (pre-tax) 0.0 1.66 1.67 1.69 1.71 1.73 1.74 1.74 1.66 1.76 1.79 1.81 1.83 1.85 1.86 1.86 1.78 1.88 1.91 1.94 1.95 1.97 1.99 1.98 1.90 2.01 2.04 2.06 2.08 2.09 10.89 + +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 +Reserves working capital disbursement ($) 0 0 0 0 0 0 0 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 working capital balance ($) 0 0 0 0 0 0 0 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 debt service 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 debt service disbursement ($) 0 0 0 0 0 0 0 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 debt service balance ($) 0 0 0 0 0 0 0 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 +Reserves receivables disbursement ($) 0 0 0 0 0 0 0 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 balance ($) 0 0 0 0 0 0 0 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 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 1 disbursement ($) 0 0 0 0 0 0 0 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 1 balance ($) 0 0 0 0 0 0 0 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 2 disbursement ($) 0 0 0 0 0 0 0 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 balance ($) 0 0 0 0 0 0 0 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 major equipment 3 disbursement ($) 0 0 0 0 0 0 0 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 balance ($) 0 0 0 0 0 0 0 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 total reserves balance ($) 0 0 0 0 0 0 0 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 on reserves (%/year) 1.75 +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 +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/tests/examples/Fervo_Project_Cape-6.txt b/tests/examples/Fervo_Project_Cape-6.txt new file mode 100644 index 000000000..604aa463b --- /dev/null +++ b/tests/examples/Fervo_Project_Cape-6.txt @@ -0,0 +1,116 @@ +# Case Study: 100 MWe EGS Project Modeled on Fervo Cape Station Phase I +# See documentation: https://softwareengineerprogrammer.github.io/GEOPHIRES/Fervo_Project_Cape-5.html + +# *** ECONOMIC/FINANCIAL PARAMETERS *** +# ************************************* +Economic Model, 5, -- 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, .027, -- US inflation as of December 2025 + +Starting Electricity Sale Price, 0.095, -- Aligns with Geysers - Sacramento pricing in [2024b ATB](https://atb.nrel.gov/electricity/2024/geothermal) (NREL, 2025). See Sensitivity Analysis for effect of different prices on results. +Electricity Escalation Rate Per Year, 0.00057, -- Calibrated to reach 10¢/kWh at project year 11 +Ending Electricity Sale Price, 1, -- Note that this value does not directly determine price at the end of the project life, but rather as a cap as the maximum price to which the starting price can escalate. +Electricity Escalation Start Year, 1 + +Fraction of Investment in Bonds, .7, -- Approximate debt required to cover CAPEX after $1 billion sponsor equity per [Matson, 2024](https://www.linkedin.com/pulse/fervo-energy-technology-day-2024-entering-geothermal-decade-matson-n4stc/). 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 precisely. +Discount Rate, 0.12, -- Typical discount rates for higher-risk projects may be 12–15%. +Inflated Bond Interest Rate, .07, -- 2024b ATB (NREL, 2025) + +Inflated Bond Interest Rate During Construction, 0.105, -- Higher than interest rate during normal operation to account for increased risk of default prior to COD. Value aligns with ATB discount rate (NREL, 2025). +Bond Financing Start Year, -2, -- Equity-only for first 2 construction years (ATB) + +Construction Years, 3 + +# ATB advanced scenario +# Construction CAPEX Schedule, 0.09,0.28,0.1,0.34,0.28 + +# DOE scenario (alternative) +# Construction CAPEX Schedule, 0.014,0.027,0.137,0.274,0.548 + +# DOE-ATB hybrid scenario +Construction CAPEX Schedule, 0.014,0.027,0.139,0.431,0.389 + +Investment Tax Credit Rate, 0.3, -- Geothermal Drilling and Completions Apprenticeship Program ensures compliance with ITC labor requirements (Southern Utah University, 2024). +Combined Income Tax Rate, .2555, -- Federal Corporate Income Tax Rate of 21% plus Utah Corporate Franchise and Income Tax Rate of 4.55%. (Note: This input uses a simple summation of statutory rates; the effective combined rate calculated in the model may differ due to standard federal-state tax interactions.) +Property Tax Rate, 0.0022, -- Utah Inland Port Authority (UIPA) tax differential incentive + +Capital Cost for Power Plant for Electricity Generation, 1900, -- [US DOE, 2021](https://betterbuildingssolutioncenter.energy.gov/sites/default/files/attachments/Waste_Heat_to_Power_Fact_Sheet.pdf). Pricing information not publicly available for Turboden or Baker Hughes Gen 2 ORC units (Turboden, 2025; Jacobs, 2025). +Exploration Capital Cost, 30, -- Equivalent to 2024b ATB NF-EGS conservative scenario exploration assumption of 5 full-size wells (NREL, 2025), plus $1M for geophysical and field work, plus 15% contingency, plus 12% indirect costs. + +Well Drilling Cost Correlation, 3, -- 2025 NREL Geothermal Drilling Cost Curve Update (Akindipe and Witter, 2025). +Well Drilling and Completion Capital Cost Adjustment Factor, 0.9, -- 2024b Geothermal ATB ([NREL, 2025](https://atb.nrel.gov/electricity/2024b/geothermal)). Note: Fervo has claimed lower drilling costs equivalent to an adjustment factor of 0.8 (Latimer, 2025); the case study conservatively uses the higher ATB-aligned value. + +Reservoir Stimulation Capital Cost per Injection Well, 4, -- The baseline stimulation cost is calibrated from costs of high-intensity U.S. shale wells (Baytex Energy, 2024; Quantum Proppant Technologies, 2020), which are the closest technological analogue for multi-stage EGS (Gradl, 2018). Costs are also driven by the requirement for high-strength ceramic proppant rather than standard sand, which would crush or chemically degrade (diagenesis) over a 30-year lifecycle at 200℃ (Ko et al., 2023; Shiozawa & McClure, 2014) and the premium for ultra-high-temperature (HT) downhole tools. Note that all-in costs per well are higher than the baseline cost because they include additional indirect costs and contingency. +Reservoir Stimulation Capital Cost per Production Well, 4, -- See Reservoir Stimulation Capital Cost per Injection Well + +Field Gathering System Capital Cost Adjustment Factor, 0.54, -- Gathering costs represent 2% of facilities CAPEX per [Matson, 2024](https://www.linkedin.com/pulse/fervo-energy-technology-day-2024-entering-geothermal-decade-matson-n4stc/). + +# *** SURFACE & SUBSURFACE TECHNICAL PARAMETERS *** +# ************************************************* +End-Use Option, 1, -- Electricity +Power Plant Type, 2, -- Supercritical ORC +Plant Lifetime, 30, -- 30-year well life per [Geothermal Mythbusting: Water Use and Impacts](https://fervoenergy.com/geothermal-mythbusting-water-use-and-impacts/) (Fervo Energy, 2025). + +Reservoir Model, 1 + +Surface Temperature, 13, -- Surface temperature near Milford, UT (38.4987670, -112.9163432) ([Project InnerSpace, 2025](https://geomap.projectinnerspace.org/test/)). + +Number of Segments, 3 +Gradient 1, 74, -- Sedimentary overburden. 200℃ at 8500 ft depth (Fercho et al. 2024); 228.89℃ at 9824 ft (Norbeck et al. 2024). +Thickness 1, 2.5 +Gradient 2, 41, -- Crystalline reservoir +Thickness 2, 0.5 +Gradient 3, 39.1, -- Sugarloaf appraisal + +Reservoir Depth, 2.68, -- Extrapolated from surface temperature, gradient, and average production temperature of shallower and deeper producers in Singh et al., 2025. + +Reservoir Density, 2800, -- phyllite + quartzite + diorite + granodiorite ([Norbeck et al., 2023](https://doi.org/10.31223/X52X0B)) +Reservoir Heat Capacity, 790 +Reservoir Thermal Conductivity, 3.05 +Reservoir Porosity, 0.0118 + +Reservoir Volume Option, 1, -- FRAC_NUM_SEP: Reservoir volume calculated with fracture separation and number of fractures as input + +Number of Fractures per Stimulated Well, 150, -- The model assumes an Extreme Limited Entry stimulation design (Fervo Energy, 2023) utilizing 12 stages with 15 clusters per stage (derived from Singh et al., 2025) and 81–85% stimulation success rate per 2024b ATB Moderate Scenario (NREL, 2025). +Fracture Separation, 9.8255, -- Based on 30 foot cluster spacing (Singh et al., 2025) marginally uprated to align with long-term thermal decline behavior trend towards wider fracture spacing (Fercho et al., 2025). + +Fracture Shape, 4, -- Bench design and fracture geometry Singh et al., 2025 are given in rectangular dimensions. +Fracture Width, 305, -- Matches intra-bench well spacing of 500 ft (corresponding to fracture length of 1000 ft) (Singh. et al., 2025) +Fracture Height, 95, -- Actual fracture geometry is irregular and heterogeneous; this height complies with the minimum height required by the implemented bench design (200 ft; 60.96 meters) and yields an effective fracture surface area consistent with simulation results in Singh. et al., 2025. + +Water Loss Fraction, 0.01, -- "Long-term modeling, calibrated to early field data, predicts circulation recapture rates exceeding 99%" ([Geothermal Mythbusting: Water Use and Impacts](https://fervoenergy.com/geothermal-mythbusting-water-use-and-impacts/); Fervo Energy, 2025). Modeling in Singh et al., 2025 predicts fluid loss of 0.36% to 0.49%. +Water Cost Adjustment Factor, 2, -- Local scarcity may increase procurement costs. Development near/on land with active/shut-in oil and gas wells could potentially utilize waste water to recover losses and offset costs. + +Ambient Temperature, 11.17, -- Average annual temperature of Milford, Utah ([NCEI](https://www.ncei.noaa.gov/access/us-climate-normals/#dataset=normals-annualseasonal&timeframe=30&station=USC00425654)). Note that this value affects heat to power conversion efficiency. The effects of hourly and seasonal ambient temperature fluctuations on efficiency and power generation are not modeled in this version of the case study. + +Utilization Factor, .9 +Plant Outlet Pressure, 2000 psi, -- McClure, 2024; Singh et al., 2025. +Circulation Pump Efficiency, 0.80 + +# *** Well Bores Parameters *** + +Number of Production Wells, 13 +Number of Injection Wells per Production Well, 0.666, -- Modeled on the reference case 5-well bench pattern (3 producers : 2 injectors) described in Singh et al., 2025. + +Nonvertical Length per Multilateral Section, 5000 feet, -- Target lateral length given in environmental assessment (BLM, 2024). Note that lateral length is assumed to be an upper bound constraining the number of fractures per well for a given cluster spacing. +Number of Multilateral Sections, 0, -- This parameter is set to 0 because, for this case study, the cost of horizontal drilling is included within the 'vertical drilling cost.' This approach allows us to more directly convey the overall well drilling and completion cost. + +Production Flow Rate per Well, 107, -- Cape Station pilot testing reported a sustained flow rate of 95–100 kg/s and maximum flow rate of 107 kg/s (Fervo Energy, 2024). Modeling by Singh et al. suggests initial flow rates of 120–130 kg/sec that gradually decrease over time (Singh et al., 2025). The ATB Advanced Scenario models sustained flow rates of 110 kg/s (NREL, 2024). +Production Well Diameter, 8.535, -- Inner diameter of 9⅝ inch casing size, the next standard casing size up from 7 inches, implied by announcement of “increasing casing diameter” (Fervo Energy, 2025). +Injection Well Diameter, 8.535, -- See Production Well Diameter + +Production Wellhead Pressure, 300 psi, -- Set constant in Singh et al., 2025. Actual production WHP may gradually increase over time if flow rates are kept constant. + +Productivity Index, 1.7458, -- Based on ATB Conservative Scenario (NREL, 2025) derated by 30% per analyses that suggest lower productivity/injectivitity (Xing et al., 2025; Yearsley and Kombrink, 2024). +Injectivity Index, 2.1105, -- See Productivity Index + +Injection Temperature, 53.6, -- Calibrated with GEOPHIRES model-calculated reinjection temperature (Beckers and McCabe, 2019). Close to upper bound of Project Red injection temperatures (75–125℉; 23.89–51.67℃) (Norbeck and Latimer, 2023). +Ramey Production Wellbore Model, True, -- Ramey's model estimates the geofluid temperature drop in production wells +Injection Wellbore Temperature Gain, 3 + + +Maximum Drawdown, 0.023, -- This value represents the drop in production temperature compared to the initial temperature that is allowed before the wellfield is redrilled. It is tuned to keep minimum net electricity generation over the project lifetime ≥100 MWe. + +# *** SIMULATION PARAMETERS *** +# ***************************** +Maximum Temperature, 500 +Time steps per year, 12 diff --git a/tests/examples/example_SAM-single-owner-PPA-5.out b/tests/examples/example_SAM-single-owner-PPA-5.out index 7d3ac7f2c..ac43d7691 100644 --- a/tests/examples/example_SAM-single-owner-PPA-5.out +++ b/tests/examples/example_SAM-single-owner-PPA-5.out @@ -4,10 +4,10 @@ Simulation Metadata ---------------------- - GEOPHIRES Version: 3.10.22 - Simulation Date: 2025-12-15 - Simulation Time: 09:15 - Calculation Time: 1.769 sec + GEOPHIRES Version: 3.10.24 + Simulation Date: 2025-12-24 + Simulation Time: 15:35 + Calculation Time: 1.840 sec ***SUMMARY OF RESULTS*** @@ -69,9 +69,9 @@ Simulation Metadata Well separation: fracture height: 500.00 meter Fracture area: 250000.00 m**2 Reservoir volume calculated with fracture separation and number of fractures as input - Number of fractures: 1663 + Number of fractures: 1650 Fracture separation: 26.00 meter - Reservoir volume: 10803000000 m**3 + Reservoir volume: 10718500000 m**3 Reservoir hydrostatic pressure: 24578.69 kPa Plant outlet pressure: 6894.76 kPa Production wellhead pressure: 2240.80 kPa @@ -179,36 +179,36 @@ Simulation Metadata YEAR ELECTRICITY HEAT RESERVOIR PERCENTAGE OF PROVIDED EXTRACTED HEAT CONTENT TOTAL HEAT MINED (GWh/year) (GWh/year) (10^15 J) (%) - 1 859.5 5629.4 3310.87 0.61 - 2 865.7 5651.2 3290.53 1.22 - 3 867.6 5657.8 3270.16 1.83 - 4 868.8 5661.7 3249.78 2.44 - 5 869.6 5664.5 3229.38 3.05 - 6 870.2 5666.6 3208.98 3.67 - 7 870.7 5668.3 3188.58 4.28 - 8 871.1 5669.6 3168.17 4.89 - 9 871.4 5670.8 3147.75 5.51 - 10 871.7 5671.9 3127.33 6.12 - 11 872.0 5672.8 3106.91 6.73 - 12 872.2 5673.6 3086.49 7.34 - 13 872.4 5674.4 3066.06 7.96 - 14 872.6 5675.0 3045.63 8.57 - 15 872.8 5675.6 3025.20 9.18 - 16 872.9 5676.2 3004.76 9.80 - 17 873.1 5676.7 2984.33 10.41 - 18 873.2 5677.2 2963.89 11.02 - 19 873.4 5677.7 2943.45 11.64 - 20 873.5 5678.1 2923.01 12.25 - 21 873.6 5678.5 2902.56 12.87 - 22 873.7 5678.9 2882.12 13.48 - 23 873.8 5679.3 2861.67 14.09 - 24 873.9 5679.6 2841.23 14.71 - 25 874.0 5679.9 2820.78 15.32 - 26 874.1 5680.2 2800.33 15.93 - 27 874.2 5680.5 2779.88 16.55 - 28 874.3 5680.8 2759.43 17.16 - 29 874.4 5681.1 2738.98 17.78 - 30 874.4 5681.4 2718.53 18.39 + 1 859.5 5629.4 3284.81 0.61 + 2 865.7 5651.2 3264.47 1.23 + 3 867.6 5657.8 3244.10 1.84 + 4 868.8 5661.7 3223.72 2.46 + 5 869.6 5664.5 3203.33 3.08 + 6 870.2 5666.6 3182.93 3.70 + 7 870.7 5668.3 3162.52 4.31 + 8 871.1 5669.6 3142.11 4.93 + 9 871.4 5670.8 3121.70 5.55 + 10 871.7 5671.9 3101.28 6.17 + 11 872.0 5672.8 3080.86 6.78 + 12 872.2 5673.6 3060.43 7.40 + 13 872.4 5674.4 3040.00 8.02 + 14 872.6 5675.0 3019.57 8.64 + 15 872.8 5675.6 2999.14 9.26 + 16 872.9 5676.2 2978.71 9.87 + 17 873.1 5676.7 2958.27 10.49 + 18 873.2 5677.2 2937.83 11.11 + 19 873.4 5677.7 2917.39 11.73 + 20 873.5 5678.1 2896.95 12.35 + 21 873.6 5678.5 2876.51 12.97 + 22 873.7 5678.9 2856.06 13.59 + 23 873.8 5679.3 2835.62 14.20 + 24 873.9 5679.6 2815.17 14.82 + 25 874.0 5679.9 2794.72 15.44 + 26 874.1 5680.2 2774.28 16.06 + 27 874.2 5680.5 2753.83 16.68 + 28 874.3 5680.8 2733.37 17.30 + 29 874.4 5681.1 2712.92 17.92 + 30 874.4 5681.4 2692.47 18.54 *************************** * SAM CASH FLOW PROFILE * @@ -232,9 +232,9 @@ Installed cost [construction] ($) -6,132,082 -12,546,240 -44,921 After-tax net cash flow [construction] ($) -6,132,082 -12,546,240 -44,921,812 -22,977,507 -47,011,979 -48,093,255 -98,398,799 ENERGY -Electricity to grid (kWh) 0.0 859,490,068 865,758,172 867,671,460 868,803,529 869,596,370 870,200,786 870,686,054 871,089,570 871,433,728 871,732,959 871,997,081 872,233,061 872,446,015 872,639,805 872,817,414 872,981,192 873,133,019 873,274,427 873,406,675 873,530,811 873,647,716 873,758,141 873,862,724 873,962,017 874,056,500 874,146,590 874,232,654 874,315,016 874,393,962 874,466,670 +Electricity to grid (kWh) 0.0 859,490,068 865,758,172 867,671,460 868,803,529 869,596,370 870,200,786 870,686,054 871,089,570 871,433,728 871,732,959 871,997,081 872,233,061 872,446,015 872,639,805 872,817,414 872,981,191 873,133,019 873,274,427 873,406,675 873,530,811 873,647,716 873,758,141 873,862,724 873,962,017 874,056,500 874,146,590 874,232,654 874,315,016 874,393,962 874,466,670 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 859,490,068 865,758,172 867,671,460 868,803,529 869,596,370 870,200,786 870,686,054 871,089,570 871,433,728 871,732,959 871,997,081 872,233,061 872,446,015 872,639,805 872,817,414 872,981,192 873,133,019 873,274,427 873,406,675 873,530,811 873,647,716 873,758,141 873,862,724 873,962,017 874,056,500 874,146,590 874,232,654 874,315,016 874,393,962 874,466,670 +Electricity to grid net (kWh) 0.0 859,490,068 865,758,172 867,671,460 868,803,529 869,596,370 870,200,786 870,686,054 871,089,570 871,433,728 871,732,959 871,997,081 872,233,061 872,446,015 872,639,805 872,817,414 872,981,191 873,133,019 873,274,427 873,406,675 873,530,811 873,647,716 873,758,141 873,862,724 873,962,017 874,056,500 874,146,590 874,232,654 874,315,016 874,393,962 874,466,670 REVENUE PPA price (cents/kWh) 0.0 8.0 8.0 8.32 8.64 8.97 9.29 9.61 9.93 10.25 10.58 10.90 11.22 11.54 11.86 12.19 12.51 12.83 13.15 13.47 13.80 14.12 14.44 14.76 15.08 15.41 15.73 16.05 16.37 16.69 17.02 @@ -328,14 +328,14 @@ After-tax cumulative NPV ($) -6,132,082 -17,487,790 -54,288 AFTER-TAX LCOE AND PPA PRICE Annual costs ($) -280,081,674 163,885,873 -45,513,073 -46,385,473 -47,249,566 -48,114,308 -48,983,020 -49,857,494 -50,738,953 -51,628,379 -52,526,647 -53,434,591 -54,353,042 -55,282,847 -56,224,884 -57,180,074 -58,149,386 -59,133,850 -60,134,558 -61,152,671 -62,189,430 -67,252,394 -72,336,738 -73,437,733 -74,563,229 -75,714,948 -76,894,734 -78,104,556 -79,346,524 -80,622,890 179,112,172 PPA revenue ($) 0 68,759,205 69,260,654 72,207,619 75,099,377 77,968,011 80,824,249 83,672,930 86,516,616 89,356,814 92,194,478 95,030,242 97,864,549 100,697,719 103,529,986 106,361,530 109,192,487 112,022,966 114,853,053 117,682,815 120,512,311 123,341,585 126,170,676 128,999,615 131,828,431 134,657,144 137,485,776 140,314,341 143,142,854 145,971,328 148,799,249 -Electricity to grid (kWh) 0.0 859,490,068 865,758,172 867,671,460 868,803,529 869,596,370 870,200,786 870,686,054 871,089,570 871,433,728 871,732,959 871,997,081 872,233,061 872,446,015 872,639,805 872,817,414 872,981,192 873,133,019 873,274,427 873,406,675 873,530,811 873,647,716 873,758,141 873,862,724 873,962,017 874,056,500 874,146,590 874,232,654 874,315,016 874,393,962 874,466,670 +Electricity to grid (kWh) 0.0 859,490,068 865,758,172 867,671,460 868,803,529 869,596,370 870,200,786 870,686,054 871,089,570 871,433,728 871,732,959 871,997,081 872,233,061 872,446,015 872,639,805 872,817,414 872,981,191 873,133,019 873,274,427 873,406,675 873,530,811 873,647,716 873,758,141 873,862,724 873,962,017 874,056,500 874,146,590 874,232,654 874,315,016 874,393,962 874,466,670 Present value of annual costs ($) 554,074,192 -Present value of annual energy nominal (kWh) 7,877,183,891 +Present value of annual energy nominal (kWh) 7,877,183,890 LCOE Levelized cost of energy nominal (cents/kWh) 7.03 Present value of PPA revenue ($) 809,659,354 -Present value of annual energy nominal (kWh) 7,877,183,891 +Present value of annual energy nominal (kWh) 7,877,183,890 LPPA Levelized PPA price nominal (cents/kWh) 10.28 PROJECT STATE INCOME TAXES diff --git a/tests/examples/example_SAM-single-owner-PPA-5.txt b/tests/examples/example_SAM-single-owner-PPA-5.txt index fe78ef48d..b46d60ad2 100644 --- a/tests/examples/example_SAM-single-owner-PPA-5.txt +++ b/tests/examples/example_SAM-single-owner-PPA-5.txt @@ -46,7 +46,7 @@ Plant Lifetime, 30 Reservoir Model, 1 Reservoir Volume Option, 1, -- FRAC_NUM_SEP: Reservoir volume calculated with fracture separation and number of fractures as input -Number of Fractures, 1663, -- 55 fractures per well +Number of Fractures per Stimulated Well, 55 Fracture Shape, 3, -- Square Fracture Separation, 26, diff --git a/tests/geophires_x_tests/test_fervo_project_cape_5.py b/tests/geophires_x_tests/test_fervo_project_cape_5.py new file mode 100644 index 000000000..8dab62136 --- /dev/null +++ b/tests/geophires_x_tests/test_fervo_project_cape_5.py @@ -0,0 +1,461 @@ +from __future__ import annotations + +import re +from typing import Any + +from pint.facets.plain import PlainQuantity + +from base_test_case import BaseTestCase +from geophires_docs import generate_fervo_project_cape_5_md +from geophires_x.GeoPHIRESUtils import quantity +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 +from geophires_x_client import ImmutableGeophiresInputParameters + + +class FervoProjectCape5TestCase(BaseTestCase): + """ + FIXME WIP - see https://github.com/softwareengineerprogrammer/GEOPHIRES/pull/117 + """ + + def test_internal_consistency(self): + + fpc5_result: GeophiresXResult = GeophiresXResult( + self._get_test_file_path('../examples/Fervo_Project_Cape-5.out') + ) + fpc5_input_params: GeophiresInputParameters = ImmutableGeophiresInputParameters( + from_file_path=self._get_test_file_path('../examples/Fervo_Project_Cape-5.txt') + ) + fpc5_input_params_dict: dict[str, Any] = self._get_input_parameters(fpc5_input_params) + + def _q(dict_val: str) -> PlainQuantity: + spl = dict_val.split(' ') + return quantity(float(spl[0]), spl[1]) + + lateral_length_q = _q(fpc5_input_params_dict['Nonvertical Length per Multilateral Section']) + frac_sep_q = quantity(float(fpc5_input_params_dict['Fracture Separation']), 'meter') + number_of_fracs_per_well = int(fpc5_input_params_dict['Number of Fractures per Stimulated Well']) + + self.assertLess(number_of_fracs_per_well * frac_sep_q, lateral_length_q) + + result_number_of_wells: int = self._number_of_wells(fpc5_result) + number_of_fracs = int(fpc5_result.result['RESERVOIR PARAMETERS']['Number of fractures']['value']) + self.assertEqual(number_of_fracs, result_number_of_wells * number_of_fracs_per_well) + self.assertEqual(result_number_of_wells, int(fpc5_input_params_dict['Number of Doublets']) * 2) + + @staticmethod + def _get_input_parameters( + params: GeophiresInputParameters, include_parameter_comments: bool = False, include_line_comments: bool = False + ) -> dict[str, Any]: + """ + TODO consolidate with src/geophires_docs/generate_fervo_project_cape_5_md.py:30 as a common utility function. + Note doing so is non-trivial because there would need to be a mechanism to ensure parsing exactly matches + GEOPHIRES behavior, which may diverge from the below implementation under some circumstances. + """ + + comment_idx = 0 + ret: dict[str, Any] = {} + for line in params.as_text().split('\n'): + parts = line.strip().split(', ') # TODO generalize for array-type params + field = parts[0].strip() + if len(parts) >= 2 and not field.startswith('#'): + fieldValue = parts[1].strip() + if include_parameter_comments and len(parts) > 2: + fieldValue += ', ' + (', '.join(parts[2:])).strip() + ret[field] = fieldValue.strip() + + if include_line_comments and field.startswith('#'): + ret[f'_COMMENT-{comment_idx}'] = line.strip() + comment_idx += 1 + + # TODO preserve newlines + + return ret + + @staticmethod + def _number_of_wells(result: GeophiresXResult) -> int: + r: dict[str, dict[str, Any]] = result.result + + number_of_wells = ( + r['SUMMARY OF RESULTS']['Number of injection wells']['value'] + + r['SUMMARY OF RESULTS']['Number of production wells']['value'] + ) + + return number_of_wells + + def test_fervo_project_cape_5_results_against_reference_values(self): + """ + Asserts that results conform to some of the key reference values claimed in docs/Fervo_Project_Cape-5.md. + """ + + r = GeophiresXClient().get_geophires_result( + GeophiresInputParameters(from_file_path=self._get_test_file_path('../examples/Fervo_Project_Cape-5.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, 550) + self.assertLess(max_total_gen, 600) + + 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, 1) + self.assertLess(redrills, 6) + max_phase_2_permitted_wells = 320 + self.assertLess(self._number_of_wells(r) * redrills, max_phase_2_permitted_wells) + self.assertGreater(self._number_of_wells(r) * redrills, max_phase_2_permitted_wells * 0.9375) + + well_cost = r.result['CAPITAL COSTS (M$)']['Drilling and completion costs per well']['value'] + self.assertLess(well_cost, 5.0) + self.assertGreater(well_cost, 4.0) + + pumping_power_pct = r.result['SURFACE EQUIPMENT SIMULATION RESULTS'][ + 'Initial pumping power/net installed power' + ]['value'] + self.assertGreater(pumping_power_pct, 5) + self.assertLess(pumping_power_pct, 15) + + 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 + (partially) by hand so that they are guaranteed to be in sync and don't need to be tested like this, + which has proved messy. + + Update 2026-01-13: Markdown is now partially generated from input and result in + src/geophires_docs/generate_fervo_project_cape_5_md.py. + """ + + def generate_documentation_markdown() -> None: + generate_fervo_project_cape_5_md.main() + + generate_documentation_markdown() + + documentation_file_content = '\n'.join( + self._get_test_file_content('../../docs/Fervo_Project_Cape-5.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) + + example_result = GeophiresXResult(self._get_test_file_path('../examples/Fervo_Project_Cape-5.out')) + + expected_drilling_cost_MUSD_per_well = 4.46 + # number_of_doublets = inputs_in_markdown['Number of Doublets']['value'] + number_of_wells = self._number_of_wells(example_result) + self.assertAlmostEqualWithinSigFigs( + expected_drilling_cost_MUSD_per_well * number_of_wells, + results_in_markdown['Well Drilling and Completion Costs']['value'], + 3, + ) + self.assertEqual('MUSD', results_in_markdown['Well Drilling and Completion Costs']['unit']) + + expected_stim_cost_MUSD_per_well = 4.83 + self.assertAlmostEqualWithinSigFigs( + expected_stim_cost_MUSD_per_well * number_of_wells, results_in_markdown['Stimulation Costs']['value'], 3 + ) + self.assertEqual('MUSD', results_in_markdown['Stimulation Costs']['unit']) + + self.assertEqual( + expected_stim_cost_MUSD_per_well, inputs_in_markdown['Reservoir Stimulation Capital Cost per Well']['value'] + ) + self.assertEqual('MUSD', inputs_in_markdown['Reservoir Stimulation Capital Cost per Well']['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['Total CAPEX']).quantity() + markdown_capex_USD_per_kW = ( + capex_q.to('USD').magnitude + / _Q(results_in_markdown['Maximum Net Electricity Generation']).quantity().to('kW').magnitude + ) + self.assertAlmostEqual( + sig_figs(markdown_capex_USD_per_kW, 2), results_in_markdown['Total CAPEX: $/kW']['value'] + ) + + field_mapping = { + 'LCOE': 'Electricity breakeven price', + 'Project capital costs: Total CAPEX': 'Total CAPEX', + 'Well Drilling and Completion Costs': 'Drilling and completion costs per well', + 'Well Drilling and Completion Costs total': 'Drilling and completion costs', + 'Stimulation Costs total': 'Stimulation costs', + } + + ignore_keys = [ + 'Total CAPEX: $/kW', # See https://github.com/NREL/GEOPHIRES-X/issues/391 + 'Total fracture surface area per production well', + 'Stimulation Costs', # remapped to 'Stimulation Costs total' + ] + + 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] + + result_capex_USD_per_kW = ( + _Q(example_result._get_result_field('Total CAPEX')).quantity().to('USD').magnitude + / _Q(example_result._get_result_field('Maximum Net Electricity Generation')).quantity().to('kW').magnitude + ) + self.assertAlmostEqual(sig_figs(result_capex_USD_per_kW, 2), sig_figs(markdown_capex_USD_per_kW, 2)) + + 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'], + ) + + additional_expected_stim_indirect_cost_frac = 0.00 + expected_stim_cost_total_MUSD = ( + expected_stim_cost_MUSD_per_well * num_doublets * 2 * (1.0 + additional_expected_stim_indirect_cost_frac) + ) + self.assertAlmostEqualWithinSigFigs( + expected_stim_cost_total_MUSD, + example_result.result['CAPITAL COSTS (M$)']['Stimulation costs']['value'], + num_sig_figs=3, + ) + + 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*IRR\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', + 'Maximum Net Electricity Generation', + 'Number of times redrilling', + 'Total CAPEX', + 'Total CAPEX: $/kW', + 'WACC', + 'Well Drilling and Completion Costs', + 'Stimulation Costs', + ]: + structured_results[key_] = self._parse_value_unit(value_) + + # Handle drilling and stimulation costs in format: "$464M total ($4.46M/well)" + for result_with_total_key in ['Well Drilling and Completion Costs', 'Stimulation Costs']: + entry = structured_results[result_with_total_key] + + unit_str = entry['unit'] + # unit_str is like "total; $4.46M/well" after _parse_value_unit processes "$464M total ($4.46M/well)" + # The entry['value'] is 464 (total MUSD) + # We need to extract per-well value from unit string + + # Parse per-well value from the parenthetical part + per_well_match = re.search(r'\$(\d+\.?\d*)M/well', unit_str) + if per_well_match: + per_well_value = float(per_well_match.group(1)) + # Store total in 'X total' key + structured_results[f'{result_with_total_key} total'] = { + 'value': entry['value'], + 'unit': 'MUSD', + } + # Update entry to be per-well value + entry['value'] = per_well_value + entry['unit'] = 'MUSD/well' + + 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() + + if clean_str.startswith('$') and 'M total' in clean_str: + return {'value': float(clean_str.split('M total')[0][1:]), 'unit': 'MUSD'} + + # 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'} + + # 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'} + + # 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} + + # 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'} + + # Percentage format (X.X%) + match = re.search(r'(\d+\.?\d*)%$', clean_str) + if match: + value = float(match.group(1)) + return {'value': value, 'unit': '%'} + + # 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'} + + # 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} + + # 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'} + + def test_fervo_project_cape_6(self) -> None: + """ + Fervo_Project_Cape-6 is derived from Fervo_Project_Cape-5 - see tests/regenerate-example-result.sh + """ + + fpc5_result: GeophiresXResult = GeophiresXResult( + self._get_test_file_path('../examples/Fervo_Project_Cape-6.out') + ) + min_net_gen_dict = fpc5_result.result['SURFACE EQUIPMENT SIMULATION RESULTS'][ + 'Minimum Net Electricity Generation' + ] + fpc5_min_net_gen_mwe = quantity(min_net_gen_dict['value'], min_net_gen_dict['unit']).to('MW').magnitude + self.assertGreater(fpc5_min_net_gen_mwe, 100) + self.assertLess(fpc5_min_net_gen_mwe, 110) diff --git a/tests/geophires_x_tests/test_well_bores.py b/tests/geophires_x_tests/test_well_bores.py index 7db431f1b..30b2ebace 100644 --- a/tests/geophires_x_tests/test_well_bores.py +++ b/tests/geophires_x_tests/test_well_bores.py @@ -82,6 +82,50 @@ def test_number_of_doublets_non_integer(self): self.assertEqual(prod_inj_lcoe_2[0], 199) self.assertEqual(prod_inj_lcoe_2[1], 199) + def test_number_of_injection_wells_per_production_well(self): + r_ratio: GeophiresXResult = self._get_result( + { + 'Number of Production Wells': 63, + 'Number of Injection Wells per Production Well': 0.666, # 3:2 ratio + } + ) + + r_explicit_counts: GeophiresXResult = self._get_result( + {'Number of Production Wells': 63, 'Number of Injection Wells': 42} + ) + + self.assertEqual(self._prod_inj_lcoe_production(r_explicit_counts), self._prod_inj_lcoe_production(r_ratio)) + + self.assertEqual( + self._prod_inj_lcoe_production( + self._get_result( + { + 'Number of Production Wells': 2, # default value + 'Number of Injection Wells per Production Well': 3, + } + ) + ), + self._prod_inj_lcoe_production(self._get_result({'Number of Injection Wells per Production Well': 3})), + ) + + with self.assertRaises(RuntimeError): + self._get_result( + { + 'Number of Production Wells': 63, + 'Number of Injection Wells per Production Well': 0.6666, # 3:2 ratio + 'Number of Injection Wells': 42, + } + ) + + with self.assertRaises(RuntimeError): + self._get_result( + { + 'Number of Production Wells': 63, + 'Number of Injection Wells per Production Well': 0.6666, # 3:2 ratio + 'Number of Doublets': 52, + } + ) + # noinspection PyMethodMayBeStatic def _get_result(self, _params) -> GeophiresXResult: params = GeophiresInputParameters( diff --git a/tests/regenerate-example-result.env.template b/tests/regenerate-example-result.env.template new file mode 100644 index 000000000..e71088891 --- /dev/null +++ b/tests/regenerate-example-result.env.template @@ -0,0 +1,4 @@ +# export GEOPHIRES_FPC5_SENSITIVITY_ANALYSIS_PROJECT_ROOT= + +export SWS_GTP_CLIENT_USERNAME= +export SWS_GTP_CLIENT_USER_PASSWORD= diff --git a/tests/regenerate-example-result.sh b/tests/regenerate-example-result.sh index faef5cee3..061e46cc5 100755 --- a/tests/regenerate-example-result.sh +++ b/tests/regenerate-example-result.sh @@ -1,15 +1,16 @@ #!/bin/zsh +set -e +cd "$(dirname "$0")" # Use this script to regenerate example results in cases where changes in GEOPHIRES # calculations alter the example test output. Example: # ./tests/regenerate-example-result.sh SUTRAExample1 # See https://github.com/NREL/GEOPHIRES-X/issues/107 -# Note: make sure your virtualenv is activated before running or this script will fail +# Note: make sure your virtualenv is activated and you have run pip install -e . before running or this script will fail # or generate incorrect results. -cd "$(dirname "$0")" python -mgeophires_x examples/$1.txt examples/$1.out rm examples/$1.json @@ -18,3 +19,35 @@ then echo "Updating CSV..." python regenerate_example_result_csv.py example1_addons fi + +if [[ $1 == "Fervo_Project_Cape-5" ]] +then + python ../src/geophires_docs/generate_fervo_project_cape_5_docs.py + + echo "Regenerating Fervo_Project_Cape-6..." + + sed -e 's/Construction Years,.*/Construction Years, 3/' \ + -e 's/^Number of Production Wells,.*/Number of Production Wells, 13/' \ + -e 's/500 MWe/100 MWe/' \ + -e 's/Phase II/Phase I/' \ + examples/Fervo_Project_Cape-5.txt > examples/Fervo_Project_Cape-6.txt + + python -mgeophires_x examples/Fervo_Project_Cape-6.txt examples/Fervo_Project_Cape-6.out + rm examples/Fervo_Project_Cape-6.json + + if [ ! -f regenerate-example-result.env ] && [ -f regenerate-example-result.env.template ]; then + echo "Creating regenerate-example-result.env from template..." + cp regenerate-example-result.env.template regenerate-example-result.env + fi + + source regenerate-example-result.env + if [ -n "$GEOPHIRES_FPC5_SENSITIVITY_ANALYSIS_PROJECT_ROOT" ]; then + echo "Updating sensitivity analysis..." + STASH_PWD=$(pwd) + cd $GEOPHIRES_FPC5_SENSITIVITY_ANALYSIS_PROJECT_ROOT + source venv/bin/activate + python -m fpc_sensitivity_analysis.generate_geophires_fpc5_sensitivity_analysis + deactivate + cd $STASH_PWD + fi +fi diff --git a/tox.ini b/tox.ini index ce5b99e78..320d5c6a9 100644 --- a/tox.ini +++ b/tox.ini @@ -38,6 +38,7 @@ usedevelop = false deps = pytest pytest-cov + Jinja2 commands = {posargs:pytest --cov --cov-report=term-missing --cov-report=xml -vv tests} @@ -61,8 +62,10 @@ deps = -r{toxinidir}/docs/requirements.txt commands = python src/geophires_x_schema_generator/main.py --build-path docs/ + python src/geophires_docs/__main__.py sphinx-build {posargs:-E} -b html docs dist/docs sphinx-build docs dist/docs + python -c "import shutil; shutil.copytree('docs/_images', 'dist/docs/_images', dirs_exist_ok=True)" ; TODO re-enable linkcheck probably - `sphinx-build -b linkcheck docs dist/docs` [testenv:report]