Note

This notebook is already available in your BattMo installation. In Matlab, run

open runSiliconGraphiteBattery

Simulation of a composite active material

This example shows how to simulate a composite active material

Setup the properties of the battery

We load the property of a composite silicon graphite electrode.

[1]:
jsonstruct_composite_material = parseBattmoJson('ParameterData/BatteryCellParameters/LithiumIonBatteryCell/composite_silicon_graphite.json');

In this structure, we have the material property of two active materials, ActiveMaterial1 and ActiveMaterial2.

[2]:
flattenJsonStruct(jsonstruct_composite_material);
[2]:
                                          parameter name                                                  parameter value
    __________________________________________________________________________________________    _______________________________
    {'NegativeElectrode.Coating.activeMaterialModelSetup.composite'                          }    {[                          1]}
    {'NegativeElectrode.Coating.ActiveMaterial1.specificHeatCapacity'                        }    {[                        632]}
    {'NegativeElectrode.Coating.ActiveMaterial1.heatCapacity'                                }    {[                     632000]}
    {'NegativeElectrode.Coating.ActiveMaterial1.thermalConductivity'                         }    {[                     1.0400]}
    {'NegativeElectrode.Coating.ActiveMaterial1.electronicConductivity'                      }    {[                        100]}
    {'NegativeElectrode.Coating.ActiveMaterial1.massFraction'                                }    {[                     0.9200]}
    {'NegativeElectrode.Coating.ActiveMaterial1.density'                                     }    {[                       2240]}
    {'NegativeElectrode.Coating.ActiveMaterial1.Interface.saturationConcentration'           }    {[                      30555]}
    {'NegativeElectrode.Coating.ActiveMaterial1.Interface.volumetricSurfaceArea'             }    {[                     723600]}
    {'NegativeElectrode.Coating.ActiveMaterial1.Interface.numberOfElectronsTransferred'      }    {[                          1]}
    {'NegativeElectrode.Coating.ActiveMaterial1.Interface.activationEnergyOfReaction'        }    {[                       5000]}
    {'NegativeElectrode.Coating.ActiveMaterial1.Interface.reactionRateConstant'              }    {[                 5.0310e-11]}
    {'NegativeElectrode.Coating.ActiveMaterial1.Interface.guestStoichiometry100'             }    {[                     0.8855]}
    {'NegativeElectrode.Coating.ActiveMaterial1.Interface.guestStoichiometry0'               }    {[                     0.1429]}
    {'NegativeElectrode.Coating.ActiveMaterial1.Interface.chargeTransferCoefficient'         }    {[                     0.5000]}
    {'NegativeElectrode.Coating.ActiveMaterial1.Interface.openCircuitPotential.type'         }    {'function'                   }
    {'NegativeElectrode.Coating.ActiveMaterial1.Interface.openCircuitPotential.functionname' }    {'computeOCP_Graphite_Torchio'}
    {'NegativeElectrode.Coating.ActiveMaterial1.Interface.openCircuitPotential.argumentlist' }    {3x1 cell                     }
    {'NegativeElectrode.Coating.ActiveMaterial1.diffusionModelType'                          }    {'full'                       }
    {'NegativeElectrode.Coating.ActiveMaterial1.SolidDiffusion.activationEnergyOfDiffusion'  }    {[                       5000]}
    {'NegativeElectrode.Coating.ActiveMaterial1.SolidDiffusion.referenceDiffusionCoefficient'}    {[                 3.9000e-14]}
    {'NegativeElectrode.Coating.ActiveMaterial1.SolidDiffusion.particleRadius'               }    {[                 1.0000e-06]}
    {'NegativeElectrode.Coating.ActiveMaterial1.SolidDiffusion.N'                            }    {[                         10]}
    {'NegativeElectrode.Coating.ActiveMaterial2.specificHeatCapacity'                        }    {[                        632]}
    {'NegativeElectrode.Coating.ActiveMaterial2.heatCapacity'                                }    {[                     632000]}
    {'NegativeElectrode.Coating.ActiveMaterial2.thermalConductivity'                         }    {[                     1.0400]}
    {'NegativeElectrode.Coating.ActiveMaterial2.electronicConductivity'                      }    {[                        100]}
    {'NegativeElectrode.Coating.ActiveMaterial2.massFraction'                                }    {[                     0.0800]}
    {'NegativeElectrode.Coating.ActiveMaterial2.density'                                     }    {[                       2330]}
    {'NegativeElectrode.Coating.ActiveMaterial2.Interface.saturationConcentration'           }    {[                 3.1131e+05]}
    {'NegativeElectrode.Coating.ActiveMaterial2.Interface.volumetricSurfaceArea'             }    {[                     723600]}
    {'NegativeElectrode.Coating.ActiveMaterial2.Interface.numberOfElectronsTransferred'      }    {[                          1]}
    {'NegativeElectrode.Coating.ActiveMaterial2.Interface.activationEnergyOfReaction'        }    {[                       5000]}
    {'NegativeElectrode.Coating.ActiveMaterial2.Interface.reactionRateConstant'              }    {[                 5.0310e-11]}
    {'NegativeElectrode.Coating.ActiveMaterial2.Interface.guestStoichiometry100'             }    {[                     0.8855]}
    {'NegativeElectrode.Coating.ActiveMaterial2.Interface.guestStoichiometry0'               }    {[                     0.1429]}
    {'NegativeElectrode.Coating.ActiveMaterial2.Interface.chargeTransferCoefficient'         }    {[                     0.5000]}
    {'NegativeElectrode.Coating.ActiveMaterial2.Interface.openCircuitPotential.type'         }    {'function'                   }
    {'NegativeElectrode.Coating.ActiveMaterial2.Interface.openCircuitPotential.functionname' }    {'computeOCP_Silicon_Li2012'  }
    {'NegativeElectrode.Coating.ActiveMaterial2.Interface.openCircuitPotential.argumentlist' }    {3x1 cell                     }
    {'NegativeElectrode.Coating.ActiveMaterial2.diffusionModelType'                          }    {'full'                       }
    {'NegativeElectrode.Coating.ActiveMaterial2.SolidDiffusion.activationEnergyOfDiffusion'  }    {[                       5000]}
    {'NegativeElectrode.Coating.ActiveMaterial2.SolidDiffusion.referenceDiffusionCoefficient'}    {[                 1.0000e-15]}
    {'NegativeElectrode.Coating.ActiveMaterial2.SolidDiffusion.particleRadius'               }    {[                 6.0000e-08]}
    {'NegativeElectrode.Coating.ActiveMaterial2.SolidDiffusion.N'                            }    {[                         10]}

