Dynamic analysis III
This notebook demonstrates how to use custom experiments with the Breathe Design API. Custom experiments allow you to define complex cycling protocols using either text-based commands or control arrays for precise current or power control.
You can use custom experiments in several ways:
- Text-based commands: Simple string commands for basic cycling
- List-based commands: More structured approach using lists
- Current control arrays: Define precise current vs. time profiles
- Power control arrays: Define precise power vs. time profiles
- Mixed approaches: Combine arrays with text commands
from breathe_design import api_interface as api
from breathe_design import enable_notebook_plotly
enable_notebook_plotly()
# Set up common simulation parameters
base_battery = "Molicel P45B"
designs = []
initialSoC = 0.5
initialTemperature_degC = 25.0
ambientTemperature_degC = 25.0
heatTransferCoefficient = 20.0
Method 1: Text-based Custom Experiments¶
The simplest way to define a custom experiment is using a text string with newline-separated commands. Each line represents a step in the experiment.
# Example 1: Simple text-based experiment
cycler_text = {
"cycle_type": "CUSTOM",
"control_parameters": {
"experiment_text": "Charge at 4.5 A until 4.2 V\nRest for 60 s\nDischarge at 4.5 A until 2.5 V",
"period": "1s",
},
}
result1 = api.run_sim(
base_battery=base_battery,
cycler=cycler_text,
designs=designs,
initialSoC=initialSoC,
initialTemperature_degC=initialTemperature_degC,
ambientTemperature_degC=ambientTemperature_degC,
heatTransferCoefficient=heatTransferCoefficient,
)
result1.plot_voltage_response()
Method 2: List-based Custom Experiments¶
You can also use a list of strings instead of a newline-separated string. This makes it easier to programmatically build complex experiments.
# Example 2: List-based experiment (easier to programmatically construct)
cycler_list = {
"cycle_type": "CUSTOM",
"control_parameters": {
"experiment_text": [
"Charge at 4.5 A until 4.2 V",
"Rest for 60 s",
"Discharge at 4.5 A until 2.5 V",
"Rest for 60 s",
"Charge at 4.5 A until 4.2 V",
"Rest for 60 s",
"Discharge at 4.5 A until 2.5 V",
],
"period": "1s",
},
}
result2 = api.run_sim(
base_battery=base_battery,
cycler=cycler_list,
designs=designs,
initialSoC=initialSoC,
initialTemperature_degC=initialTemperature_degC,
ambientTemperature_degC=ambientTemperature_degC,
heatTransferCoefficient=heatTransferCoefficient,
)
result2.plot_dynamic_response(["Voltage [V]", "Current [A]"])
Method 3: Current Control Arrays¶
For precise current control, you can define time-current profiles using control arrays. This is useful for replicating drive cycles, test protocols, or any arbitrary current profile.
# Example 3: Current control array
# Define a drive cycle-like profile with time and current arrays
time_points = list(range(0, 120, 2)) # 0 to 118 seconds, 2-second intervals
current_points = []
for t in time_points:
if t < 20:
# Initial rest
current_points.append(0.0)
elif t < 40:
# Discharge at 2A
current_points.append(2.0)
elif t < 50:
# Rest
current_points.append(0.0)
elif t < 70:
# Charge (regenerative braking) at -1.5A
current_points.append(-1.5)
elif t < 80:
# Rest
current_points.append(0.0)
elif t < 100:
# High discharge at 3A
current_points.append(3.0)
else:
# Final rest
current_points.append(0.0)
cycler_current_array = {
"cycle_type": "CUSTOM",
"control_parameters": {
"control_arrays": {
"DriveCycle": {
"time": time_points,
"value": current_points, # Use "current" key for current control
}
},
"experiment_text": [
{
"type": "current",
"array": "DriveCycle",
} # Reference the array with type "current"
],
"period": "1s",
},
}
result3 = api.run_sim(
base_battery=base_battery,
cycler=cycler_current_array,
designs=designs,
initialSoC=initialSoC,
initialTemperature_degC=initialTemperature_degC,
ambientTemperature_degC=ambientTemperature_degC,
heatTransferCoefficient=heatTransferCoefficient,
)
result3.plot_dynamic_response(["Voltage [V]", "Current [A]"])
Method 4: Power Control Arrays¶
Power control is useful for applications where you want to maintain constant power (e.g., grid storage, power tools). Use the "power" key in control arrays and reference it with {"type": "power", "array": "array_name"}.
Note: Positive power = discharge, negative power = charge
# Example 4: Power control array
# Define a power profile (in Watts)
time_points_power = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
power_points = [
0.0, # Rest at 0s
10.0, # Discharge at 10W from 10s
0.0, # Rest at 20s
-5.0, # Charge (regenerative braking) at -5W from 30s
0.0, # Rest at 40s
15.0, # High discharge at 15W from 50s
0.0, # Rest at 60s
-8.0, # Charge at -8W from 70s
0.0, # Rest at 80s
12.0, # Discharge at 12W from 90s
0.0, # Final rest at 100s
]
cycler_power_array = {
"cycle_type": "CUSTOM",
"control_parameters": {
"control_arrays": {
"PowerProfile": {
"time": time_points_power,
"value": power_points, # Use "power" key for power control
}
},
"experiment_text": [
{"type": "power", "array": "PowerProfile"} # Reference with type "power"
],
"period": "1s",
},
}
result4 = api.run_sim(
base_battery=base_battery,
cycler=cycler_power_array,
designs=designs,
initialSoC=initialSoC,
initialTemperature_degC=initialTemperature_degC,
ambientTemperature_degC=ambientTemperature_degC,
heatTransferCoefficient=heatTransferCoefficient,
)
result4.plot_dynamic_response(["Voltage [V]", "Current [A]"])
Method 5: Mixing Arrays with Text Commands¶
You can combine control arrays with text-based commands in the same experiment. This allows you to use arrays for complex profiles and text commands for simple steps.
# Example 5: Mixing arrays with text commands
cycler_mixed = {
"cycle_type": "CUSTOM",
"control_parameters": {
"control_arrays": {
"DriveCycle": {"time": [0, 10, 20, 30], "value": [0.0, 2.0, 0.0, -1.5]}
},
"experiment_text": [
"Rest for 10 s", # Text command
{"type": "current", "array": "DriveCycle"}, # Array reference
"Rest for 30 s", # Another text command
"Charge at 3.0 A until 4.2 V", # More text commands
"Rest for 60 s",
],
"period": "1s",
},
}
result5 = api.run_sim(
base_battery=base_battery,
cycler=cycler_mixed,
designs=designs,
initialSoC=initialSoC,
initialTemperature_degC=initialTemperature_degC,
ambientTemperature_degC=ambientTemperature_degC,
heatTransferCoefficient=heatTransferCoefficient,
)
result5.plot_dynamic_response(["Voltage [V]", "Current [A]"])