utils

This module contains utility functions for the model.

Here, we determine factors to covert future value to present value for costs and benefits. To account for the variable factor, fixed factor, and capital factor, we need to convert all future costs to their net present value. This means adjusting for the time value of money so that all costs are expressed in terms of today's dollars.

We also assume that variable cost and fixed cost for non-modelled years are assumed to be equal to the cost of the last modelled year preceding them. This allows for consistent comparison across different time periods and technologies.

Variable Factor

Calculation of variable costs

Given the following:

  • Variable cost of modeled year: \(B\)

  • Discount rate: \(r\)

  • \(m\)-th modeled year: \(m = y - y_\text{min}\)

  • Depreciation periods: \(n\)

The total present value can be calculated as follows:

\begin{align} \text{total present value} &= \frac{B}{(1+r)^m} + \frac{B}{(1+r)^{m+1}} + \cdots + \frac{B}{(1+r)^{(m+k-1)}} \\ &= B(1+r)^{(1-m)}\frac{1-(1+r)^k}{r} \end{align}

And we can calculate the variable factor as follows:

\begin{align} \text{factor}_{y}^{var} &= (1+r)^{1-m_y}\frac{1-(1+r)^{k_y}}{r} \\ m_{y} &= y - y_\text{min} \\ k_{y} &= y_\text{periods} \end{align}

Fixed Factor

We can equate the fixed factor with the variable factor as follows:

\[\text{factor}_{y}^\text{fix} = factor_{y}^\text{var}\]

Investment Factor

Calculation of investment costs

Given the following:

  • Weighted Average Cost of Capital (WACC, or otherwise known as the interest rate): \(i\)

  • Discount rate: \(r\)

  • \(m\)-th modeled year: \(m = y - y_\text{min}\)

  • Length of \(m\)-th planning periods: \(k\)

The total present value can be calculated as follows:

\begin{align} \text{total present value} &= \frac{P}{(1+r)^m} \\ &= \frac{\frac{A}{(1+i)} + \frac{A}{(1+i)^2} + \cdots + \frac{A}{(1+i)^n}}{(1+r)^m} \\ &= A\frac{1-(1+i)^{-n}}{i}\times\frac{1}{(1+r)^m} \end{align}

From the above, we can solve for the annualized cost of depreciation periods, \(A\), as:

\[A = P\frac{i}{1-(1+i)^{-n}}\]

The capital recovery factor is then calculated as:

\[\text{capital recovery factor} = \frac{i}{1-(1+i)^{-n}}\]

Let's focus on the time periods that fall within the modelled time horizon (indicated in black colour). We can calculate the length of time periods, \(k\), as follows:

\[k = y_{max} - y\]

Using \(k\), we can calculate the net present value as follows:

\[\begin{split}\text{net present value} = \begin{cases} \frac{\frac{A}{(1+r)} + \frac{A}{(1+r)^2} + \cdots + \frac{A}{(1+r)^{min(n, k)}}}{(1+r)^m} \text{if} \quad n \le k \\ \text{total present value}\quad \text{if }n > k \\ \frac{A\frac{1-(1+r)^{-k}}{r}}{(1+r)^m} = P\frac{i}{1-(1+i)^{-n}}\times \frac{1-(1+r)^{-k}}{r(1+r)^m} & \text{otherwise} \end{cases}\end{split}\]

And we can calculate the investment factor as follows:

\[factor_{y}^{inv} = \frac{i}{1-(1+i)^{-n}}\times \frac{1-(1+r)^{-min(n,k)}}{r(1+r)^m}\]
prepshot.utils.calc_cost_factor(discount_rate, modeled_year, year_min, next_modeled_year)[source]

Compute the variable and fixed cost factor while considering the multi-stage planning horizon.

Parameters
  • discount_rate (float) -- The discount rate to apply.

  • modeled_year (int) -- The year in which the cost occurs.

  • year_min (int) -- The first year of the planning horizon. All costs are discounted to this year.

  • next_modeled_year (int) -- The subsequent modeled year. The cost incurred bewteen modeled_year and modeled_year and next_modeled_year is calculated.

Returns

The computed cost factor.

Return type

float

Raises

ValueError -- if next_modeled_year < modeled_year.

Examples

Given annual cost incurred in 2025, next_modeled_year = 2030, and starting year = 2020, compute present value in 2020 of the cost incurred in 2025-2029:

>>> calc_cost_factor(0.05, 2025, 2020, 2030)
3.561871
prepshot.utils.calc_interest_rate(public_debt_ratio, private_debt_ratio, cost_of_public_debt, cost_of_private_equity, cost_of_private_debt)[source]

