Profiles / Chemistry & life
EB

Electrochemist & battery engineer

Nernst, cell energy, Peukert and state of charge.

10 recipes on this page · open in the interactive view

Recipes
  1. Cell voltage, Gibbs energy and energy content
  2. Peukert exponent and runtime at high current
  3. State of charge by Coulomb counting
  4. OCV-SOC curve of a Li-ion cell
  5. Impedance spectrum of a Randles circuit
  6. PEM electrolyzer: voltage breakdown at 2 A/cm2
  7. Diffusion coefficient from a Cottrell plot
  8. Electrons transferred in the oxygen reduction (Koutecky-Levich)
  9. Finite-space Warburg impedance of an insertion electrode
  10. Incoming inspection and matching of 18650 cells
executed on MentDB

Cell voltage, Gibbs energy and energy content

A Daniell cell runs at 37 degC with 0.50 M Zn2+ and 0.010 M Cu2+. The script applies the Nernst equation E = E0 - RT/(nF).ln Q, gives dG = -nFE and the energy that 10 g of zinc can deliver in Wh.

Pipeline
Script
admin
#1. Daniell cell at 37 degC: Zn | Zn2+ 0.50 M || Cu2+ 0.010 M | Cu, E0 = 1.100 V, n = 2;
-> "[t]" 310.15;
-> "[e]" (math calc "1.100 - _R*[t]/(2*_F)*ln(0.50/0.010)");
#2. Gibbs energy and maximum electrical work per mol of Zn;
-> "[dg]" (unit calc (concat "-2 * _F * " [e] " V") "kJ/mol");
#3. Wh delivered by 10 g of Zn, M = 65.38 g/mol;
-> "[wh]" (unit calc (concat "10 g / (65.38 g/mol) * 2 * _F * " [e] " V") "Wh");
concat "E = " (math round [e] 4) " V, dG = " (math round [dg] 1) " kJ/mol, energy from 10 g Zn = " (math round [wh] 2) " Wh"
Result
mentdb
E = 1.0477 V, dG = -202.2 kJ/mol, energy from 10 g Zn = 8.59 Wh
Go further
  • Tabulate E versus temperature with num table.
  • Compute the theoretical specific capacity in mAh/g with unit calc.
executed on MentDB

Peukert exponent and runtime at high current

A 100 Ah (C20) lead-acid battery was discharged at 5, 10, 20 and 40 A. The script fits Peukert's law t = a.I^-k as a power law, then predicts the runtime and the usable capacity at 30 A.

Pipeline
Script
admin
#1. Lead-acid battery: runtimes measured at constant currents;
-> "[i]" "[5, 10, 20, 40]"; -> "[h]" "[20.0, 8.9, 3.95, 1.75]";
#2. Peukert law t = a.I^-k, power fit;
json load "rx_ch_pk" (stat fit [i] [h] "power");
-> "[a]" (json select "rx_ch_pk" /a); -> "[k]" (math calc (concat "-1*" (json select "rx_ch_pk" /b)));
#3. Runtime and usable capacity at 30 A;
-> "[t30]" (math calc "[a]*30^(-[k])");
concat "Peukert k = " (math round [k] 3) ", runtime at 30 A = " (math round [t30] 2) " h, usable capacity " (math round (math calc "30*[t30]") 1) " Ah instead of 100 Ah, r2 = " (math round (json select "rx_ch_pk" /r2) 5)
Result
mentdb
Peukert k = 1.172, runtime at 30 A = 2.45 h, usable capacity 73.6 Ah instead of 100 Ah, r2 = 1
Go further
  • Compare with a fit in log-log space using stat fit "linear".
  • Turn the runtime into energy with the mean voltage and unit calc in Wh.
executed on MentDB

State of charge by Coulomb counting

A 3.0 Ah Li-ion cell starting at 95 % SOC is discharged with a current logged every 15 minutes. The script integrates the current (zero-order hold) to get the charge removed, deduces the SOC and reads the expected open circuit voltage from the OCV-SOC table with a monotone interpolation.