For the remaining properties, we load a standard data set

[3]:
jsonstruct_cell = parseBattmoJson('ParameterData/BatteryCellParameters/LithiumIonBatteryCell/lithium_ion_battery_nmc_graphite.json');

We remove from this structure active material field. This step is not necessary but is cleaner and we avoid a warning.

[4]:
jsonstruct_cell = removeJsonStructField(jsonstruct_cell, {'NegativeElectrode', 'Coating', 'ActiveMaterial'});

we load a 1d geometry

[5]:
jsonfilename = fullfile('Examples', 'JsonDataFiles', 'geometry1d.json');
jsonstruct_geometry = parseBattmoJson(jsonfilename);

We merge the json structures

[6]:
jsonstruct = mergeJsonStructs({jsonstruct_composite_material, ...
                               jsonstruct_cell              , ...
                               jsonstruct_geometry});

We do not consider the thermal model and remove the current collector. We also use a CV switch control.

[7]:
jsonstruct.use_thermal                = false;
jsonstruct.include_current_collectors = false;

We define some shorcuts for the sub-models, for convenience

[8]:
ne   = 'NegativeElectrode';
pe   = 'PositiveElectrode';
co   = 'Coating';
am1  = 'ActiveMaterial1';
am2  = 'ActiveMaterial2';
bd   = 'Binder';
ad   = 'ConductingAdditive';
sd   = 'SolidDiffusion';
itf  = 'Interface';
ctrl = 'Control';

We modify some parameters

We adjust the mass fractions parameters of the active material in the negative electrode

[9]:
jsonstruct = setJsonStructField(jsonstruct, {ne, co, am1, 'massFraction'}, 0.9, 'handleMisMatch', 'quiet');
jsonstruct = setJsonStructField(jsonstruct, {ne, co, am2, 'massFraction'}, 0.08, 'handleMisMatch', 'quiet');
jsonstruct = setJsonStructField(jsonstruct, {ne, co, bd , 'massFraction'}, 0.01, 'handleMisMatch', 'quiet');
jsonstruct = setJsonStructField(jsonstruct, {ne, co, ad , 'massFraction'}, 0.01, 'handleMisMatch', 'quiet');

We run the simulations

[10]:
output = runBatteryJson(jsonstruct);

Plotting

