thevenin#
Summary
The Thevenin equivalent circuit model is a common low-fidelity battery model
consisting of a single resistor in series with any number of RC pairs, i.e.,
parallel resistor-capacitor pairs. This Python package contains an API for
building and running experiments using Thevenin models. When referring to the
model itself, we use capitalized “Thevenin”, and for the package lowercase
thevenin.
Accessing the Documentation
Documentation is accessible via Python’s help() function which prints
docstrings from a package, module, function, class, etc. You can also access
the documentation by visiting the website, hosted on Read the Docs. The website
includes search functionality and more detailed examples.
Submodules#
Classes#
All-step solution. |
|
Experiment builder. |
|
Prediction model wrapper. |
|
Simulation model wrapper. |
|
Single-step solution. |
|
Transient state for predictions. |
Functions#
|
Copy example templates to into a local directory. |
List names of available example templates. |
|
|
Load example templates by name. |
|
Print example templates. |
Package Contents#
- class thevenin.CycleSolution(*soln, t_shift=0.001)[source]#
All-step solution.
A solution instance with all experiment steps stitch together into a single cycle.
- Parameters:
*soln (StepSolution) – All unpacked StepSolution instances to stitch together. The given steps should be given in the same sequential order that they were run.
t_shift (float) – Time (in seconds) to shift step solutions by when stitching them together. If zero the end time of each step overlaps the starting time of its following step. The default is 1e-3.
- append_soln(soln, t_shift=0.001)[source]#
Append another solution object to the current instance. Appending the instance itself is also allowed, which is helpful to visualize multiple cycles.
- Parameters:
soln (StepSolution | CycleSolution) – A solution (step or cycle) to append to the end of the current instance.
t_shift (float, optional) – Time (in seconds) to add between the current solution and new appended solution. If zero, the current final time and new initial time will exactly overlap. The default is 1e-3.
- Returns:
None.
- Raises:
TypeError – ‘soln’ input must be StepSolution, CycleSolution.
ValueError – ‘soln’ input is incompatible because it came from a simulation with different num_RC_pairs.
Notes
The
t_shiftinput only affects the shift between the current and new solutions. For example if twoCycleSolutionhad original shifts of 1 and 5 for their respective steps when stitched, this appended case will still have 1 for the first few steps,t_shiftbetween, and 5 for the shifts between the latter half’s steps.Appending solutions cannot simply be undone. If you think you may want to go back to an instance prior to appending another solution then you should create a copy to operate on. Use
copy.deepcopyfrom Python’s standard library to make sure the copy is memory safe, i.e., the two instances do not share any common memory.
- get_steps(idx)[source]#
Return a subset of the solution.
- Parameters:
idx (int | tuple) – The step index (int) or range (first, last) to return.
- Returns:
StepSolution|CycleSolution– The returned solution subset. A StepSolution is returned if ‘idx’ is an int, and a CycleSolution will be returned for the range of requested steps when ‘idx’ is a tuple.
- plot(x, y, **kwargs)#
Plot any two variables in ‘vars’ against each other.
- Parameters:
x (str) – A variable key in ‘vars’ to be used for the x-axis.
y (str) – A variable key in ‘vars’ to be used for the y-axis.
**kwargs (dict, optional) – Keyword arguments to pass through to
plt.plot(). For more info please refer to documentation formaplotlib.pyplot.plot().
- Returns:
None.
- property solvetime: str#
Print a statement specifying how long IDASolver spent integrating.
- Returns:
solvetime (str) – An f-string with the total solver integration time in seconds.
- class thevenin.Experiment(**kwargs)[source]#
Experiment builder.
A class to define an experimental protocol. Use the
add_step()method sequential steps to run. Each step defines a control mode, a constant or time-dependent load profile, a time span, and optional limiting criteria to stop the step early if a specified event/state is detected. Note thatExperimentis designed to only interface with thethevenin.Simulationmodel wrapper.- Parameters:
**kwargs (dict, optional) – IDASolver keyword arguments that span all steps.
See also
IDASolverThe solver class, with documentation for most keyword arguments that you might want to adjust.
SimulationThe correct model interface to use with the
Experimentclass.
- add_step(mode, value, tspan, limits=None, **kwargs)[source]#
Add a step to the experiment.
- Parameters:
mode (str) – Control mode, {‘current_A’, ‘current_C’, ‘voltage_V’, ‘power_W’}.
value (float or Callable) – Value of boundary contion mode, in the appropriate units.
tspan (float or tuple[float, float] or 1D np.array) – Relative times for recording solution [s]. Providing a float will result in the solver picking time steps to save on its own. A tuple is interpreted as
(tmax, dt)where the first element is the max time for the step (in seconds) and the second is the time interval between steps (also seconds). You can also provide any custom array of times at which to save the solution by providing a 1Dnp.array; however, the first element must be zero and the array must be in a monotonically increasing order, and there must be at least three elements. An array likenp.array([0, tmax])will will result in the solver choosing its own time steps, similar to just providing a float. See notes for more information.limits (tuple[str, float], optional) – Stopping criteria for the new step, must be entered in sequential name/value pairs. Allowable names are {‘soc’, ‘temperature_K’, ‘current_A’, ‘current_C’, ‘voltage_V’, ‘power_W’, ‘capacity_Ah’, ‘time_s’, ‘time_min’, ‘time_h’}. Values for each limit should immediately follow a corresponding name and match its units. Time limits are in reference to total experiment time. The default is None.
**kwargs (dict, optional) – IDASolver keyword arguments specific to the new step only.
- Returns:
None.
- Raises:
ValueError – ‘mode’ is invalid.
ValueError – A ‘limits’ name is invalid.
TypeError – ‘tspan’ must be type float, tuple, or np.array.
ValueError – ‘tspan’ tuple must be length 2.
TypeError – ‘tspan’ tuple values must be type float.
ValueError – ‘tspan[1]’ must be less than ‘tspan[0]’ when given a tuple.
ValueError – ‘tspan’ array must be one-dimensional.
ValueError – ‘tspan[0]’ must be zero when given an array.
ValueError – ‘tspan’ arrays must be monotonically increasing.
See also
IDASolverThe solver class, with documentation for most keyword arguments that you might want to adjust.
Notes
For time-dependent loads, use a Callable for
valuewith a function signature likedef load(t: float) -> float, wheretis the step’s relative time, in seconds.When
tspanis given as a 2-tuple, like(tmax, dt), the time span is constructed as:tspan = np.arange(0., tspan[0], tspan[1])
In the case where
tmaxis not an integer multiple ofdt, a final time point is appended to ensure thattspan[-1] == tmax. If this is too restrictive, you can instead provide a custom 1Dnp.arrayfor thetspanargument. However, the array is checked to make sure the first element is zero and the array is monotonically increasing. If either of these checks fail, aValueErroris raised.
- property num_steps: int#
Return number of steps.
- Returns:
num_steps (int) – Number of steps.
- property steps: list[dict]#
Return steps list.
- Returns:
steps (list[dict]) – List of the step dictionaries.
- class thevenin.Prediction(params='params.yaml')[source]#
Prediction model wrapper.
This class is primarily intended for prediction-correction algorithms, e.g., Kalman filters. The interface is set up to let the user manage the internal state via the
TransientStateclass. In addition, the model is only designed to run current-based loads in a step-by-step fashion. If you are looking to simulate more complex protocols and use the resulting timeseries data you should use theSimulationclass instead.Note that this class and the
Simulationclass share the sameparamsinputs. This is for convenience so that users can easily switch between the two. However, the ‘soc0’ value has no real meaning for the prediction interface. Instead, the user manages the state of charge for each step through theTransientStateinterface. This means that you can effectively ignore the ‘soc0’ input when using this class.Models can be constructed using either a dictionary or a ‘.yaml’ file. Note that the number of Rj and Cj attributes must be consistent with the ‘num_RC_pairs’ value. See the notes for more information on the callable parameters.
- Parameters:
params (dict | str) –
Mapping of model parameter names to their values. Can be either a dict or absolute/relative file path to a yaml file (str). The keys/value pair descriptions are given below. The default uses an internal yaml file.
Key
Value
type, units
num_RC_pairs
number of RC pairs
int, -
soc0
initial state of charge
float, -
capacity
maximum battery capacity
float, Ah
ce
coulombic efficiency
float, -
gamma
hysteresis approach rate
float, -
mass
total battery mass
float, kg
isothermal
flag for isothermal model
bool, -
Cp
specific heat capacity
float, J/kg/K
T_inf
room/air temperature
float, K
h_therm
convective coefficient
float, W/m2/K
A_therm
heat loss area
float, m2
ocv
open circuit voltage
Callable, V
M_hyst
max hysteresis magnitude
Callable, V
R0
series resistance
Callable, Ohm
Rj
resistance in RCj
Callable, Ohm
Cj
capacity in RCj
Callable, F
- Raises:
TypeError – ‘params’ must be type dict or str.
ValueError – ‘params’ contains invalid and/or excess key/value pairs.
Notes
The ‘ocv’ and ‘M_hyst’ properties need to be callables with signatures like
f(soc: float) -> float, where ‘soc’ is the state of charge. All other properties that require callables (e.g., R0, Rj, and Cj) need signatures likef(soc: float, T_cell: float) -> float, with ‘T_cell’ being the cell temperature in K.Rj and Cj are not real property names. These are used generally in the documentation. If
num_RC_pairs=1then in addition to R0, you should define R1 and C1. Ifnum_RC_pairs=2then you should also give R2 and C2, etc. For the special case wherenum_RC_pairs=0, you should not provide any resistance or capacitance values besides the series resistance R0, which is always required.While most parameters can be dynamically updated, the
num_RC_pairsattribute is read-only. Consequently, you cannot add nor remove Rj and Cj attributes. However, modifying the values of Rj and Cj functions is allowed. If you need a circuit with a different number of RC pairs then you will need to create a separate instance.- pre()[source]#
Pre-process and prepare the model to make predictions. Specifically, this method builds pointers so it can manage mapping the state back and forth between the solver-required array format and the user-managed
TransientStateclass.- Returns:
None.
Notes
This method runs during the class initialization. It generally does not have to be run again unless you want to re-run internal checks on the class instance.
- set_options(**options)[source]#
Set the solver options for the underlying ODE integrator.
- Parameters:
**options (dict, optional) – CVODESolver keyword arguments that span all steps. You can re-run this method between prediction steps if you need different settings per step.
See also
CVODESolverThe solver class, with documentation for most keyword arguments that you might want to adjust.
- take_step(state, current, delta_t)[source]#
Take a step forward in time to predict the new state and voltage given a starting state, demand current, and time step.
- Parameters:
state (TransientState) – Description of the starting state.
current (float | Callable) – Demand current [A]. For a dynamic current, use a callable with a signature like
def current(t: float) -> float, where the input time is in seconds relative to the overall step.delta_t (float) – Magnitude of time step, in seconds.
- Returns:
TransientState– Predicted state at the end of the time step.
- to_simulation(state0=True)[source]#
Generate a
Simulationclass instance with the same properties as the currentPrediction.- Parameters:
state0 (bool | Solution | TransientState) – Control how the model state is initialized. If True (default), the state is set to a rested condition at ‘soc0’. If False, the state is left alone and only internal checks are run. Given a Solution instance, the state is set to the final state of the solution. See the notes for more information.
- Returns:
Simulation– An instance of the Simulation interface, initialized with the same properties as the current Prediction instance.
Notes
When initializing based on a Solution instance, the solution must be the same size as the current model. In other words, a 1RC-pair model cannot be initialized by a solution from a 2RC-pair circuit. Similarly, the eta_j size from a TransientState instance must match the size of RC pairs in the current model.
- property classname: str#
Return the name of the class.
- property num_RC_pairs: int#
Return the number of RC pairs.
- class thevenin.Simulation(params='params.yaml')[source]#
Simulation model wrapper.
This class is primarily intended for full timeseries simulations. Use the
Experimentclass to provide the details of an experiment. Note that this version of the model interface manages its own internal state. The user has limited ability to directly control the state. At the beginning of all simulations, the model is assumed in a fully rested state at the user-supplied state-of-chargesoc0. Through the pre-processor methodpre()you can manually force the state to start at a value given a previous solution, or using aTransientStateinstance for maximum control.Models can be constructed using either a dictionary or a ‘.yaml’ file. Note that the number of Rj and Cj attributes must be consistent with the ‘num_RC_pairs’ value. See the notes for more information on the callable parameters.
- Parameters:
params (dict | str) –
Mapping of model parameter names to their values. Can be either a dict or absolute/relative file path to a yaml file (str). The keys/value pair descriptions are given below. The default uses an internal yaml file.
Key
Value
type, units
num_RC_pairs
number of RC pairs
int, -
soc0
initial state of charge
float, -
capacity
maximum battery capacity
float, Ah
ce
coulombic efficiency
float, -
gamma
hysteresis approach rate
float, -
mass
total battery mass
float, kg
isothermal
flag for isothermal model
bool, -
Cp
specific heat capacity
float, J/kg/K
T_inf
room/air temperature
float, K
h_therm
convective coefficient
float, W/m2/K
A_therm
heat loss area
float, m2
ocv
open circuit voltage
Callable, V
M_hyst
max hysteresis magnitude
Callable, V
R0
series resistance
Callable, Ohm
Rj
resistance in RCj
Callable, Ohm
Cj
capacity in RCj
Callable, F
- Raises:
TypeError – ‘params’ must be type dict or str.
ValueError – ‘params’ contains invalid and/or excess key/value pairs.
Notes
The ‘ocv’ and ‘M_hyst’ properties need to be callables with signatures like
f(soc: float) -> float, where ‘soc’ is the state of charge. All other properties that require callables (e.g., R0, Rj, and Cj) need signatures likef(soc: float, T_cell: float) -> float, with ‘T_cell’ being the cell temperature in K.Rj and Cj are not real property names. These are used generally in the documentation. If
num_RC_pairs=1then in addition to R0, you should define R1 and C1. Ifnum_RC_pairs=2then you should also give R2 and C2, etc. For the special case wherenum_RC_pairs=0, you should not provide any resistance or capacitance values besides the series resistance R0, which is always required.While most parameters can be dynamically updated, the
num_RC_pairsattribute is read-only. Consequently, you cannot add nor remove Rj and Cj attributes. However, modifying the values of Rj and Cj functions is allowed. If you need a circuit with a different number of RC pairs then you will need to create a separate instance.- pre(state0=True)[source]#
Pre-process and prepare the model for running experiments.
This method builds solution pointers, registers algebraic variable indices, stores the mass matrix, and initializes the hidden state.
- Parameters:
state0 (bool | Solution | TransientState) – Control how the model state is initialized. If True (default), the state is set to a rested condition at ‘soc0’. If False, the state is left alone and only internal checks are run. Given a Solution instance, the state is set to the final state of the solution. See the notes for more information.
- Returns:
None.
Notes
This method runs during the class initialization. It generally does not have to be run again unless you want to reset the internal hidden state. Using the
state0argument, you can set the state back to a rested condition at ‘soc0’, to the final value from aSolution, or to a user-defined state given by aTransientStateinstance.When initializing based on a Solution instance, the solution must be the same size as the current model. In other words, a 1RC-pair model cannot be initialized by a solution from a 2RC-pair circuit. Similarly, the eta_j size from a TransientState instance must match the size of RC pairs in the current model.
- run(expr, reset_state=True, t_shift=0.001)[source]#
Run a full experiment.
- Parameters:
expr (Experiment) – An experiment instance.
reset_state (bool) – If True (default), the internal state of the model will be reset back to a rested condition at ‘soc0’ at the end of all steps. When False, the state does not reset. Instead it will update to match the final state of the last experimental step.
t_shift (float) – Time (in seconds) to shift step solutions by when stitching them together. If zero the end time of each step overlaps the starting time of its following step. The default is 1e-3.
- Returns:
CycleSolution– A stitched solution with all experimental steps.
Warning
The default behavior resets the model’s internal state back to a rested condition at ‘soc0’ by calling the
pre()method at the end of all steps. This means that if you run a second experiment afterward, it will not start where the previous one left off. Instead, it will start from the original rested condition that the model initialized with. You can bypass this by usingreset_state=False, which keeps the state at the end of the final experimental step.See also
ExperimentBuild an experiment.
CycleSolutionWrapper for an all-steps solution.
- run_step(expr, stepidx)[source]#
Run a single experimental step.
- Parameters:
expr (Experiment) – An experiment instance.
stepidx (int) – Step index to run. The first step has index 0.
- Returns:
StepSolution– Solution to the experimental step.
Warning
The model’s internal state is changed at the end of each experimental step. Consequently, you should not run steps out of order. You should always start with
stepidx = 0and then progress to the subsequent steps afterward. If at any time you want to reset the internal hidden state back to a rested condition, usepre().See also
ExperimentBuild an experiment.
StepSolutionWrapper for a single-step solution.
Notes
Using
run()loops through all steps in an experiment and stitches their solutions together. Most of the time, this is more convenient. However, running step-by-step provides maximum control to fine tune solver options. It also allows for complex analyses and/or control decisions to be performed between experimental steps.
- to_prediction()[source]#
Generate a
Predictionclass instance with the same properties as the currentSimulation.- Returns:
Prediction– An instance of the Prediction interface, initialized with the same properties as the current Simulation instance.
- property classname: str#
Return the name of the class.
- property num_RC_pairs: int#
Return the number of RC pairs.
- class thevenin.StepSolution(sim, ida_soln, timer)[source]#
Single-step solution.
A solution instance for a single experimental step.
- Parameters:
sim (Simulation) – The simulation instance that was run to produce the solution.
ida_soln (IDAResult) – The unformatted solution returned by IDASolver.
timer (float) – Amount of time it took for IDASolver to perform the integration.
- plot(x, y, **kwargs)#
Plot any two variables in ‘vars’ against each other.
- Parameters:
x (str) – A variable key in ‘vars’ to be used for the x-axis.
y (str) – A variable key in ‘vars’ to be used for the y-axis.
**kwargs (dict, optional) – Keyword arguments to pass through to
plt.plot(). For more info please refer to documentation formaplotlib.pyplot.plot().
- Returns:
None.
- property solvetime: str#
Print a statement specifying how long IDASolver spent integrating.
- Returns:
solvetime (str) – An f-string with the solver integration time in seconds.
- class thevenin.TransientState(soc, T_cell, hyst, eta_j)[source]#
Transient state for predictions.
A container that allows users to manage the internal hidden states of model classes. Users only have control over independent state variables (i.e., soc, T_cell, hyst, eta_j). The
Predictioninterface requires an instance of theTransientStateeach time a prediction step is made. You can optionally use instances of this class to define the starting state ofSimulationinstances using theirpre()method. In general, however, the model interface for simulations is more limited when it comes to user-defined states.The read-only
voltageproperty will return None if the state was user defined. If instead the state was returned by thePredictionclass, the value will be the predicted voltage after a given step.- Parameters:
soc (float) – State of charge [-].
T_cell (float) – Temperature of the cell [K].
hyst (float) – Hysteresis voltage [V].
eta_j (1D np.array | None) – RC pair overpotentials [V].
See also
SimulationThe model wrapper used for timeseries simulations.
PredictionThe model wrapper used for step-by-step predictions.
- property num_RC_pairs: int#
Number of RC pairs in the circuit.
- property voltage: float | None#
None if user-defined state. Otherwise, predicted voltage [V].
- thevenin.download_templates(path=None)[source]#
Copy example templates to into a local directory.
- Parameters:
path (str or PathLike or None, optional) – Path to parent directory where a new
thevenin_templatesfolder will be created and example templates will be copied to. If None (default), the current working directory is used.
- thevenin.list_templates()[source]#
List names of available example templates.
- Returns:
names (list[str]) – A list of example file names from an internal
resourcesfolder.
- thevenin.load_templates(*names)[source]#
Load example templates by name.
- Parameters:
*names (str) – One or more names of example template files to load. See options with
list_templates().- Returns:
templates (dict or tuple[dict]) – A single template dictionary if one name is provided, or a tuple of template dictionaries in the same order as the given
names.- Raises:
FileNotFoundError – Requested template name is not available.