Pipeline
Script
admin
#1. Discharge of a 3.0 Ah Li-ion cell, current logged every 15 min, in A;
-> "[t]" "[0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5]";
-> "[i]" "[1.5, 1.5, 2.8, 2.8, 0.6, 0.6, 1.2]";
#2. Coulomb counting: charge removed = integral of i dt, then SOC;
-> "[q]" (num interpolate_integral [t] [i] 0 1.5 "previous");
-> "[soc]" (math calc "100*(0.95 - [q]/3.0)");
#3. Open circuit voltage from the OCV - SOC table, monotone interpolation;
-> "[ocv]" (num interpolate "[0, 10, 20, 40, 60, 80, 100]" "[3.00, 3.45, 3.55, 3.65, 3.78, 3.95, 4.18]" [soc] "pchip");
concat "Charge removed " (math round [q] 3) " Ah, SOC " (math round [soc] 1) " %, expected OCV " (math round [ocv] 3) " V"
Result
mentdb
Charge removed 2.45 Ah, SOC 13.3 %, expected OCV 3.495 V
Go further
  • Invert the OCV table to correct the SOC from a rest voltage.
  • Compute the energy with the logged voltage and num interpolate_integral on V.i.
executed on MentDB

OCV-SOC curve of a Li-ion cell

The open circuit voltage measured at rest against the state of charge is modelled by a degree-5 polynomial, as in a battery management system. The script evaluates the OCV and its slope at 55 % SOC and draws the model curve.

Pipeline
Script
admin
#1. Open circuit voltage of a Li-ion cell measured at rest, SOC 0 to 1;
-> "[pts]" "[[0, 3.00], [0.1, 3.45], [0.2, 3.58], [0.3, 3.64], [0.4, 3.68], [0.5, 3.72], [0.6, 3.78], [0.7, 3.86], [0.8, 3.95], [0.9, 4.05], [1.0, 4.18]]";
#2. Polynomial OCV model of degree 5;
-> "[c]" (pa polynomial_curve_fit_get_coeff [pts] 5);
#3. OCV at 55 % SOC and slope dOCV/dSOC there, for SOC estimation;
-> "[v55]" (pa polynomial_curve_fit_eval [c] 0.55);
-> "[s55]" (num poly_eval (num poly_derive [c]) 0.55);
#4. Model curve every 2 % SOC;
-> "[curve]" (pa polynomial_curve_fit_eval_incr [c] 0 1 0.02);
plot xy (matrix col [curve] 1) (matrix col [curve] 2)
Result
mentdb
The chart is shown in the Plot tab of the editor.
Go further
  • Invert the model with num solve to read the SOC from a measured voltage.
  • Compare with a monotone interpolation: plot interpolate with mode pchip.
executed on MentDB

Impedance spectrum of a Randles circuit

Compute the impedance of a Randles equivalent circuit (Rs, Rct parallel to Cdl, Warburg diffusion) from 100 kHz to 10 mHz: Nyquist coordinates, Bode phase, the top of the semicircle at 1/(2.pi.Rct.Cdl), and the exchange current density from Rct.

Pipeline
Script
admin
#1. Randles circuit: Rs 10 Ohm, Rct 100 Ohm, Cdl 20 uF, Warburg coefficient 50 Ohm.s^-1/2, electrode 1 cm2;
-> "[Rs]" 10; -> "[Rct]" 100; -> "[Cdl]" 20e-6; -> "[sig]" 50;
-> "[fpk]" (math calc "1/(2*pi*[Rct]*[Cdl])");
-> "[out]" "";
json load "rx_tu1_f" (concat "[100000, 1000, " [fpk] ", 1, 0.01]");
for (-> "[i]" 0) (< [i] 5) (++ "[i]") {
	-> "[w]" (math calc (concat "2*pi*" (json select "rx_tu1_f" (concat "/[" [i] "]"))));
	#2. Warburg Zw = sigma.1-j over sqrt w, Faradaic branch Rct + Zw in parallel with Cdl;
	-> "[Zw]" (complex div (concat [sig] "-" [sig] "i") (math sqrt [w]));
	-> "[Yc]" (complex mul (concat [Cdl] "i") [w]);
	-> "[Z]" (complex add [Rs] (complex div 1 (complex add [Yc] (complex div 1 (complex add [Rct] [Zw])))));
	#3. Nyquist coordinates and Bode phase;
	-> "[p]" (complex to_polar [Z]);
	-> "[out]" (concat [out] "f = " (math round_sig (json select "rx_tu1_f" (concat "/[" [i] "]")) 4) " Hz: Z' = " (math round (complex re [Z]) 1) ", -Z'' = " (math round (- 0 (complex im [Z])) 1) ", phase " (math round (math rad_to_deg (json load select "rx_tu1_p" [p] "/theta")) 1) " deg; ");
};
#4. Exchange current density from Rct, n = 1, 25 degC;
concat [out] "i0 = RT/nFRct = " (math round (math calc (concat (const get "R") "*298.15/(" (const get "F") "*[Rct])*1000")) 3) " mA/cm2"
Result
mentdb
f = 100000 Hz: Z' = 10, -Z'' = 0.1, phase -0.5 deg; f = 1000 Hz: Z' = 10.6, -Z'' = 7.9, phase -36.6 deg; f = 79.58 Hz: Z' = 58.9, -Z'' = 51.1, phase -40.9 deg; f = 1.0 Hz: Z' = 129.3, -Z'' = 21.7, phase -9.5 deg; f = 0.01 Hz: Z' = 309.3, -Z'' = 199.5, phase -32.8 deg; i0 = RT/nFRct = 0.257 mA/cm2
Go further
  • Replace Cdl by a constant phase element Q(jw)^n with complex pow
  • Fit Rct and Cdl to measured data with stat fit_model on the real and imaginary parts
