1. Ramping#

Even with robust numerical solvers and thoughtfully chosen default tolerances, simulations may occasionally fail under certain conditions. This is often due to either inconsistent initial conditions or stiffness issues that prevent proper initialization from a rested state. While our solvers are designed to handle many scenarios effectively, the following issues may still arise:

  1. Inconsistent initial conditions:

    Many solvers are capable of detecting and resolving inconsistent initial conditions before taking the first step. However, this feature can be disabled, allowing bad initial conditions to be passed to the solver, and generally resulting in failures.

  2. Stiff problems:

    Some problems are inherently stiff and cannot be initialized effectively, even with a solver’s initialization correction schemes. In such cases, the solver may have difficulty determining a stable solution.

To address these issues, introducing a ramped load can stabilize the simulation. By default, Thevenin models are set to always ask the solver to correct the initial condition. The starting guess that gets passed to the solver is always a rested condition. Therefore, ramped loads can gradually adjust from the initial state to the desired load, making them easier for the solver to handle. This technique helps avoid the solver crashing due to an abrupt change in load.

In this tutorial, we will demonstrate how to use the loadfns module to create a ramped load profile. While we will focus one specific function, other useful helper functions are available in the loadfns module, and we encourage you to explore the full documentation for more information.

1.1. Creating a ramped load#

Thevenin supports both constant and dynamic load profiles for each experimental step. For example, below we make a profile that discharges the battery at a constant current until 3.5 V and then charges the battery by ramping the voltage until 4.2 V.

1import thevenin
2import matplotlib.pyplot as plt
3
4def voltage_ramp(t: float) -> float:
5    return 3.5 + 5e-3*t
6
7demand = thevenin.Experiment()
8demand.add_step('current_A', 75., (3600., 60.), limits=('voltage_V', 3.5))
9demand.add_step('voltage_V', voltage_ramp, (600., 10.), limits=('voltage_V', 4.2))

This general approach provides the most flexibility so users can write any constant or dynamic load, including interpolations of data. However, we also provide select loads in the loadfns module that help with both solver stability and reduce the amount of code users need to write out for simple profiles. For instance, the same experiment above can also be constructed using the Ramp class, as shown below.

1voltage_ramp = thevenin.loadfns.Ramp(5e-3, 3.5)
2
3demand = thevenin.Experiment()
4demand.add_step('current_A', 75., (3600., 60.), limits=('voltage_V', 3.5))
5demand.add_step('voltage_V', voltage_ramp, (600., 10.), limits=('voltage_V', 4.2))

Below, we demonstrate running this experimental protocol so we can see that it is doing what we expect.

1model = thevenin.Model()
2
3soln = model.run(demand)
4soln.plot('time_min', 'voltage_V')
[thevenin UserWarning]: Using the default parameter file 'params.yaml'.
../_images/bf30186108bc72343f3c33fecefe9277e36392192a96c309f071cfd5b3eb4d56.png

1.2. Stability ramps#

Aside from a constant ramp, like Ramp demonstrated above, ramps are also commonly used to quickly move from a rested state to a constant load. This can help with solver stability over trying to instantaneously pull a load. To build this type of provile, use the Ramp2Constant class. Below, we ramp up to a 20 C discharge in one millisecond and then hold the 20 C discharge rate until 3 V.

1dynamic_load = thevenin.loadfns.Ramp2Constant(20*75/1e-3, 20*75)
2
3demand = thevenin.Experiment()
4demand.add_step('current_A', dynamic_load, (180., 0.5), limits=('voltage_V', 3.))
5
6soln = model.run(demand)
7soln.plot('time_s', 'current_A')
8soln.plot('time_s', 'voltage_V')
C:\Users\crandall\Documents\THEVENIN\src\thevenin\loadfns\_ramps.py:97: RuntimeWarning: overflow encountered in exp
  sigmoid = 1. / (1. + np.exp(-self._sharpness*(linear - self._step)))
../_images/8db22403485fbe4480d0cc5e13fce4bc93180755628bfd2c02b35bb41cd17f4d.png ../_images/ace90a46b8d4203d119c06825097951c42852339047cb26f73d78b1637a42f43.png

These types of “stability” ramps become more and more helpful (or needed) as loads become more demanding. They can also depend on the model’s parameter set, i.e., for one set of parameters the model may start crashing at a 5 C discharge whereas another set is stable up to 50 C.

1.3. Comparing to instantaneous loads#

The default model parameters, and the Thevenin model in general, is typically fairly stable compared to other higher-fidelity models (e.g., the single particle model or pseudo-2D model). Therefore, here we can also demonstrate that when we run an instantaneous 20 C discharge profile that the results are not significantly impacted. See the figure below that compares the voltage profile above to one obtained without the ramped profile.

 1demand = thevenin.Experiment()
 2demand.add_step('current_A', 75*20, (180., 75), limits=('voltage_V', 3.))
 3
 4soln2 = model.run(demand)
 5
 6plt.plot(soln.vars['time_s'], soln.vars['voltage_V'], '-k')
 7plt.plot(soln2.vars['time_s'], soln2.vars['voltage_V'], 'ok', markerfacecolor='none')
 8    
 9plt.xlabel('Time [s]');
10plt.ylabel('Voltage [V]');
../_images/7bdc777046c1c114be028beeaf78b078b852f3f468d81e5de44497baa0098529.png

Due to the ramp the initial conditions are obviously a bit different. However, since the ramp occurs over just one millisecond, the profile from the ramped case (solid line) very quickly adjusts to the same voltage as the case where current is instantaneous (open markers). The solutions maintain good agreement throughout the rest of discharge.

1.4. Conclusion#

In this tutorial, you’ve seen how ramped loads can stabilize simulations that struggle with abrupt load changes. By using the loadfns module, you can easily implement these profiles, ensuring smoother transitions for the solver. For more advanced load functions, check out the full documentation to optimize your simulations further.