Fitting parameters¶
Fitting is staged: because the linear rate f is a product of independent
stress factors, you cannot fit everything from one data set. You pin the SEI
envelope first, then extract one rate per condition, then fit each stress
factor from data that varies only that condition.
This page is the practical recipe; the fitting architecture explains the design and the contracts between stages in depth.
0. Prepare the data¶
SampleData holds aging measurements. Feed it plain DataFrames plus a
DataType and the units they arrive in; the selectors return flat frames in
the internal units.
import esf
data = esf.SampleData()
data.add_data(
frame, # columns: t, SoH, T, SoC[, subset]
data_type=esf.DataType.CALENDAR_VS_TEMPERATURE,
time_unit="days",
temperature_unit="K",
)
data.calculate_life_fraction() # adds L = 1 - SoH
1. SEI fit at reference conditions¶
Uses only data at the reference temperature and SoC, where every stress factor
is 1 and f is the reference rate. This is the only stage where sei_alpha
and sei_beta vary.
prms = esf.get_example_params()
at_reference = data.calendar_life_vs_temperature(filter_value=298.15, strict_mode=False)
esf.sei_fit_at_reference_conditions(
prms, at_reference, data_type=esf.DataType.CALENDAR_VS_TEMPERATURE
)
2. One degradation rate per condition¶
With the envelope frozen, every temperature (or SoC) series is refit for its linear rate only.
rates = esf.degradation_rates_fit(
prms,
data.calendar_life_vs_temperature(strict_mode=False),
data_type=esf.DataType.CALENDAR_VS_TEMPERATURE,
)
3. Stress-factor fits¶
Each stress-factor fit normalizes the rates by the reference rate
(S = f / f_ref) and fits its model function.
esf.temperature_stress_factor_fit(prms, rates)
esf.soc_stress_factor_fit(prms, rates_vs_soc, data_type=esf.DataType.CALENDAR_VS_SOC)
esf.time_stress_factor_calc(prms, rates, data_type=esf.DataType.CALENDAR_VS_TEMPERATURE)
The DoD stress factor¶
DoD is special: its data is cycle life N to end-of-life vs DoD, and the
model output is the per-cycle rate (it is not normalized to 1 at a reference
DoD). The model form follows the chemistry (battery_chemistry): empirical
for LMO, exponential for LFP, quadratic for NMC.
prms = esf.ESFParams(battery_chemistry="NMC") # -> quadratic DoD form
esf.dod_stress_factor_fit(prms, frame, data_type=esf.DataType.CYCLE_VS_DOD)
With is_at_reference=False the temperature, SoC and calendar-time stress
factors are stripped out first (Xu et al. eqs. 20/31), which needs per-point
T, SoC and t_cycle columns.
Results, overrides, verbosity¶
- By default fits write into
ESFParams(andmark_changed()timestamps it). Passapply=Falseto leaveprmsuntouched and read the result viafit.fitted_parameters(). - Every fit accepts
verbose=True(report + plots) and aparameter_overridesdict, e.g.parameter_overrides={"x_ref": {"value": 1.0, "vary": False}}. Unknown parameter names raise with the list of valid ones.