We extract the voltage, current and time from the simulation output

[11]:
states = output.states;
model  = output.model;

E    = cellfun(@(x) x.Control.E, states);
I    = cellfun(@(x) x.Control.I, states);
time = cellfun(@(x) x.time, states);

We plot the voltage and current

[12]:
figure
subplot(2, 1, 1);
plot(time/hour, E);
xlabel('Time / h');
ylabel('Voltage / V');
title('Voltage')
subplot(2, 1, 2);
plot(time/hour, I/milli);
xlabel('Time / h');
ylabel('Current / mA');
title('Current')
[12]:
figure_0.png

We compute and plot the state of charges in the different material

[13]:
figure
hold on

for istate = 1 : numel(states)
    states{istate} = model.evalVarName(states{istate}, {ne, co, 'SOC'});
end

SOC  = cellfun(@(x) x.(ne).(co).SOC, states);
SOC1 = cellfun(@(x) x.(ne).(co).(am1).SOC, states);
SOC2 = cellfun(@(x) x.(ne).(co).(am2).SOC, states);

plot(time/hour, SOC, 'displayname', 'SOC - cumulated');
plot(time/hour, SOC1, 'displayname', 'SOC - Graphite');
plot(time/hour, SOC2, 'displayname', 'SOC - Silicon');

xlabel('Time / h');
ylabel('SOC / -');
title('SOCs')

legend show
[13]:
figure_1.png

plot of the particle concentration distribution in the particle at the end time

We recover the state at the last time step

[14]:
state = states{end};

We iterate over the two active materials. The first one is the graphite and the second one the silicon

[15]:
ams = {am1, am2};

for iam = 1 : numel(ams)

    am = ams{iam};

    model_sd = model.(ne).(co).(am).(sd);
    state_sd = state.(ne).(co).(am).(sd);

We recover the concentration as an array. The column index corresponds to the spatial direction (here x as we are considering a 1D model) and the row index corresponds to the particle radia direction

[16]:
    c = model_sd.getParticleConcentrations(state_sd);
    r = model_sd.operators.radii;

    figure
    hold on
[16]:
figure_2.png
figure_3.png

We plot the concentration distribution at the last point in the grid, which corresponds in this case to the closest to the positive electrode

[17]:
    plot(r/(micro*meter), c(size(c, 1), :)/(mol/litre));
    title(sprintf('Particle concentration profile in %s', am));
    xlabel('radius / m');
    ylabel('concentration / mol/litre');

end

Charge step

[18]:
initstate = states{end};
jsonstruct.(ctrl).CRate = 1;
jsonstruct = setJsonStructField(jsonstruct, {'Control', 'controlPolicy'}, 'CCCharge', 'handleMisMatch', 'quiet');

jsonstruct.initializationSetup = 'given matlab object';

output = runBatteryJson(jsonstruct, 'initstate', initstate);

Visualisation

We concatenate the states we have computed

[19]:
dischargeStates = states; % see previous assignement
chargeStates    = output.states; % assigned from last simulation output

allStates = vertcat(dischargeStates, chargeStates);
[20]:
% We extract the voltage, current and time from the simulation output
E    = cellfun(@(x) x.Control.E, allStates);
I    = cellfun(@(x) x.Control.I, allStates);
time = cellfun(@(x) x.time, allStates);
We plot the voltage and current
[21]:
figure
subplot(2, 1, 1);
plot(time/hour, E);
xlabel('Time / h');
ylabel('Voltage / V');
title('Voltage')
subplot(2, 1, 2);
plot(time/hour, I/milli);
xlabel('Time / h');
ylabel('Current / mA');
title('Current')
[21]:
figure_4.png

We compute and plot the state of charges in the different material

[22]:
figure
hold on

for istate = 1 : numel(allStates)
    allStates{istate} = model.evalVarName(allStates{istate}, {ne, co, 'SOC'});
end

SOC  = cellfun(@(x) x.(ne).(co).SOC, allStates);
SOC1 = cellfun(@(x) x.(ne).(co).(am1).SOC, allStates);
SOC2 = cellfun(@(x) x.(ne).(co).(am2).SOC, allStates);

plot(time/hour, SOC, 'displayname', 'SOC - cumulated');
plot(time/hour, SOC1, 'displayname', 'SOC - Graphite');
plot(time/hour, SOC2, 'displayname', 'SOC - Silicon');

xlabel('Time / h');
ylabel('SOC / -');
title('SOCs')

legend show
[22]:
figure_5.png