Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
2be5b10
started with ECM
xavierr Apr 17, 2026
07ab3f2
update in ECM
xavierr Apr 17, 2026
1fd7238
update in ecm
xavierr Apr 17, 2026
ca45a81
update in ecm
xavierr Apr 17, 2026
c058e24
added ecm notebook in doc
xavierr Apr 17, 2026
0d2c22d
little bug changes
alexandrefichter Apr 20, 2026
c279864
fixed capacity definition
alexandrefichter Apr 20, 2026
be32715
Translation
alexandrefichter Apr 20, 2026
7f99366
changed parameters in vectors
alexandrefichter Apr 20, 2026
c06f07a
new funtion create_parameters instead of json file to allow calculs
alexandrefichter Apr 20, 2026
b69906b
push of EIS
alexandrefichter Apr 29, 2026
77864fe
problem with omega loading fixed
alexandrefichter Apr 29, 2026
5bff6e5
fitting with unitboxBFGS, limit: C1<10*C2
alexandrefichter May 4, 2026
5369350
details
alexandrefichter May 5, 2026
b78424b
added iterations
alexandrefichter May 5, 2026
67075ac
iterations and constraints
alexandrefichter May 5, 2026
6b3d582
classe fitting_eis
alexandrefichter May 6, 2026
7595336
classe lmoins incohérente?
alexandrefichter May 6, 2026
2eca284
modif
alexandrefichter May 6, 2026
d31ee97
commentaire
alexandrefichter May 6, 2026
0a1441d
2
alexandrefichter May 6, 2026
d4a46f9
run file added
alexandrefichter May 6, 2026
037c138
renamed file
xavierr May 6, 2026
c8edd2d
some updates
xavierr May 6, 2026
f226093
added filename input in load_experimental_data
xavierr May 6, 2026
68ae254
some changes runclassfitting
xavierr May 6, 2026
96f329a
some clean-up in FittingEIS
xavierr May 6, 2026
5318a92
class efficient
alexandrefichter May 6, 2026
dff9f8e
load more flexible
alexandrefichter May 7, 2026
2de7702
runImpedance working
alexandrefichter May 11, 2026
ff7c9a7
Comparison with Ank's experimental data
alexandrefichter May 12, 2026
d3c3de3
first try to fit an ecm circuit with chen data
alexandrefichter May 13, 2026
81ff691
try with lsqnonlin, it works
alexandrefichter May 13, 2026
e8d3e28
nonworking try with unitbox
alexandrefichter May 13, 2026
d449384
error with chen data lowered
alexandrefichter May 19, 2026
aed07b6
beginning of change with scales, all programs _impact
alexandrefichter May 29, 2026
70a5efe
new boxing working for thevenin model
alexandrefichter Jun 1, 2026
64e0958
scaling changed to pmin and pmax, try to normalize the error (doesn't…
alexandrefichter Jun 2, 2026
daa45b8
fitting robuste pour thevenin
alexandrefichter Jun 3, 2026
e9e4a96
fix in doubleLayerCapacitance
xavierr Jun 3, 2026
86de364
little changes
alexandrefichter Jun 4, 2026
f142a8f
Merge branch 'ecm' of https://github.com/BattMoTeam/BattMo-alexandre-…
alexandrefichter Jun 4, 2026
19b7a00
Recet changes and notebook added
alexandrefichter Jun 16, 2026
73960d2
codes added to notebook
alexandrefichter Jun 16, 2026
a7e1d76
Code added to Notebook
alexandrefichter Jun 16, 2026
dd6936c
images added
alexandrefichter Jun 16, 2026
02fe833
Slider interesting cases and comments
alexandrefichter Jun 16, 2026
650b2eb
moved notebook
xavierr Jun 22, 2026
bee98be
changed notebook name
xavierr Jun 22, 2026
0d9e758
update
xavierr Jun 22, 2026
b36f505
update
xavierr Jun 22, 2026
11bde03
update
xavierr Jun 22, 2026
a6f79bb
update
xavierr Jun 22, 2026
bdf13c0
Merge branch 'main' into ecm
xavierr Jun 22, 2026
c233414
fix in load_chen_data
xavierr Jun 22, 2026
3a97d0c
added ECM tests in testsuite
xavierr Jun 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
587 changes: 587 additions & 0 deletions Battery/EquivalentCircuitModel/calibration/FittingEIS.m

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@



load_experimental_data();

% --- 5. Tracé du diagramme de Nyquist ---
figure('Name', 'EIS', 'Color', 'w');

% On trace -Im(Z) en fonction de Re(Z)
plot(Z_real, Z_imag, 'o', ...
'LineWidth', 1.5, ...
'MarkerSize', 2, ...
'MarkerFaceColor', [0 0.4470 0.7410], ...
'MarkerEdgeColor', 'k');
grid on;
axis equal; % Force la même échelle en X et Y

% Ajout des labels
xlabel('Z_{re} ', 'FontSize', 12, 'FontWeight', 'bold');
ylabel('-Z_{im}', 'FontSize', 12, 'FontWeight', 'bold');
title('Nyquist diagram from data', 'FontSize', 14);

% Amélioration des axes
set(gca, 'FontSize', 11, 'LineWidth', 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

% params = [6.2261e-3, 0.3e-3, 10000.1264, 0.00097, 2e5]; % best params
% handmade

params = [0.003, 0.00134, 5, 0.00330, 20000];
[Z_re_exp, Z_im_exp, omega] = load_experimental_data();
omega = logspace(-2, 4, 100);

[Z_real, Z_imag] = load_nyquist(params, omega)

figure;
plot(Z_re_exp, Z_im_exp, 'ro', 'MarkerFaceColor', 'r');
hold on;
plot(Z_real, Z_imag, '-o', 'LineWidth', 1.5, 'MarkerFaceColor', 'b');
axis equal; % Essentiel pour Nyquist
grid on;
xlabel('Z_{réel} (\Omega)', 'FontWeight', 'bold');
ylabel('-Z_{imaginaire} (\Omega)', 'FontWeight', 'bold');
title('Nyquist diagram simulated');







Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@

mrstModule add ad-core mrst-gui mpfa agmg linearsolvers

jsonstruct_material = parseBattmoJson(fullfile('ParameterData','ParameterSets','Chen2020','chen2020_lithium_ion_battery.json'));
jsonstruct_geometry = parseBattmoJson(fullfile('Examples', 'JsonDataFiles', 'geometryChen.json'));

jsonstruct = mergeJsonStructs({jsonstruct_material, ...
jsonstruct_geometry});



includeDoubleLayer = false;

if includeDoubleLayer

jsonstruct.(ne).(co).(am).(itf).useDoubleLayerCapacity = true;
jsonstruct.(ne).(co).(am).(itf).doubleLayerCapacitance = 0.2;

end


[model, inputparams, ~] = setupModelFromJson(jsonstruct);

c_ne = 29.866*mol/litre; % initial concentration at negative electrode
c_pe = 17.038*mol/litre; % initial concentration at positive electrode

initstate = initStateChen2020(model, c_ne, c_pe);

b = linspace(1e4, 5e4, 20); %change here

frequences = logspace(-2, 4, 30);
figure;
hold on;
for i = 1:length(b)

inputparams.NegativeElectrode.Coating.ActiveMaterial.Interface.activationEnergyOfReaction = b(i); %change here
inputparams.PositiveElectrode.Coating.ActiveMaterial.Interface.activationEnergyOfReaction = b(i)/2; %change here

impsolv = ImpedanceSolver(inputparams, 'initstate', initstate, 'computeSteadyState', false);

Z = impsolv.computeImpedance(frequences);
Z_re = real(Z);
Z_im = imag(Z);
curve = sprintf('activation Energy Of Reaction = %.2e', b(i)); %change here
plot(Z_re, -Z_im, 'DisplayName', curve);
end

legend('show');
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

mrstModule add ad-core mrst-gui mpfa agmg linearsolvers

jsonstruct_material = parseBattmoJson(fullfile('ParameterData','ParameterSets','Chen2020','chen2020_lithium_ion_battery.json'));
jsonstruct_geometry = parseBattmoJson(fullfile('Examples', 'JsonDataFiles', 'geometryChen.json'));

jsonstruct = mergeJsonStructs({jsonstruct_material, ...
jsonstruct_geometry});



includeDoubleLayer = false;

if includeDoubleLayer

jsonstruct.(ne).(co).(am).(itf).useDoubleLayerCapacity = true;
jsonstruct.(ne).(co).(am).(itf).doubleLayerCapacitance = 0.2;

end


[model, inputparams, ~] = setupModelFromJson(jsonstruct);

c_ne = 29.866*mol/litre; % initial concentration at negative electrode
c_pe = 17.038*mol/litre; % initial concentration at positive electrode

initstate = initStateChen2020(model, c_ne, c_pe);
b = linspace(0.5,2,10);

frequences = logspace(-2, 4, 30);
figure;
hold on;
for i = 1:length(b)

inputparams.NegativeElectrode.Coating.bruggemanCoefficient = b(i);
inputparams.PositiveElectrode.Coating.bruggemanCoefficient = b(i);
impsolv = ImpedanceSolver(inputparams, 'initstate', initstate, 'computeSteadyState', false);

Z = impsolv.computeImpedance(frequences);
Z_re = real(Z);
Z_im = imag(Z);
curve = sprintf('Bruggeman coefficient = %.2f', b(i));
plot(Z_re, -Z_im, 'DisplayName', curve);
end

legend('show');
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
mrstModule add ad-core mrst-gui mpfa agmg linearsolvers

jsonstruct_material = parseBattmoJson(fullfile('ParameterData','ParameterSets','Chen2020','chen2020_lithium_ion_battery.json'));
jsonstruct_geometry = parseBattmoJson(fullfile('Examples', 'JsonDataFiles', 'geometryChen.json'));

jsonstruct = mergeJsonStructs({jsonstruct_material, ...
jsonstruct_geometry});

ne = 'NegativeElectrode';
co = 'Coating';
am = 'ActiveMaterial';
itf = 'Interface';

includeDoubleLayer = true;

if includeDoubleLayer

jsonstruct.(ne).(co).(am).(itf).useDoubleLayerCapacity = true;
jsonstruct.(ne).(co).(am).(itf).doubleLayerCapacitance = 0.2;

end


[model, inputparams, ~] = setupModelFromJson(jsonstruct);

c_ne = 29.866*mol/litre; % initial concentration at negative electrode
c_pe = 17.038*mol/litre; % initial concentration at positive electrode

initstate = initStateChen2020(model, c_ne, c_pe);

b = logspace(-2, 2, 20); %change here

frequences = logspace(-2, 4, 30);


figure;
hold on;
for i = 1:length(b)

fprintf('compute impedance for doubleLayerCapacitance = %.2e ... ', b(i)); %change here

tic
jsonstruct.(ne).(co).(am).(itf).doubleLayerCapacitance = b(i); %change here
[model, inputparams, ~] = setupModelFromJson(jsonstruct);

% inputparams.(ne).(co).(am).(itf).doubleLayerCapacitance = b(i);
impsolv = ImpedanceSolver(inputparams, 'initstate', initstate, 'computeSteadyState', false);

Z = impsolv.computeImpedance(frequences);

fprintf('done in %g s\n', toc);

Z_re = real(Z);
Z_im = imag(Z);
curve = sprintf('doubleLayerCapacitance = %.2e', b(i)); %change here

plot(Z_re, -Z_im, 'DisplayName', curve);

end

legend('show');
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
mrstModule add ad-core mrst-gui mpfa agmg linearsolvers

jsonstruct_material = parseBattmoJson(fullfile('ParameterData','ParameterSets','Chen2020','chen2020_lithium_ion_battery.json'));
jsonstruct_geometry = parseBattmoJson(fullfile('Examples', 'JsonDataFiles', 'geometryChen.json'));

jsonstruct = mergeJsonStructs({jsonstruct_material, ...
jsonstruct_geometry});



includeDoubleLayer = false;

if includeDoubleLayer

jsonstruct.(ne).(co).(am).(itf).useDoubleLayerCapacity = true;
jsonstruct.(ne).(co).(am).(itf).doubleLayerCapacitance = 0.2;

end


[model, inputparams, ~] = setupModelFromJson(jsonstruct);

c_ne = 29.866*mol/litre; % initial concentration at negative electrode
c_pe = 17.038*mol/litre; % initial concentration at positive electrode

initstate = initStateChen2020(model, c_ne, c_pe);
b = linspace(10, 1000, 20); %change here

frequences = logspace(-2, 4, 30);
figure;
hold on;
for i = 1:length(b)

inputparams.NegativeElectrode.Coating.ActiveMaterial.electronicConductivity = b(i); %change here
inputparams.PositiveElectrode.Coating.ActiveMaterial.electronicConductivity = b(i); %change here

impsolv = ImpedanceSolver(inputparams, 'initstate', initstate, 'computeSteadyState', false);

Z = impsolv.computeImpedance(frequences);
Z_re = real(Z);
Z_im = imag(Z);
curve = sprintf('Electronic Conductivity = %.2f', b(i)); %change here
plot(Z_re, -Z_im, 'DisplayName', curve);
end

legend('show');
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
function param_dependency()
mrstModule add ad-core mrst-gui mpfa agmg linearsolvers

% 1. Chargement des données (Conservez votre méthode de chargement ici)
jsonstruct_material = parseBattmoJson(fullfile('ParameterData','ParameterSets','Chen2020','chen2020_lithium_ion_battery.json'));
jsonstruct_geometry = parseBattmoJson(fullfile('Examples', 'JsonDataFiles', 'geometryChen.json'));
jsonstruct = mergeJsonStructs({jsonstruct_material, jsonstruct_geometry});
ne = 'NegativeElectrode';
co = 'Coating';
am = 'ActiveMaterial';
itf = 'Interface';

includeDoubleLayer = true;

if includeDoubleLayer

jsonstruct.(ne).(co).(am).(itf).useDoubleLayerCapacity = true;
jsonstruct.(ne).(co).(am).(itf).doubleLayerCapacitance = 0.2;

end



c_ne = 29.866*mol/litre;
c_pe = 17.038*mol/litre;

% 2. Création de l'interface graphique (Fenêtre plus haute pour accueillir les curseurs)
fig = uifigure('Name', 'Impedance Explorer', 'Position', [100, 50, 600, 650]);

% Graphique
ax = uiaxes(fig, 'Position', [50, 350, 500, 250]);
title(ax, 'Nyquist Diagram');
xlabel(ax, 'Re(Z)');
ylabel(ax, '-Im(Z)');
grid(ax, 'on');

% Label de statut
lbl = uilabel(fig, 'Position', [50, 310, 500, 22], 'Text', 'Ready, use slider to start.');
lbl.FontWeight = 'bold';

% --- CURSEUR 1 : Diffusion Solide (Anode) ---
% Modifie le transport du Lithium dans les particules de graphite.
uilabel(fig, 'Position', [50, 260, 500, 40], 'WordWrap', 'on', ...
'Text', '1. Anode refDiffusionCoefficient * 10^x (initial value: 1.3135e-15)');

sld1 = uislider(fig, 'Position', [50, 240, 500, 3], ...
'Limits', [-5, 5], ... % Définit le minimum et le maximum
'MajorTicks', -5:0.5:5, ... % Ajoute une graduation tous les 1
'Value', 0);

% --- CURSEUR 2 : Taux de réaction (Anode) ---
% Modifie la cinétique de transfert de charge.
uilabel(fig, 'Position', [50, 170, 500, 40], 'WordWrap', 'on', ...
'Text', '2. Anode reactionRateConstant * 10^x (initial value: 5.031e-11) ');
sld2 = uislider(fig, 'Position', [50, 150, 500, 3],...
'Limits', [-5, 5], ... % Définit le minimum et le maximum
'MajorTicks', -5:0.5:5, ... % Ajoute une graduation tous les 1
'Value', 0);

% --- CURSEUR 3 : Taux de réaction (Cathode) ---
% Modifie la cinétique de transfert de charge.
uilabel(fig, 'Position', [50, 80, 500, 40], 'WordWrap', 'on', ...
'Text', '3. doubleLayerCapacitance * x (initial value: 0.2)');
sld3 = uislider(fig, 'Position', [50, 60, 500, 3], ...
'Limits', [-5, 5], ... % Définit le minimum et le maximum
'MajorTicks', -5:0.5:5, ... % Ajoute une graduation tous les 1
'Value', 0);

% 4. Connexion aux événements
% On crée une fonction anonyme qui passe TOUS les curseurs
callback_fcn = @(src, event) updatePlot(ax, lbl, jsonstruct, c_ne, c_pe, sld1, sld2, sld3);

sld1.ValueChangedFcn = callback_fcn;
sld2.ValueChangedFcn = callback_fcn;
sld3.ValueChangedFcn = callback_fcn;

% Optionnel : Tracer l'état initial
% updatePlot(ax, lbl, jsonstruct, c_ne, c_pe, sld1, sld2, sld3);
end

% --- Fonction de mise à jour ---
function updatePlot(ax, lbl, jsonstruct, c_ne, c_pe, sld1, sld2, sld3)

% Figer l'affichage
lbl.Text = 'Processing... ';
lbl.FontColor = '#D95319';
drawnow;

% 1. Récupération des valeurs de base depuis votre JSON
base_diff_anode = 1.3135e-15;
base_rate_anode = 5.031e-11;
base_doubleLayerCapacitance = 0.2;

% 2. Application des multiplicateurs (10^slider_val)
val_diff_anode = base_diff_anode * (10^(sld1.Value));
val_rate_anode = base_rate_anode * (10^(sld2.Value));
val_doubleLayerCapacitance = base_doubleLayerCapacitance * 10^(sld3.Value);

% 3. Injection dans la structure JSON AVANT de construire le modèle
jsonstruct.NegativeElectrode.Coating.ActiveMaterial.SolidDiffusion.referenceDiffusionCoefficient = val_diff_anode;
jsonstruct.NegativeElectrode.Coating.ActiveMaterial.Interface.reactionRateConstant = val_rate_anode;
jsonstruct.NegativeElectrode.Coating.ActiveMaterial.Interface.doubleLayerCapacitance = val_doubleLayerCapacitance;

try
% 4. Exécution de BattMo
[model, inputparams, ~] = setupModelFromJson(jsonstruct);
initstate = initStateChen2020(model, c_ne, c_pe);

impsolv = ImpedanceSolver(inputparams, 'initstate', initstate, 'computeSteadyState', false);

frequences = logspace(-2, 3, 30);
Z = impsolv.computeImpedance(frequences);

% 5. Tracé
plot(ax, real(Z), -imag(Z), '-o', 'LineWidth', 2, 'Color', '#0072BD');

% Retour à la normale
lbl.Text = 'Done';
lbl.FontColor = '#77AC30';

catch ME
lbl.Text = 'Erreur lors du calcul (vérifiez la console).';
lbl.FontColor = '#A2142F';
disp('--- ERREUR DANS LE CALCUL ---');
disp(ME.message);
for k = 1:length(ME.stack)
disp(['Ligne ', num2str(ME.stack(k).line), ' dans ', ME.stack(k).name]);
end
end
end
Loading
Loading