executed on MentDB

PEM electrolyzer: voltage breakdown at 2 A/cm2

Split the cell voltage of a PEM electrolyzer at 80 degC into reversible voltage, OER and HER activation overpotentials (Butler-Volmer inverted with asinh) and ohmic loss, then give the HHV efficiency and the specific energy per kg of hydrogen.

Pipeline
Script
admin
#1. PEM electrolyzer at 80 degC and 2 A/cm2: exchange current densities in A/cm2, ohmic ASR in ohm.cm2;
-> "[t]" 353.15; -> "[i]" 2.0; -> "[i0a]" 0.00001; -> "[i0c]" 0.3; -> "[asr]" 0.12;
-> "[aa]" 1.0; -> "[ac]" 0.5;
#2. Reversible voltage at T, then overpotentials from i = 2.i0.sinh of alpha.F.eta/RT, inverted by asinh;
-> "[erev]" (math calc "1.229-0.0009*([t]-298.15)");
-> "[eta_a]" (math calc (concat "_R*[t]/([aa]*_F)*" (math asinh (math calc "[i]/(2*[i0a])"))));
-> "[eta_c]" (math calc (concat "_R*[t]/([ac]*_F)*" (math asinh (math calc "[i]/(2*[i0c])"))));
#3. Error of the pure Tafel approximation on the cathode;
-> "[tafel_c]" (math calc "_R*[t]/([ac]*_F)*ln([i]/[i0c])");
#4. Cell voltage and efficiency on the higher heating value 1.481 V;
-> "[u]" (math calc "[erev]+[eta_a]+[eta_c]+[asr]*[i]");
concat "Erev = " (math round [erev] 3) " V, eta OER = " (math round [eta_a] 3) " V, eta HER = " (math round [eta_c] 3) " V, Tafel error HER = " (math round (math calc "1000*([tafel_c]-[eta_c])") 2) " mV, Ucell = " (math round [u] 3) " V, HHV efficiency = " (math round (math calc "100*1.481/[u]") 1) " %, energy = " (math round (math calc "[u]*2*_F/0.002016/3600000") 1) " kWh/kg H2"
Result
mentdb
Erev = 1.18 V, eta OER = 0.371 V, eta HER = 0.117 V, Tafel error HER = -1.33 mV, Ucell = 1.908 V, HHV efficiency = 77.6 %, energy = 50.7 kWh/kg H2
Go further
  • Sweep the current with num table to draw the polarization curve.
  • Add the mass transport loss with a limiting current term.
executed on MentDB

Diffusion coefficient from a Cottrell plot

After a potential step on a Pt disk in ferrocyanide, regress the current against t^-1/2 through the origin (Cottrell) and deduce the diffusion coefficient.

