run.py → inputs.py → fleet_results.py → outputs.py calculation.
Domain, dimensions and units
A fleet need is one vehicle service requirement, keyed byFleetNeed.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.
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 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 areInputs, 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 fromstart_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 and . Daily unconstrained electric demand is .- ICE: and .
- BEV: and the complete electric demand is served, even if it exceeds battery capacity. Charging availability is not a feasibility constraint here.
- PHEV: if , all driving and idling are electric. Otherwise, for , set , electric distance , remaining distance , and fuel when . PHEV idling remains electric. A nonpositive battery raises an error.
Retirement and synthetic penalties
The recurring replacement cycle is . Initial eligibility uses the minimum of remaining retirement age and . Purchase indicators reset the subsequent replacement schedule. A zero resulting cycle is invalid for penalty division. For penalties, age is and mileage grows from the supplied starting mileage. Age and mileage thresholds use strict> comparisons. Let indicate either exceeded threshold, and be the configured age/mileage multipliers when exceeded and one otherwise, and be the same-powertrain replacement cost.
For ICE, . The additional component is calculated using the maximum EV and same-powertrain prices after multiplication by :
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: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 , , , , , 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.