Skip to content

Compute Cell KPIs

BattMo implements several utilities to compute cell metrics, derived both from the cell parameter set and from simulation outputs. These metrics cover most of the points required by battery checklists published by reputable journals, namely the Cell Press checklist and the ACS Energy Letter's checklist.

Load parameter set and run simulation

julia
using BattMo, GLMakie


cell_parameters = load_cell_parameters(; from_default_set = "Chen2020")
cycling_protocol = load_cycling_protocol(; from_default_set = "CCDischarge")

model_setup = LithiumIonBattery()

sim = Simulation(model_setup, cell_parameters, cycling_protocol);

output = solve(sim)
✔️ Validation of ModelSettings passed: No issues found.
──────────────────────────────────────────────────
✔️ Validation of CellParameters passed: No issues found.
──────────────────────────────────────────────────
✔️ Validation of CyclingProtocol passed: No issues found.
──────────────────────────────────────────────────
✔️ Validation of SimulationSettings passed: No issues found.
──────────────────────────────────────────────────
Jutul: Simulating 2 hours, 12 minutes as 163 report steps
╭────────────────┬───────────┬───────────────┬──────────╮
 Iteration type   Avg/step   Avg/ministep     Total 
 146 steps  146 ministeps  (wasted) 
├────────────────┼───────────┼───────────────┼──────────┤
 Newton         │   2.32877 │       2.32877 │  340 (0) │
 Linearization  │   3.32877 │       3.32877 │  486 (0) │
 Linear solver  │   2.32877 │       2.32877 │  340 (0) │
 Precond apply  │       0.0 │           0.0 │    0 (0) │
╰────────────────┴───────────┴───────────────┴──────────╯
╭───────────────┬────────┬────────────┬──────────╮
 Timing type      Each    Relative     Total 
     ms  Percentage        ms 
├───────────────┼────────┼────────────┼──────────┤
 Properties    │ 0.0359 │     2.44 % │  12.1987 │
 Equations     │ 0.5811 │    56.51 % │ 282.4383 │
 Assembly      │ 0.1771 │    17.23 % │  86.0913 │
 Linear solve  │ 0.1652 │    11.24 % │  56.1539 │
 Linear setup  │ 0.0000 │     0.00 % │   0.0000 │
 Precond apply │ 0.0000 │     0.00 % │   0.0000 │
 Update        │ 0.0479 │     3.26 % │  16.2918 │
 Convergence   │ 0.0666 │     6.48 % │  32.3675 │
 Input/Output  │ 0.0223 │     0.65 % │   3.2604 │
 Other         │ 0.0323 │     2.20 % │  10.9764 │
├───────────────┼────────┼────────────┼──────────┤
 Total         │ 1.4699 │   100.00 % │ 499.7783 │
╰───────────────┴────────┴────────────┴──────────╯

Cell KPIs from the parameter set

Some KPIs are directly computable from the cell parameter set. Here below we list the main KPIs we can compute with BattMo. For illustration, we create a Dictionary storing the values of the computations.