Pipeline
Script
admin
#1. Chronoamperometry of 5 mM ferrocyanide on a 3 mm Pt disk, current in uA, times 0.5 to 5 s;
-> "[i]" "[72.97, 51.77, 42.11, 36.44, 32.43, 29.76, 27.88, 25.93, 24.6, 23.16]";
-> "[x]" (matrix transpose (concat "[" (matrix col (num table "1/sqrt(t)" "t" 0.5 5 0.5) 2) "]"));
#2. Cottrell plot i vs t^-1/2 forced through the origin;
pa rm load_from_json "rx_p4c_cot" [x] [i];
pa rm set_no_intercept "rx_p4c_cot" true;
-> "[k]" (matrix get (concat "[" (pa rm estimate_regression_parameters "rx_p4c_cot") "]") 1 1);
-> "[r2]" (pa rm calculate_r_squared "rx_p4c_cot");
#3. Cottrell: k = n.F.A.C.sqrt of D/pi, A in cm2, C in mol/cm3;
-> "[a]" (math calc "pi*0.15^2");
-> "[d]" (math calc "pi*([k]*1e-6/(1*_F*[a]*5e-6))^2");
pa rm close "rx_p4c_cot";
concat "Cottrell slope = " (math round [k] 2) " uA.s^1/2, R2 = " (math round [r2] 5) ", A = " (math round [a] 4) " cm2, D = " (math round (math calc "[d]*1e6") 2) "e-6 cm2/s"
Result
mentdb
Cottrell slope = 51.66 uA.s^1/2, R2 = 0.99998, A = 0.0707 cm2, D = 7.21e-6 cm2/s
Go further
  • Keep the intercept with pa rm to detect a capacitive or background current.
  • Compare with D from the Randles-Sevcik slope of a CV scan-rate study.
executed on MentDB

Electrons transferred in the oxygen reduction (Koutecky-Levich)

From rotating disk currents at several rotation rates, fit the Koutecky-Levich law to get the kinetic current and the Levich constant B, then the number of electrons per O2 (4 = direct path, 2 = peroxide).

Pipeline
Script
admin
#1. Oxygen reduction on a Pt/C rotating disk in O2-saturated 0.1 M KOH, j in mA/cm2 at 0.6 V vs RHE;
-> "[rpm]" "[400, 625, 900, 1225, 1600, 2025, 2500]";
-> "[j]" "[2.067, 2.425, 2.718, 3.038, 3.288, 3.519, 3.687]";
#2. Koutecky-Levich: 1/j = 1/jk + 1/B.w^1/2, w in rad/s;
json load "rx_p4c_kl" (stat fit_model "1/(1/jk + 1/(B*sqrt(2*pi*x/60)))" "x" [rpm] [j] "{\"jk\": 5, \"B\": 0.5}");
-> "[jk]" (json select "rx_p4c_kl" /parameters/jk); -> "[b]" (json select "rx_p4c_kl" /parameters/B);
#3. Levich constant for one electron: 0.62.F.D^2/3.nu^-1/6.C, D and nu in cm2/s, C in mol/cm3;
-> "[d23]" (math power (math cbrt 0.000019) 2);
-> "[nu16]" (math nroot 0.01 -6);
-> "[b1]" (math calc "0.62*_F*[d23]*[nu16]*1.2e-6*1000");
#4. Number of electrons exchanged per O2;
concat "jk = " (math round [jk] 2) " mA/cm2, B = " (math round [b] 4) " mA.cm-2.s^1/2, n = " (math round (math calc "[b]/[b1]") 2) " electrons per O2"
Result
mentdb
jk = 7.88 mA/cm2, B = 0.4329 mA.cm-2.s^1/2, n = 3.93 electrons per O2
Go further
  • Repeat at several potentials: jk vs E gives the Tafel slope of the ORR.
  • Confirm the peroxide yield with a ring-disk electrode collection efficiency.
executed on MentDB

Finite-space Warburg impedance of an insertion electrode

Compute the reflective finite Warburg impedance R_D.coth(s)/s of a graphite particle at 0.1 Hz and 1 mHz, check the low-frequency limits (R_D/3 and capacitive behaviour) and get the chemical diffusion coefficient of lithium.

Pipeline
Script
admin
#1. Graphite particle in a Li-ion anode: diffusion resistance R_D in ohm and time constant tau = L^2/D in s;
-> "[rd]" 8.5; -> "[tau]" 40;
#2. Reflective finite Warburg: Z = R_D.coth of s / s, with s = sqrt of j.w.tau, at 0.1 Hz;
-> "[s]" (complex sqrt (concat (math calc "2*pi*0.1*[tau]") "i"));
-> "[z]" (complex div (complex mul [rd] (complex cosh [s])) (complex mul [s] (complex sinh [s])));
#3. Same at 1 mHz: Re Z tends to R_D/3 and the line becomes a capacitance C = tau/R_D;
-> "[s2]" (complex sqrt (concat (math calc "2*pi*0.001*[tau]") "i"));
-> "[z2]" (complex div (complex mul [rd] (complex cosh [s2])) (complex mul [s2] (complex sinh [s2])));
#4. Chemical diffusion coefficient with a diffusion length of 2 um;
concat "Z at 0.1 Hz = " (math round (complex abs [z]) 3) " ohm, phase " (math round (math calc (concat (complex arg [z]) "*180/pi")) 1) " deg; at 1 mHz Re Z = " (math round (complex re [z2]) 3) " ohm (R_D/3 = " (math round (math calc "[rd]/3") 3) "), phase " (math round (math calc (concat (complex arg [z2]) "*180/pi")) 1) " deg; C_lim = " (math round (math calc "[tau]/[rd]") 2) " F; D = " (math calc "(2e-4)^2/[tau]") " cm2/s"
Result
mentdb
Z at 0.1 Hz = 1.697 ohm, phase -45.1 deg; at 1 mHz Re Z = 2.832 ohm (R_D/3 = 2.833), phase -85.2 deg; C_lim = 4.71 F; D = 1.0E-9 cm2/s
Go further
  • Use complex tanh for the transmissive Warburg (blocking vs. absorbing boundary).
  • Add Rct and a CPE in series to build a full Randles-Warburg spectrum.