Weighted-average cost of capital for a single project.

Combines public-debt, private-debt and equity tranches into a single project-level interest rate that the investment cost factor uses to discount construction outlays. Equity ratio is the residual 1 - public_debt_ratio - private_debt_ratio.

Parameters
  • public_debt_ratio (float) -- Share of project capital financed by public debt, in [0, 1].

  • private_debt_ratio (float) -- Share financed by private debt, in [0, 1].

  • cost_of_public_debt (float) -- Annual interest rate on the public-debt tranche.

  • cost_of_private_equity (float) -- Required annual return on the equity tranche.

  • cost_of_private_debt (float) -- Annual interest rate on the private-debt tranche.

Returns

Weighted-average interest rate for the project.

Return type

float

Raises

ValueError -- If any of the three ratios is negative or their sum exceeds 1.

prepshot.utils.calc_inv_cost_factor(dep_period, interest_rate, year_built, discount_rate, year_min, year_max)[source]

Compute the investment cost factor. When the depreciation period is greater than the planning horizon, the investment cost factor is calculated by only considering the period within the planning horizon.

Parameters
  • dep_period (int) -- Depreciation period, in years, i.e., lifetime of the infrastructure.

  • interest_rate (float) -- Interest rate.

  • year_built (int) -- Year of investment.

  • discount_rate (float) -- Discount rate.

  • year_min (int) -- Minimum year, i.e., the first year of the planning horizon.

  • year_max (int) -- Maximum year, i.e., the last year of the planning horizon.

Returns

Investment cost factor.

Return type

float

Raises

ValueError -- If year_max <= year_min, year_max < year_built, or year_built < year_min.

Examples

Given a depreciation period of 20 years, interest rate of 0.05, year of investment in 2025, discount rate of 0.05, planning horizon from 2020 to 2050, compute the investment cost factor:

>>> calc_inv_cost_factor(20, 0.05, 2025, 0.05, 2020, 2050)
0.783526

If the depreciation perios is 100 years, compute the investment cost factor for the same scenario:

>>> calc_inv_cost_factor(100, 0.05, 2025, 0.05, 2020, 2050)
0.567482
prepshot.utils.cartesian_product(*args)[source]

Generate cartesian product of input iterables.

Parameters

args (List[Union[int, str]]) -- Iterables to be combined.

Returns

List of tuples representing the Cartesian product.

Return type

List[Tuple[Union[int, str]]]

Examples

Combine two lists [1, 2] and [7, 8]:

>>> cartesian_product([1, 2], [7, 8])
[(1, 7), (1, 8), (2, 7), (2, 8)]
prepshot.utils.check_positive(*values)[source]

Ensure all values are greater than 0.

Parameters

values (Union[int, float]) -- Values to be checked.

Raises

ValueError -- If any value is less than or equal to 0.

Return type

None

prepshot.utils.interpolate_z_by_q_or_s(name, qs, zqv)[source]

Interpolate forebay water level (Z) by reservoir storage (S) or tailrace water level (Z) by the reservoir outflow (Q).

Parameters
  • name (str) -- Code of the hydropower station.

  • qs (Union[np.ndarray, float]) -- Reservoir storage or outflow values.

  • zqv (pandas.DataFrame) -- DataFrame of ZQ or ZV values.

Returns

Interpolated values.

Return type

Union[np.ndarray, float]

prepshot.utils.sparse_tupledict(index_iter, rule)[source]

Like poi.make_tupledict but iterates an explicit list of multi-dim keys instead of a Cartesian product of axes.

The stock poi.make_tupledict(I, J, K, rule=...) calls rule(*coord) over len(I)*len(J)*len(K) tuples and routes every tuple through flatten_tuple (one call) plus an isinstance check (per element). For dense, structurally full grids that's the right thing. For PREP-SHOT's (h, m, y, z, te)-style grids though, the (z, te) sub-axis is overwhelmingly sparse -- a thermal unit lives at exactly one bus, so 99% of the (z, te) pairs are 0-by-construction. Walking those pairs through the rule is wasted work.

This helper takes a pre-built iterable of keys (each key is the full multi-dim tuple, e.g. (h, m, y, z, te)) and avoids the Cartesian / flatten overhead. It otherwise behaves like make_tupledict: returns a poi.tupledict (so .select() still works), and skips entries whose rule returns None.

Parameters
  • index_iter (iterable of tuple) -- Each element is a complete multi-dim key. Single-element keys may be passed as a bare value rather than a 1-tuple.

  • rule (callable) -- Called as rule(*key) per key.

Returns

Mapping key -> rule(*key) for keys whose rule returned a non-None value.

Return type

poi.tupledict