julia
cell_kpis_from_set = Dict(
	"Positive Electrode Coating Mass" => compute_electrode_coating_mass(cell_parameters, "PositiveElectrode"),
	"Negative Electrode Coating Mass" => compute_electrode_coating_mass(cell_parameters, "NegativeElectrode"),
	"Separator Mass" => compute_separator_mass(cell_parameters),
	"Positive Electrode Current Collector Mass" => compute_current_collector_mass(cell_parameters, "PositiveElectrode"),
	"Negative Electrode Current Collector Mass" => compute_current_collector_mass(cell_parameters, "NegativeElectrode"),
	"Electrolyte Mass" => compute_electrolyte_mass(cell_parameters),
	"Cell Mass" => compute_cell_mass(cell_parameters),
	"Cell Volume" => compute_cell_volume(cell_parameters),
	"Positive Electrode Mass Loading" => compute_electrode_mass_loading(cell_parameters, "PositiveElectrode"),
	"Negative Electrode Mass Loading" => compute_electrode_mass_loading(cell_parameters, "NegativeElectrode"),
	"Cell Theoretical Capacity" => compute_cell_theoretical_capacity(cell_parameters),
	"Cell N:P Ratio" => compute_np_ratio(cell_parameters),
)
Dict{String, Float64} with 12 entries:
  "Positive Electrode Coating Mass"           => 0.0255595
  "Negative Electrode Current Collector Mass" => 0.0107662
  "Cell Theoretical Capacity"                 => 5.0912
  "Cell Volume"                               => 9.00538e-5
  "Positive Electrode Current Collector Mass" => 0.00451983
  "Negative Electrode Mass Loading"           => 0.144414
  "Separator Mass"                            => 0.000617901
  "Negative Electrode Coating Mass"           => 0.0148313
  "Electrolyte Mass"                          => 0.00644079
  "Cell Mass"                                 => 0.0627356
  "Cell N:P Ratio"                            => 0.913058
  "Positive Electrode Mass Loading"           => 0.248875

The functions to compute the cell mass and cell volume also offer an option to print the breakdown of masses without returning the total mass. The breakdown can be useful to verify the parameters are sensible, and to calculate a Bill of Materials (BOM)

julia
compute_cell_mass(cell_parameters; print_breakdown = true);
		Component                 | Mass/kg |  Percentage
-------------------------------------------------------------
Cell                                 | 0.06274 |    100
Positive Electrode                   | 0.02556 |    40.7
Negative Electrode                   | 0.01483 |    23.6
Positive Electrode Current Collector | 0.00452 |    7.2
Negative Electrode Current Collector | 0.01077 |    17.2
Electrolyte                          | 0.00644 |    10.3
Separator                            | 0.00062 |    1.0

Cell KPIs from simulation output

Once we run a simulation we can access additional cell KPIs such as energy density, specific energy, mean power output, etc.

julia
cell_kpis_from_output = Dict(
	"Discharge capacity" => compute_discharge_capacity(output),
	"Discharge energy" => compute_discharge_energy(output),
	"Energy density" => compute_discharge_energy(output) / compute_cell_volume(cell_parameters),
	"Specific energy" => compute_discharge_energy(output) / compute_cell_mass(cell_parameters),
)
Dict{String, Float64} with 4 entries:
  "Discharge capacity" => 4.98095
  "Specific energy"    => 1.02328e6
  "Discharge energy"   => 64195.9
  "Energy density"     => 7.12862e8

Example full cycle

When we run a protocol with a full or multiple cycles we can retrieve some extra KPIs from the output. Let's run a CCCV protocol.

julia
cycling_protocol = load_cycling_protocol(; from_default_set = "CCCV")
{
    "Protocol" => "CCCV"
    "UpperVoltageLimit" => 4.0
    "InitialControl" => "charging"
    "DRate" => 1.0
    "TotalNumberOfCycles" => 3
    "CRate" => 1.0
    "InitialStateOfCharge" => 0
    "CurrentChangeLimit" => 0.0001
    "VoltageChangeLimit" => 0.0001
    "InitialTemperature" => 298.15
    "Metadata" =>     {
        "Description" => "Parameter set for a constant current constant voltage cyling protocol."
        "Title" => "CCCV"
    }
    "LowerVoltageLimit" => 3.0
}

This protocol will run 3 cycles

julia
sim = Simulation(model_setup, cell_parameters, cycling_protocol)

output = solve(sim)
✔️ Validation of CellParameters passed: No issues found.
──────────────────────────────────────────────────
✔️ Validation of CyclingProtocol passed: No issues found.
──────────────────────────────────────────────────
✔️ Validation of SimulationSettings passed: No issues found.
──────────────────────────────────────────────────
Jutul: Simulating 15 hours as 1080 report steps
╭────────────────┬───────────┬───────────────┬────────────╮
 Iteration type   Avg/step   Avg/ministep       Total 
 787 steps  831 ministeps    (wasted) 