executed on MentDB

Incoming inspection and matching of 18650 cells

Test whether a lot of cells meets the nominal capacity with the supplier sigma (z-test), find the modal internal resistance class used for matching, and compute the resistance of a 4P group.

Pipeline
Script
admin
#1. Incoming inspection of 16 cells 18650: capacity in Ah and AC internal resistance in mohm;
-> "[cap]" "[2.981, 2.995, 2.972, 2.988, 3.004, 2.979, 2.968, 2.990, 2.985, 2.976, 2.999, 2.983, 2.970, 2.992, 2.987, 2.980]";
-> "[res]" "[22, 23, 22, 21, 22, 24, 23, 22, 22, 21, 23, 22, 25, 22, 23, 22]";
#2. Is the lot below the 3.000 Ah nominal? z-test with the supplier sigma of 0.015 Ah;
json load "rx_p4c_zt" (stat ztest [cap] 3.000 0.015);
#3. Resistance classes: the modal class is used to build the 4P groups;
-> "[mode]" (matrix get (concat "[" (stat mode [res]) "]") 1 1);
-> "[share]" (json load select "rx_p4c_fr" (stat frequency [res]) (concat "/[" (math calc "[mode]-21") "]/frequency"));
#4. Resistance of a 4P group: cells in parallel, harmonic mean / 4;
-> "[rp]" (math calc (concat (stat harm_mean "[22, 22, 22, 21]") "/4"));
concat "mean capacity = " (math round (stat mean [cap]) 4) " Ah, z = " (math round (json select "rx_p4c_zt" /z) 2) ", p = " (math round (json select "rx_p4c_zt" /p_value) 6) "; modal resistance = " [mode] " mohm for " (math round (math calc (concat "100*" [share])) 1) " % of the cells; 4P group = " (math round [rp] 3) " mohm"
Result
mentdb
mean capacity = 2.9843 Ah, z = -4.18, p = 2.9E-5; modal resistance = 22 mohm for 50 % of the cells; 4P group = 5.435 mohm
Go further
  • Reject the outliers with stat zscore before building the pack.
  • Use stat kruskal to compare the resistance of three lots.

Also in Chemistry & life

CH
Chemist
Stoichiometry, concentrations, dilutions, pH and gases with real units.
10 recipes
AP
Advanced physical chemist
Kinetics, stiff ODEs, activation energies and equilibria from thermodynamics.
10 recipes
CR
Corrosion researcher
Mass loss, Faraday, Tafel / Stern-Geary, Nernst and Arrhenius lifetimes.
10 recipes
MS
Materials scientist
Tensile curves, thermal expansion and hardness statistics.
10 recipes
PR
Process & reactor engineer
Batch and continuous reactors, conversion, residence time and heat of reaction.
10 recipes
PP
Pharmacist & pharmacokinetics
Compartment models, half-life, AUC and dosing regimens.
10 recipes
BM
Biologist & microbiologist
Growth curves, enzyme kinetics and dilution series.
10 recipes
EP
Epidemiologist
Epidemic models, reproduction numbers and incidence statistics: ODE models, growth fits and exact Poisson limits.
10 recipes
GB
Geneticist & bioinformatician
Sequences, allele frequencies and Mendelian risks: regex on DNA, Hardy-Weinberg tests, exact fractions.
10 recipes
MP
Medical physicist
Dose, attenuation, decay correction and image quality: MIRD dose, HVL, dual energy, MTF.
10 recipes
AG
Agronomist
Crop development, fertilization and irrigation: degree days, response curves and rainfall statistics.
10 recipes