Skip to content

Adding inflation rate to numpy NPV calc - #788

Open
johnjasa wants to merge 7 commits into
NatLabRockies:developfrom
johnjasa:fix_issue_339
Open

Adding inflation rate to numpy NPV calc#788
johnjasa wants to merge 7 commits into
NatLabRockies:developfrom
johnjasa:fix_issue_339

Conversation

@johnjasa

Copy link
Copy Markdown
Collaborator

Allow user to provide inflation rate to NumPy Financial NPV model

Resolves #339.

  1. Added an optional inflation_rate input to NumpyFinancialNPVFinanceConfig. Defaults to 0.0, validator-constrained to [0, 1].
  2. Combined it with discount_rate via the Fisher equation (1 + r_eff) = (1 + discount_rate) * (1 + inflation_rate) to match how ProFAST combines its real discount rate and general_inflation inputs, keeping the two finance backends consistent.
  3. Default behavior is preserved: with inflation_rate=0 the effective rate reduces to discount_rate, so existing configs and tests don't change.

Section 1: Type of Contribution

  • Feature Enhancement
    • Framework
    • New Model
    • Updated Model
    • Tools/Utilities
    • Other (please describe):
  • Bug Fix
  • Documentation Update
  • CI Changes
  • Other (please describe):

Section 2: Draft PR Checklist

  • Open draft PR
  • Describe the feature that will be added
  • Fill out TODO list steps
  • Describe requested feedback from reviewers on draft PR
  • Complete Section 8: New Model Checklist (if applicable)

Type of Reviewer Feedback Requested (on Draft PR)

Structural feedback:

  • The original issue suggested summing the two rates. I switched to the Fisher equation (1 + r_eff) = (1 + discount_rate) * (1 + inflation_rate) because it's the exact relationship between real and nominal rates and is what ProFAST uses internally.

Implementation feedback:

  • inflation_rate is validated with range_val(0, 1) to mirror discount_rate. Let me know if a different range (e.g. negative inflation) should be allowed.

Other feedback:

  • N/A

Section 3: General PR Checklist

  • PR description thoroughly describes the new feature, bug fix, etc.
  • Added tests for new functionality or bug fixes
  • Tests pass (If not, and this is expected, please elaborate in the Section 6: Test Results)
  • Documentation
    • Docstrings are up-to-date
    • Related docs/ files are up-to-date, or added when necessary
    • Documentation has been rebuilt successfully
    • Examples have been updated (if applicable)
  • CHANGELOG.md
    • At least one complete sentence has been provided to describe the changes made in this PR
    • After the above, a hyperlink has been provided to the PR using the following format:
      "A complete thought. PR XYZ", where
      XYZ should be replaced with the actual number.

Section 4: Related Issues

Fully resolves #339.

Section 5: Impacted Areas of the Software

Section 5.1: New Files

  • N/A

Section 5.2: Modified Files

  • h2integrate/finances/numpy_financial_npv.py
  • h2integrate/finances/test/test_numpy_financial_npv.py

Section 6: Additional Supporting Information

N/A

For typical project finance rates (real 5%, inflation 4%), the multiplicative form gives r_eff = 0.092 vs. the additive 0.09 results in a 0.2 pp difference.

Section 7: Test Results, if applicable

$ python -m pytest h2integrate/finances/test/test_numpy_financial_npv.py -v
...
5 passed, 8 subtests passed in 0.37s

Ruff lint + format clean on the two modified files.

Section 8 (Optional): New Model Checklist

N/A

@johnjasa
johnjasa requested a review from jaredthomas68 June 22, 2026 17:40

@jaredthomas68 jaredthomas68 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a good start. However, there are a few other related values that we should be aware of in our implementation and documentation to be clear and help users properly analyze the results. The rate used as shown should probably be the WACC, but that is not mentioned anywhere in the documentation. If we want discount rate to be the expected rate of return on equity, then we need to also request the debt rate the, the tax rate, and the debt/equity ratio (or debt-equity split).

| --------------------------------- | ---------------- | ----------------------------------------------------- | ----------- |
| `plant_life` | `int` | Operating life of the plant in years. Must be ≥ 0. | — |
| `discount_rate` | `float` | Discount rate (0–1). | — |
| `discount_rate` | `float` | Discount rate (0–1). Can be either a real or nominal rate depending on how `inflation_rate` is specified. | — |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be more clear to specify if we want real or nominal if we are always adjusting with the Fisher equation. So, instead of discount_rate, call it real_discount_rate if we are always adding in inflation to the discount rate.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way we are using discount_rate, I think it should probably be the weighted average cost of capital (WACC) unless we are also getting the debt rate and debt/equity ratio and the tax rate as inputs.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

numpy NPV is a very simple NPV calculation - finance parameters like debt to equity ratio or debt rate are not included.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood on your views here, Jared! I think we should do this renaming of discount rate across all models simultaneously outside of this PR so I've made an issue here: #812

I agree with Elenya that the numpy NPV calc is so simplistic that other values like WACC might not come into play. Are you suggesting that we have a different input into the numpy NPV model, either in value or name?

Comment thread docs/finance_models/numpy_financial_npv.md Outdated

3. Discounting
- Each series of cash flows is discounted using NumPy Financial’s `npf.npv(discount_rate, values)`.
- Each series of cash flows is discounted using NumPy Financial’s `npf.npv(effective_rate, values)`, where `effective_rate = (1 + discount_rate) * (1 + inflation_rate) - 1` is the Fisher-equation combination of the user-supplied real (or nominal) discount rate and inflation rate. When `inflation_rate` is left at its default of 0, this reduces to `discount_rate`, so the user can supply either a nominal discount rate alone or split it into a real discount rate plus an inflation rate. The multiplicative (Fisher) form matches the way ProFAST combines its real discount rate and `general_inflation` inputs.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this discussion is slightly confusing. See my comment above about calling the input real_discount_rate.

@johnjasa
johnjasa requested a review from jaredthomas68 July 23, 2026 20:25
@johnjasa
johnjasa marked this pull request as ready for review July 27, 2026 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants