> ## Documentation Index
> Fetch the complete documentation index at: https://docs.models.dunsky.com/llms.txt
> Use this file to discover all available pages before exploring further.

# eFleet calculation specification

> Exact eFleet contracts for annual replacement decisions, vehicle energy use, costs, emissions, penalties, limits and reporting conventions.

eFleet evaluates replacement strategies for individual vehicle needs. This specification describes the active `run.py → inputs.py → fleet_results.py → outputs.py` calculation.

## Domain, dimensions and units

A fleet need is one vehicle service requirement, keyed by `FleetNeed.key`, with region, facility, annual distance, annual idle time, subtype and initial vehicle. Subtype acquisition options specify ICE, BEV and PHEV performance and prices. Counts represent vehicle needs, not weighted population samples.

| Symbol | Definition | Units |
| - | - | - |
| $D,I,W$ | Annual distance, idle hours, working days | distance/year, h/year, days/year |
| $e,e_i$ | Electric driving and idle intensity | kWh/distance, kWh/h |
| $f,f_i$ | Fuel driving and idle intensity | fuel-unit/distance, fuel-unit/h |
| $B$ | Usable battery quantity supplied to the calculation | kWh |
| $p^e_y,p^f_y$ | Electricity and fuel prices | currency/kWh, currency/fuel-unit |
| $g^e_y,g^f_y$ | Emission intensities | grams CO2e per corresponding energy unit |
| $c_y$ | Carbon price | currency/tCO2e |

Distance inputs, efficiencies, retirement mileage and per-distance maintenance must use the same distance unit. The calculation does not convert kilometres to miles. The division by $10^6$ in emissions requires intensities in grams per energy unit. Monetary series are added in their supplied currency without inflation or exchange-rate conversion.

## Workbook contract and defaults

Required worksheets are `Inputs`, `Annual Inputs`, `Global Data`, `Vehicle Specs`, `EV Availability`, `Maintenance Costs`, `Basic Fleet Info` and the three `Replacement Costs (ICE)`, `(BEV)`, `(PHEV)` sheets.

`Inputs` reads `StartYear`, `EndYear`, `Horizon`, `GHG_Penalty` and `NumPHEV_Cycles`. Regional prices and intensities use annual columns recognized by `Y?2...`, accepting year-like labels and `Y2026`-style names. A complete `all` region provides fallback energy and carbon values. A regional energy series is selected based on its first cost-year value; missing later values are not individually backfilled.

The workbook importer supplies 250 working days unless its override branch applies. That branch tests `WorkingDays` against an already lowercased column index, so the intended override does not take effect for ordinary string headers. Programmatic `FleetNeed` construction defaults to 365. Do not treat the two entry paths as having identical defaults.

`StudyParameters` declares age and mileage penalty multipliers of 1.035 and ICE penalty multiplier of 1.01. Its global `early_rep` workbook assignment evaluates `bool("EarlyRep_Allowed")`, which is always true; per-need early replacement eligibility is the value checked by purchase selection. `ghg_penalty` and `phev_cycles` are read but do not multiply annual usage energy in `_calc_usage_costs`.

## Time indexing

Cost years run from `start_year` through `horizon + max_ret_age`, inclusive. Purchase indicators extend through `horizon`, but annual decision loops use `range(start_year, horizon)`, excluding the horizon year. `end_year` controls the post-study ICE penalty threshold. Costs are compared over the full cost-year array, not only study years, and are summed without discounting.

## Daily and annual energy

Set $d=D/W$ and $i=I/W$. Daily unconstrained electric demand is $E_d=de+ie_i$.

* ICE: $E_d=0$ and $F_d=df+if_i$.
* BEV: $F_d=0$ and the complete electric demand is served, even if it exceeds battery capacity. Charging availability is not a feasibility constraint here.
* PHEV: if $B>E_d$, all driving and idling are electric. Otherwise, for $B>0$, set $E_d=B$, electric distance $d_e=(B-ie_i)/e$, remaining distance $d_f=d-d_e$, and fuel $F_d=d_ff$ when $d_f>0$. PHEV idling remains electric. A nonpositive battery raises an error.

The comparison is strict: equality follows the battery-limited branch. Electric distance is not clipped to zero if idle energy exceeds battery capacity. Positive annual distance and working days, consistent nonzero efficiencies and physically compatible idle/battery inputs are required for meaningful results; the implementation does not comprehensively validate those conditions.

$$
E_y=WE_d,\quad F_y=WF_d,\quad C^{energy}_y=W(E_dp^e_y+F_dp^f_y)
$$

With electric distance share $a=d_e/d$, maintenance for non-ICE options is $D[am_{BEV}+(1-a)m_{ICE}]$; ICE uses $Dm_{ICE}$.

$$
G_y=\frac{W(E_dg^e_y+F_dg^f_y)}{10^6},\qquad C^{carbon}_y=G_yc_y
$$

## Retirement and synthetic penalties

The recurring replacement cycle is $L=\min(\lfloor M_{ret}/D\rfloor,A_{ret})$. Initial eligibility uses the minimum of remaining retirement age and $\max(0,\lceil(M_{ret}-M_0)/D\rceil)$. Purchase indicators reset the subsequent replacement schedule. A zero resulting cycle is invalid for penalty division.

For penalties, age is $a_y=y-y_{vehicle}+1$ and mileage grows from the supplied starting mileage. Age and mileage thresholds use strict `>` comparisons. Let $u_y$ indicate either exceeded threshold, $v_y$ and $z_y$ be the configured age/mileage multipliers when exceeded and one otherwise, and $C_y$ be the same-powertrain replacement cost.

$$
P^{ret}_y=\frac{\max_t(C_tu_t)}{L}v_yz_yu_y
$$

For ICE, $j_y=\mathbf1[y>end\_year]k_{ICE}$. The additional component is calculated using the maximum EV and same-powertrain prices after multiplication by $j$:

$$
b=\frac{\max(\max_t(C^{EV}_tj_t)-\max_t(C_tj_t),0)}{L}k_{ICE},\qquad P_y=P^{ret}_y+bj_y
$$

The EV price reference uses BEV when available, otherwise PHEV. These synthetic penalties influence selection; they are not cash expenditures. The repeated ICE multiplier is part of the implemented calculation.

## Annual selection algorithm

Initialize a keep-all strategy and derive same-powertrain and alternative-powertrain trial strategies. For each decision year, candidate purchase costs, operating costs and penalties are updated; previously accepted decisions remain in the strategy. Candidate savings for each need are:

$$
S_n=\sum_y(C^{current}_{n,y}-C^{trial}_{n,y})
$$

`total_cost_mat` includes capital, energy, maintenance, carbon and penalty costs. Sort needs by savings and traverse from largest to smallest. Purchase only for strictly positive savings, an eligible early/due replacement and available fleet/subtype capacity. Equal savings follow NumPy's ordering; there is no explicit business tie-breaker.

Fleet cap is `round(percent × fleet_count / 100)`. Subtype caps use the same rounding except a positive raw quantity below one becomes one. A zero subtype cap remains zero. Without a fleet cap, capacity equals fleet size. This greedy annual ranking is not a simultaneous multi-year optimization.

### Input-series validation

`check_inputs` requires fuel price/intensity, subtype replacement-limit and acquisition-price series to extend through **one additional year beyond the last cost year**. That is `horizon + max_ret_age + 1`, although usage calculations end one year earlier. Missing subtype limits log an error and continue through the caught `AttributeError`; insufficient supplied year coverage raises `ValueError`. Missing initial vehicle mileage is replaced with zero with a warning.

## Output contract and validation

`StudyResults.replacement_strategies` contains strategy objects with vehicle-by-cost-year capital, energy, fuel, electricity, maintenance, carbon, emissions and penalty matrices, plus purchase/replacement indicators and option selections. `no_penalty_cost_mat` excludes penalties. Reporting produces strategy summaries, replacement plans, vehicle and fleet costs/emissions and facility charging energy. Facility charging energy is annual kWh, not charger power or an hourly dispatch profile.

For an illustrative BEV with $D=20,000$, $I=100$, $W=250$, $e=0.2$, $e_i=1$, annual electricity is 4,100 kWh. At 0.15 currency/kWh, energy costs 615/year. At 100 g/kWh, emissions are 0.41 tCO2e/year. These assumptions are examples, not defaults.

Check annual energy against distance and idle inputs, cash costs separately from penalty-inclusive selection, cap rounding, cost-year endpoints, replacement timing and repeatability under the same input order. Existing tests cover usage, savings, replacement limits and penalty behaviour; missing acquisition options and invalid vehicle powertrain references raise explicit errors. Unchecked missing Excel columns, zero denominators and incompatible year labels can fail earlier in pandas or arithmetic operations.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.