├────────────────┼───────────┼───────────────┼────────────┤
 Newton         │   2.91233 │       2.75812 │ 2292 (600) │
 Linearization  │   3.96823 │       3.75812 │ 3123 (630) │
 Linear solver  │   2.91233 │       2.75812 │ 2292 (600) │
 Precond apply  │       0.0 │           0.0 │      0 (0) │
╰────────────────┴───────────┴───────────────┴────────────╯
╭───────────────┬────────┬────────────┬────────╮
 Timing type      Each    Relative   Total 
     ms  Percentage       s 
├───────────────┼────────┼────────────┼────────┤
 Properties    │ 0.0656 │     3.19 % │ 0.1503 │
 Equations     │ 0.6132 │    40.65 % │ 1.9149 │
 Assembly      │ 0.2625 │    17.40 % │ 0.8197 │
 Linear solve  │ 0.2024 │     9.85 % │ 0.4639 │
 Linear setup  │ 0.0000 │     0.00 % │ 0.0000 │
 Precond apply │ 0.0000 │     0.00 % │ 0.0000 │
 Update        │ 0.0962 │     4.68 % │ 0.2205 │
 Convergence   │ 0.0674 │     4.47 % │ 0.2104 │
 Input/Output  │ 0.0231 │     0.41 % │ 0.0192 │
 Other         │ 0.3977 │    19.35 % │ 0.9116 │
├───────────────┼────────┼────────────┼────────┤
 Total         │ 2.0552 │   100.00 % │ 4.7104 │
╰───────────────┴────────┴────────────┴────────╯

As our data represents multiple cycles now, we can choose for which cycle we'd like to compute the KPI.

julia
cell_kpis_from_output_cycle_0 = Dict(
	"Discharge capacity" => compute_discharge_capacity(output; cycle_number = 0),
	"Discharge energy" => compute_discharge_energy(output; cycle_number = 0),
	"Energy density" => compute_discharge_energy(output; cycle_number = 0) / compute_cell_volume(cell_parameters),
	"Specific energy" => compute_discharge_energy(output; cycle_number = 0) / compute_cell_mass(cell_parameters),
	"Charge capacity" => compute_charge_capacity(output; cycle_number = 0),
	"Charge energy" => compute_charge_energy(output; cycle_number = 0),
	"Round trip efficiency" => compute_round_trip_efficiency(output; cycle_number = 0),
)
Dict{String, Float64} with 7 entries:
  "Discharge capacity"    => 3.67642
  "Specific energy"       => 7.23548e5
  "Charge capacity"       => -4.29348
  "Charge energy"         => 59700.1
  "Round trip efficiency" => 77.5289
  "Discharge energy"      => 45392.2
  "Energy density"        => 5.04057e8

Or from the second cycle:

julia
cell_kpis_from_output_cycle_1 = Dict(
	"Discharge capacity" => compute_discharge_capacity(output; cycle_number = 1),
	"Discharge energy" => compute_discharge_energy(output; cycle_number = 1),
	"Energy density" => compute_discharge_energy(output; cycle_number = 1) / compute_cell_volume(cell_parameters),
	"Specific energy" => compute_discharge_energy(output; cycle_number = 1) / compute_cell_mass(cell_parameters),
	"Charge capacity" => compute_charge_capacity(output; cycle_number = 1),
	"Charge energy" => compute_charge_energy(output; cycle_number = 1),
	"Round trip efficiency" => compute_round_trip_efficiency(output; cycle_number = 1),
)
Dict{String, Float64} with 7 entries:
  "Discharge capacity"    => 3.67642
  "Specific energy"       => 7.2355e5
  "Charge capacity"       => -3.68803
  "Charge energy"         => 52124.7
  "Round trip efficiency" => 88.7963
  "Discharge energy"      => 45392.3
  "Energy density"        => 5.04058e8

Example on GitHub

If you would like to run this example yourself, it can be downloaded from the BattMo.jl GitHub repository as a script.


This page was generated using Literate.jl.