chemistrykit.electrochem#
chemistrykit.electrochem: electrochemistry.
The Nernst equation for standard and concentration cells (with an
activity-coefficient-corrected variant built on
chemistrykit.solutions.systems.activity); a curated table of
standard reduction potentials with redox-couple balancing and
cell-potential combination; Butler-Volmer electrode kinetics, exchange
current density, and Tafel-plot linearization; galvanic vs. electrolytic
cells and Faraday’s laws of electrolysis; and a simplified constant-
current battery discharge/capacity model with Peukert’s-law rate
dependence; Kohlrausch’s conductivity laws; fuel-cell thermodynamic
limits; and the diffusion-limited currents of electroanalysis (Cottrell,
Ilkovič, Heyrovský-Ilkovič wave, Randles-Ševčík).
- class chemistrykit.electrochem.BatteryDischargeModel[source]#
Bases:
ABCCommon interface for a simplified constant-current battery discharge model.
Simplified model, flagged explicitly: a real battery’s discharge behavior involves a nonlinear open-circuit-voltage-vs-state-of-charge curve, temperature dependence, cycle aging, and relaxation effects under load – none of that is modeled here. This captures only the two textbook-level effects usually taught alongside each other: a constant ohmic (IR) voltage drop under load, and the empirical Peukert’s-law dependence of effective capacity on discharge rate (W. Peukert, Elektrotechnische Zeitschrift 20, 20 (1897); see Linden & Reddy, Handbook of Batteries, 3rd ed., Ch. 3.3).
Concrete subclasses implement
state_of_charge()andterminal_voltage();discharge_curve()is then available on every subclass for free, mirroring howchemistrykit.solutions.core.base_system.Titration.curve()is shared machinery built once atop each subclass’spH_at.- discharge_curve(t)[source]#
Compute the full discharge curve over a range of elapsed times.
- Parameters:
t (array-like of float) – Elapsed discharge times to evaluate at.
- Return type:
- Returns:
DischargeResult
- class chemistrykit.electrochem.ConstantCurrentBattery(capacity_peukert, current, v_nominal, internal_resistance=0.0, k=1.0)[source]#
Bases:
BatteryDischargeModelA battery discharged at constant current, with a Peukert-law-limited runtime and an ohmic voltage sag.
Simplified model (see the module and
BatteryDischargeModeldocstrings): the open-circuit voltage is treated as constant at v_nominal until the Peukert-law discharge time is reached, at which point the battery is treated as instantaneously exhausted (voltage drops to 0); the only voltage variation modeled during discharge is a constant ohmic sag \(IR_{internal}\), not the smoothly declining open-circuit-voltage-vs-state-of-charge curve of a real cell.- Parameters:
Examples
>>> battery = ConstantCurrentBattery(capacity_peukert=2.0, current=1.0, v_nominal=3.7, internal_resistance=0.05) >>> round(battery.discharge_time(), 4) 2.0 >>> round(float(battery.terminal_voltage(1.0)), 4) 3.65 >>> round(float(battery.terminal_voltage(3.0)), 4) 0.0
- discharge_time()[source]#
Total discharge time (runtime until exhausted), in h.
- Return type:
- Returns:
float
- class chemistrykit.electrochem.DischargeResult(t, voltage, state_of_charge)[source]#
Bases:
objectContainer for the output of a
BatteryDischargeModel.discharge_curve()call.Mirrors
chemistrykit.solutions.core.base_system.TitrationResult(a stable, dataclass return type for a curve traced out over an independent variable – there, titrant volume; here, elapsed time).- state_of_charge: ndarray#
Fraction of rated capacity remaining at each time in t, in [0, 1].
- Type:
ndarray
- class chemistrykit.electrochem.HalfReaction(name, n, E_standard)[source]#
Bases:
objectA single tabulated standard reduction half-reaction.
- Parameters:
- class chemistrykit.electrochem.KohlrauschFit(limiting_molar_conductivity, kohlrausch_coefficient, r_squared)[source]#
Bases:
objectResult of fitting \(\Lambda_m\) vs. \(\sqrt{c}\) data to Kohlrausch’s square-root law.
- class chemistrykit.electrochem.TafelFit(tafel_slope, exchange_current_density, r_squared)[source]#
Bases:
objectResult of fitting \(\log_{10}|i|\) vs. \(\eta\) data to a Tafel line.
Since \(\eta = b\log_{10}i - b\log_{10}i_0\), a plot of eta against \(\log_{10}|i|\) is a straight line of slope b (the Tafel slope) and intercept \(-b\log_{10}i_0\), from which the exchange current density \(i_0=10^{-\text{intercept}/b}\) follows.
- chemistrykit.electrochem.activity_corrected_reaction_quotient(concentrations, charges, stoich_coeffs, T=298.15, extended=True, Ba=1.0)[source]#
Reaction quotient built from Debye-Huckel activity-corrected concentrations rather than raw concentrations.
\(Q = \prod_i a_i^{\nu_i} = \prod_i (\gamma_i c_i / c^\circ)^{\nu_i}\) (with \(c^\circ = 1\) mol/L implicit throughout, matching the convention of
chemistrykit.thermo.systems.equilibrium.reaction_quotient()), where every ion’s activity coefficient \(\gamma_i\) is evaluated at the mixture’s ionic strength viachemistrykit.solutions.systems.activity.ionic_strength()andchemistrykit.solutions.systems.activity.activity_coefficient_debye_huckel_extended()– reusing this package’s existing Debye-Huckel machinery rather than assuming unit activity coefficients, per Bard & Faulkner, Electrochemical Methods, 2nd ed., Ch. 2.1.3. Only ionic species (\(z\neq0\)) get a nontrivial activity coefficient; a neutral species or a pure solid/liquid passed with charges=0 is treated as ideal (\(\gamma=1\)), the usual convention for a species at unit activity by definition (e.g. the solid electrode itself).- Parameters:
concentrations (array-like of float) – Concentration of each species, in mol/L.
charges (array-like of float) – Charge number of each species (0 for a neutral species / pure solid or liquid).
stoich_coeffs (array-like of float) – Signed net stoichiometric coefficient of each species (positive for products, negative for reactants), same order as concentrations.
T (
float) – Absolute temperature; only used insofar aschemistrykit.solutions.systems.activity.DEBYE_HUCKEL_A_25Cis itself a 25 degC value – this function does not otherwise adjust A for T, an approximation reasonable near room temperature only.extended (
bool) – Use the extended Debye-Huckel law (valid to higher ionic strength) rather than the limiting law.Ba (
float) – Ion-size parameter for the extended law; seechemistrykit.solutions.systems.activity.activity_coefficient_debye_huckel_extended().
- Return type:
- Returns:
float
Examples
At vanishing ionic strength (dilute limit), activity coefficients are all 1 and this reduces to the raw-concentration reaction quotient:
>>> c = [1e-9, 1e-12] >>> z = [1, -2] >>> nu = [-1.0, 1.0] >>> round(activity_corrected_reaction_quotient(c, z, nu), 6) 0.001
At higher ionic strength the two differently-charged species’ activity coefficients diverge from each other and from 1, so the activity-corrected quotient deviates from the raw-concentration ratio (here \(c_1/c_0=0.1\)):
>>> Q_raw = 0.1 >>> Q_corrected = activity_corrected_reaction_quotient([0.1, 0.1], [1, -2], nu) >>> bool(abs(Q_corrected - Q_raw) > 1e-3) True
- chemistrykit.electrochem.balance_redox_reaction(cathode, anode)[source]#
Electron-balancing multiples for combining two half-reactions into one overall redox reaction.
Half-reaction potentials combine as-is (see
cell_potential()), but mass-balancing the overall reaction – and therefore computing e.g. how much of each species is consumed per mole of overall reaction via Faraday’s laws (chemistrykit.electrochem.systems.electrolysis) – requires each half-reaction to transfer the same number of electrons. The standard recipe (Atkins & de Paula, Physical Chemistry, 11th ed., Ch. 6.9) multiplies the cathode half-reaction by \(n_{anode}/\gcd(n_{cathode},n_{anode})\) and the anode half-reaction by \(n_{cathode}/\gcd(n_{cathode},n_{anode})\), so both sides transfer \(\mathrm{lcm}(n_{cathode},n_{anode})\) electrons.- Parameters:
cathode (
HalfReaction)anode (
HalfReaction)
- Return type:
- Returns:
cathode_multiple, anode_multiple, n_total (int) – Multiples applied to the cathode and anode half-reactions, and the resulting total number of electrons transferred by the balanced overall reaction.
Examples
\(Zn + Cu^{2+} \to Zn^{2+} + Cu\) needs no rebalancing (both transfer 2 electrons already):
>>> from chemistrykit.electrochem.systems.standard_potentials import STANDARD_REDUCTION_POTENTIALS as T >>> balance_redox_reaction(T["Cu2+/Cu"], T["Zn2+/Zn"]) (1, 1, 2)
\(MnO_4^- + 5Fe^{2+} \to \dots\) needs Fe balanced 5-fold against Mn’s single 5-electron step:
>>> balance_redox_reaction(T["MnO4-/Mn2+"], T["Fe3+/Fe2+"]) (1, 5, 5)
- chemistrykit.electrochem.butler_volmer_current_density(i0, eta, alpha=0.5, n=1, T=298.15, R_gas=8.31446261815324, F=96485.33212331001)[source]#
The Butler-Volmer equation for net electrode current density.
\[i = i_0\left[\exp\!\left(\frac{\alpha n F\eta}{RT}\right) -\exp\!\left(-\frac{(1-\alpha)nF\eta}{RT}\right)\right]\]where \(\eta = E - E_{eq}\) is the overpotential, \(i_0\) the exchange current density, and \(\alpha\) the (cathodic) charge transfer coefficient (Bard & Faulkner, Electrochemical Methods, 2nd ed., Ch. 3.3, eq. 3.3.11). The first term is the anodic (oxidation) partial current, the second the cathodic (reduction) partial current; at \(\eta=0\) they exactly cancel, so no net current flows at equilibrium even though both partial reactions are still occurring (a genuinely dynamic equilibrium, unlike a thermodynamic “nothing is happening”).
- Parameters:
i0 (
float) – Exchange current density, in A/m^2 (or any consistent current- density unit; the same unit is returned).alpha (
float) – Charge transfer (symmetry) coefficient, in \((0, 1)\); 0.5 is the common approximation for a simple, symmetric one-electron step.n (
int) – Number of electrons transferred in the rate-determining step.T (
float) – Absolute temperature, in K.R_gas (
float) – Gas constant, in J mol^-1 K^-1.F (
float) – Faraday constant, in C/mol.
- Returns:
float or ndarray
Examples
At zero overpotential the net current is exactly zero:
>>> round(float(butler_volmer_current_density(i0=1e-6, eta=0.0)), 12) 0.0
A positive overpotential drives net anodic (positive) current:
>>> bool(butler_volmer_current_density(1e-6, eta=0.1) > 0) True
- chemistrykit.electrochem.cell_potential(cathode, anode)[source]#
Standard cell potential from two half-reactions: \(E^\circ_{cell} = E^\circ_{cathode} - E^\circ_{anode}\).
Both potentials are used exactly as tabulated reduction potentials – the anode’s is not negated by hand and neither is scaled by the number of electrons it transfers, because electrode potential is an intensive quantity (a per-electron driving force), unaffected by how many electrons the balanced overall reaction happens to require (Atkins & de Paula, Physical Chemistry, 11th ed., Ch. 6.9). See
balance_redox_reaction()for the electron-balancing arithmetic this potential calculation deliberately does not need.- Parameters:
cathode (
HalfReaction) – The half-reaction that runs as a reduction (gains electrons).anode (
HalfReaction) – The half-reaction that runs as an oxidation (loses electrons); its tabulated reduction potential is passed in unchanged.
- Return type:
- Returns:
float – Standard cell potential, in V. Positive means the reaction as paired (cathode reduction + anode oxidation) is spontaneous.
Examples
The classic Daniell cell, Cu cathode / Zn anode:
>>> from chemistrykit.electrochem.systems.standard_potentials import STANDARD_REDUCTION_POTENTIALS as T >>> round(cell_potential(T["Cu2+/Cu"], T["Zn2+/Zn"]), 2) 1.1
- chemistrykit.electrochem.charge_from_current(current, time)[source]#
Charge passed at constant current: \(Q = It\).
- Parameters:
- Returns:
float or ndarray – Charge, in C.
Examples
>>> round(float(charge_from_current(current=2.0, time=3600.0)), 2) 7200.0
- chemistrykit.electrochem.concentration_cell_potential(n, C_cathode, C_anode, T=298.15, R_gas=8.31446261815324, F=96485.33212331001)[source]#
Cell potential of a concentration cell: \(E = \frac{RT}{nF}\ln(C_{cathode}/C_{anode})\).
A concentration cell pairs two half-cells built from the same electrode material and redox couple, differing only in the concentration of the dissolved species – so \(E^\circ = 0\) identically (both half-reactions have the same standard potential) and the entire driving force comes from the Nernst equation’s activity term (Atkins & de Paula, Physical Chemistry, 11th ed., Ch. 6.10). By convention the more concentrated compartment is the cathode (reduction is favored there, consuming ions and diluting that side, driving the system toward equalized concentrations).
- Parameters:
n (
int) – Number of electrons transferred per formula unit of the redox couple (e.g. 2 for \(M^{2+} + 2e^- \to M\)).C_cathode (float or array-like of float) – Ion concentrations in the cathode and anode compartments, in the same units (an activity-coefficient-corrected value is more accurate at high concentration – see
nernst_potential_with_activity()).C_anode (float or array-like of float) – Ion concentrations in the cathode and anode compartments, in the same units (an activity-coefficient-corrected value is more accurate at high concentration – see
nernst_potential_with_activity()).T (
float) – Absolute temperature, in K.R_gas (
float) – Gas constant, in J mol^-1 K^-1.F (
float) – Faraday constant, in C/mol.
- Returns:
float or ndarray – Cell potential, in V. Positive when C_cathode > C_anode, zero at equal concentrations (no net driving force between two identical half-cells).
Examples
Equal concentrations give exactly zero cell potential:
>>> round(float(concentration_cell_potential(n=2, C_cathode=0.10, C_anode=0.10)), 9) 0.0
A tenfold concentration difference in a 1-electron couple at 25 degC gives the textbook ~59 mV per decade:
>>> round(float(concentration_cell_potential(n=1, C_cathode=0.10, C_anode=0.010)), 4) 0.0592
- chemistrykit.electrochem.cottrell_current(t, n, A, C, D, F=96485.33212331001)[source]#
The Cottrell equation: \(i(t) = nFAC\sqrt{D/(\pi t)}\).
Diffusion-limited current at a planar electrode after a potential step that drives the surface concentration of the electroactive species to zero (Bard & Faulkner, Electrochemical Methods, 2nd ed., eq. 5.2.11).
- Parameters:
- Returns:
float or ndarray – Current, in A.
Examples
The Cottrell signature – \(i\sqrt{t}\) is constant in time:
>>> a = cottrell_current(1.0, n=1, A=1e-6, C=1.0, D=1e-9) >>> b = cottrell_current(4.0, n=1, A=1e-6, C=1.0, D=1e-9) >>> round(a / b, 12) 2.0
- chemistrykit.electrochem.effective_capacity(capacity_peukert, current, k=1.0)[source]#
Effective delivered capacity at a given discharge current: \(C_{eff}(I) = I\,t(I) = C_p I^{1-k}\).
At the ideal Peukert exponent \(k=1\), this is exactly \(C_p\) regardless of current – a battery that delivers its full rated capacity no matter how fast it is discharged. Real batteries (\(k>1\)) deliver progressively less effective capacity as the discharge current increases, the practical meaning of “capacity fade with rate” (Linden & Reddy, Handbook of Batteries, 3rd ed., Ch. 3.3).
- Parameters:
- Returns:
float or ndarray – Effective capacity, in Ah.
Examples
At k=1, effective capacity is independent of current (the ideal, rate-independent battery):
>>> c_low = effective_capacity(10.0, current=1.0, k=1.0) >>> c_high = effective_capacity(10.0, current=5.0, k=1.0) >>> round(c_low, 6), round(c_high, 6) (10.0, 10.0)
At k>1, effective capacity decreases as discharge current increases – the real-battery “capacity fade at high rate” effect:
>>> c_low = effective_capacity(10.0, current=1.0, k=1.2) >>> c_high = effective_capacity(10.0, current=5.0, k=1.2) >>> bool(c_high < c_low) True
- chemistrykit.electrochem.exchange_current_density(k0, C_ox, C_red, n=1, alpha=0.5, F=96485.33212331001)[source]#
Exchange current density from a standard heterogeneous rate constant and bulk concentrations.
\[i_0 = nFk^0 C_{ox}^{1-\alpha}C_{red}^{\alpha}\](Bard & Faulkner, Electrochemical Methods, 2nd ed., Ch. 3.4, eq. 3.4.6) – the current density that flows equally in both directions at equilibrium, before any net overpotential is applied.
- Parameters:
k0 (
float) – Standard heterogeneous electron-transfer rate constant, in m/s (or a consistent length/time unit).C_ox (
float) – Bulk concentrations of the oxidized and reduced forms, in mol/m^3 (consistent with k0’s length unit).C_red (
float) – Bulk concentrations of the oxidized and reduced forms, in mol/m^3 (consistent with k0’s length unit).n (
int) – Electrons transferred.alpha (
float) – Charge transfer coefficient.F (
float) – Faraday constant, in C/mol.
- Return type:
- Returns:
float – Exchange current density, in A/m^2.
Examples
Symmetric concentrations and alpha=0.5 give a simple, order-1-free-of-composition result:
>>> i0 = exchange_current_density(k0=1e-5, C_ox=1.0, C_red=1.0, n=1) >>> round(i0, 6) 0.964853
- chemistrykit.electrochem.faradays_law_mass(current, time, molar_mass, n, efficiency=1.0, F=96485.33212331001)[source]#
Mass deposited/consumed by electrolysis at constant current: \(m = ItM\eta/(nF)\).
Combines
charge_from_current()andmass_from_charge()(Faraday’s laws of electrolysis together, Bard & Faulkner, Electrochemical Methods, 2nd ed., Ch. 1.3.3), with an optional current (faradaic) efficiency \(\eta\in(0,1]\) for the common real-world case where a side reaction (e.g. competing gas evolution) consumes some of the current without depositing the desired product.- Parameters:
current (float) – Applied current, in A.
time (float) – Duration, in s.
molar_mass (
float) – Molar mass of the deposited/consumed species, in g/mol.n (
int) – Electrons transferred per formula unit.efficiency (
float) – Current (faradaic) efficiency, in \((0, 1]\); 1.0 is the ideal, no-side-reaction case.F (
float) – Faraday constant, in C/mol.
- Returns:
float – Mass, in g.
- Raises:
ValueError – If efficiency is not in \((0, 1]\).
Examples
Copper electrorefining, \(Cu^{2+}+2e^-\to Cu\) (M=63.546 g/mol, n=2), run at 10 A for 1 hour: mass scales linearly with both current and time, the direct experimental test of Faraday’s first law.
>>> m1 = faradays_law_mass(current=10.0, time=3600.0, molar_mass=63.546, n=2) >>> m2 = faradays_law_mass(current=20.0, time=3600.0, molar_mass=63.546, n=2) >>> round(m2 / m1, 6) 2.0 >>> round(m1, 4) 11.8549
Reduced current efficiency proportionally reduces the mass deposited:
>>> m_ideal = faradays_law_mass(10.0, 3600.0, 63.546, n=2, efficiency=1.0) >>> m_90pct = faradays_law_mass(10.0, 3600.0, 63.546, n=2, efficiency=0.9) >>> round(m_90pct / m_ideal, 6) 0.9
- chemistrykit.electrochem.fit_kohlrausch_law(c, Lambda_m)[source]#
Extrapolate measured molar conductivities to infinite dilution.
Fits \(\Lambda_m\) against \(\sqrt{c}\) by ordinary least squares; the intercept is \(\Lambda_m^\circ\) and minus the slope is K – exactly Kohlrausch’s graphical extrapolation.
- Parameters:
- Return type:
- Returns:
KohlrauschFit
Examples
>>> import numpy as np >>> c = np.array([1e-4, 1e-3, 5e-3, 1e-2]) >>> fit = fit_kohlrausch_law(c, kohlrausch_molar_conductivity(c, 0.0126, 0.0089)) >>> round(fit.limiting_molar_conductivity, 6), round(fit.kohlrausch_coefficient, 6) (0.0126, 0.0089)
- chemistrykit.electrochem.fit_tafel_plot(eta, i)[source]#
Fit high-overpotential (eta, i) data to a Tafel line and recover b and \(i_0\).
Linearizes \(\eta = b\log_{10}i - b\log_{10}i_0\) and fits by ordinary least squares against \(x=\log_{10}|i|\) – the experimental Tafel-plot analysis (Bard & Faulkner, Electrochemical Methods, 2nd ed., Ch. 3.4), structurally identical to
chemistrykit.kinetics.systems.arrhenius.fit_arrhenius()’s Arrhenius plot.- Parameters:
- Return type:
- Returns:
TafelFit
Examples
Generate exact high-overpotential data from a known \((b, i_0)\) and recover both:
>>> import numpy as np >>> i0_true, alpha, n = 2e-6, 0.5, 1 >>> eta = np.linspace(0.2, 0.4, 10) >>> i = butler_volmer_current_density(i0_true, eta, alpha=alpha, n=n) >>> fit = fit_tafel_plot(eta, i) >>> round(fit.exchange_current_density / i0_true, 2) 1.0
- chemistrykit.electrochem.fuel_cell_efficiency_limit(delta_G, delta_H)[source]#
Maximum (thermodynamic) efficiency of a fuel cell: \(\eta_{max} = \Delta G/\Delta H\).
The fraction of the fuel’s heat of combustion that a reversible cell can deliver as electrical work – not bounded by the Carnot factor of a heat engine (Larminie & Dicks, Fuel Cell Systems Explained, 2nd ed., Ch. 2.3).
- Parameters:
- Return type:
- Returns:
float
Examples
>>> round(fuel_cell_efficiency_limit(-237.13e3, -285.83e3), 3) 0.83
- chemistrykit.electrochem.ilkovic_diffusion_current(n, D, m, t_drop, C, average=False)[source]#
The Ilkovič equation for the polarographic diffusion current at a dropping mercury electrode.
\(i_d = k\,n D^{1/2} m^{2/3} t^{1/6} C\) with \(k=708\) for the maximum current at the end of each drop’s life and \(k=607\) for the drop-averaged current (Bard & Faulkner, Electrochemical Methods, 2nd ed., eqs. 7.1.6-7.1.7). Traditional polarographic units are used here, not SI, because the numerical constants are defined in them.
- Parameters:
- Return type:
- Returns:
float – Diffusion current, in microamperes.
Examples
>>> round(ilkovic_diffusion_current(n=2, D=1e-5, m=2.0, t_drop=4.0, C=1.0), 2) 8.96
The diffusion current is strictly proportional to concentration – the basis of quantitative polarography:
>>> round(ilkovic_diffusion_current(2, 1e-5, 2.0, 4.0, C=3.0) / ilkovic_diffusion_current(2, 1e-5, 2.0, 4.0, C=1.0), 12) 3.0
- chemistrykit.electrochem.is_spontaneous(E_cell)[source]#
Whether a cell reaction is spontaneous as written, from its cell potential.
\(E_{cell} > 0 \iff \Delta G = -nFE_{cell} < 0\), the spontaneity criterion (Atkins & de Paula, Physical Chemistry, 11th ed., Ch. 6.9). A spontaneous cell reaction discharges galvanically (delivers electrical work); a non-spontaneous one must be driven electrolytically by an externally applied voltage exceeding \(|E_{cell}|\) in the opposing sense – see
chemistrykit.electrochem.systems.electrolysis.- Parameters:
E_cell (
float) – Cell potential (standard or Nernst-corrected), in V.- Return type:
- Returns:
bool
Examples
>>> is_spontaneous(1.10) True >>> is_spontaneous(-0.76) False
- chemistrykit.electrochem.kohlrausch_molar_conductivity(c, Lambda0, K)[source]#
Kohlrausch’s square-root law: \(\Lambda_m = \Lambda_m^\circ - K\sqrt{c}\).
Empirically valid for strong electrolytes at low concentration (Atkins & de Paula, Physical Chemistry, 11th ed., Topic 16C.1(b)); Debye, Hückel and Onsager later derived the coefficient K from ion-atmosphere theory.
- Parameters:
- Returns:
float or ndarray – Molar conductivity, in S m^2 mol^-1.
Examples
>>> round(kohlrausch_molar_conductivity(0.0, Lambda0=0.0126, K=0.0089), 6) 0.0126 >>> round(float(kohlrausch_molar_conductivity(0.01, Lambda0=0.0126, K=0.0089)), 6) 0.01171
- chemistrykit.electrochem.limiting_molar_conductivity(ions, table=None)[source]#
Kohlrausch’s law of independent migration: \(\Lambda_m^\circ = \sum_i \nu_i\lambda_i^\circ\).
At infinite dilution every ion migrates independently of its counter-ions, so an electrolyte’s limiting molar conductivity is the stoichiometry-weighted sum of per-ion contributions (Atkins & de Paula, Physical Chemistry, 11th ed., Topic 16C.1(b)).
- Parameters:
ions (
dict[str,float]) – Map from ion name (a key of table) to the number of those ions per formula unit, e.g.{"Mg2+": 1, "Cl-": 2}for MgCl2. Negative multiples are allowed, which is how Kohlrausch’s law is used to combine measured electrolytes.table (
dict[str,float] |None) – Limiting ionic conductivities, in S m^2 mol^-1; defaults toLIMITING_IONIC_CONDUCTIVITIES.
- Return type:
- Returns:
float – Limiting molar conductivity, in S m^2 mol^-1.
Examples
>>> round(limiting_molar_conductivity({"Na+": 1, "Cl-": 1}) * 1e4, 1) # S cm^2/mol 126.4
Independent migration means the difference between two salts with a common anion depends only on the cations:
>>> kcl = limiting_molar_conductivity({"K+": 1, "Cl-": 1}) >>> kno3 = limiting_molar_conductivity({"K+": 1, "NO3-": 1}) >>> nacl = limiting_molar_conductivity({"Na+": 1, "Cl-": 1}) >>> nano3 = limiting_molar_conductivity({"Na+": 1, "NO3-": 1}) >>> round(kcl - nacl, 8) == round(kno3 - nano3, 8) True
- chemistrykit.electrochem.mass_from_charge(charge, molar_mass, n, F=96485.33212331001)[source]#
Mass of substance deposited/consumed: \(m = QM/(nF)\).
- Parameters:
- Returns:
float or ndarray – Mass, in g.
Examples
Silver electroplating, \(Ag^+ + e^- \to Ag\) (n=1, M=107.87 g/mol): depositing 1 mol of electrons’ worth of charge deposits exactly one mole (107.87 g) of silver:
>>> round(mass_from_charge(charge=96485.332, molar_mass=107.87, n=1), 2) 107.87
- chemistrykit.electrochem.minimum_applied_voltage_electrolytic(E_cell)[source]#
Minimum externally applied voltage to drive a non-spontaneous (electrolytic) cell reaction.
A cell reaction with \(E_{cell}<0\) is non-spontaneous as written and will not run galvanically; forcing it to run (electrolysis) requires an externally applied voltage of at least \(|E_{cell}|\), opposing the cell’s natural (spontaneous reverse) direction (Atkins & de Paula, Physical Chemistry, 11th ed., Ch. 6.10). Approximation flagged: this is the thermodynamic minimum only – a real electrolysis cell also needs to overcome activation overpotential at each electrode (see
chemistrykit.electrochem.systems.butler_volmer) and ohmic (solution-resistance) losses, so the practically applied voltage is always somewhat larger than this minimum.- Parameters:
E_cell (
float) – Cell potential of the reaction as written, in V (should be negative – i.e. the reaction is non-spontaneous – for electrolysis to be the relevant regime; seechemistrykit.electrochem.systems.standard_potentials.is_spontaneous()).- Return type:
- Returns:
float – Minimum applied voltage magnitude, in V.
Examples
Electrolyzing molten NaCl (reversing the spontaneous Na/Cl2 cell reaction, \(E_{cell}=-4.07\) V as written for Na deposition):
>>> round(minimum_applied_voltage_electrolytic(-4.07), 2) 4.07
- chemistrykit.electrochem.moles_from_charge(charge, n, F=96485.33212331001)[source]#
Faraday’s first law: moles of substance transformed \(= Q/(nF)\).
One mole of electrons (\(F\) coulombs) transforms exactly \(1/n\) mole of a species undergoing an n-electron electrode reaction – charge passed is directly proportional to the amount of chemical change, the historically first-discovered electrochemical stoichiometry relationship (Faraday, 1834; Bard & Faulkner, Electrochemical Methods, 2nd ed., Ch. 1.3.3).
- Parameters:
- Returns:
float or ndarray – Amount transformed, in mol.
Examples
Doubling the charge exactly doubles the moles transformed (Faraday’s first law’s defining linearity):
>>> moles_from_charge(2.0 * 96485.332, n=1) == 2.0 * moles_from_charge(96485.332, n=1) True
- chemistrykit.electrochem.nernst_potential(E_standard, n, Q, T=298.15, R_gas=8.31446261815324, F=96485.33212331001)[source]#
The Nernst equation: \(E = E^\circ - \frac{RT}{nF}\ln Q\).
Relates a cell’s potential under arbitrary (non-standard-state) conditions to its standard potential and the reaction quotient Q of the overall cell reaction (Bard & Faulkner, Electrochemical Methods, 2nd ed., Ch. 2.1, eq. 2.1.13).
- Parameters:
E_standard (
float) – Standard cell potential \(E^\circ\), in V.n (
int) – Number of electrons transferred in the balanced overall cell reaction (seechemistrykit.electrochem.systems.standard_potentials.balance_redox_reaction()).Q (float or array-like of float) – Reaction quotient of the overall cell reaction (activities of products over reactants, each raised to its stoichiometric coefficient).
T (
float) – Absolute temperature, in K.R_gas (
float) – Gas constant, in J mol^-1 K^-1.F (
float) – Faraday constant, in C/mol.
- Returns:
float or ndarray
Examples
At \(Q = 1\) (unit activity of every species – the standard state by definition), the Nernst equation reduces exactly to \(E^\circ\):
>>> round(float(nernst_potential(E_standard=1.10, n=2, Q=1.0)), 6) 1.1
Increasing the product-side activity (Q > 1) lowers the cell potential below standard, per Le Chatelier’s principle applied to the driving reaction:
>>> bool(nernst_potential(1.10, 2, Q=10.0) < 1.10) True
- chemistrykit.electrochem.nernst_potential_with_activity(E_standard, n, concentrations, charges, stoich_coeffs, T=298.15, extended=True, Ba=1.0)[source]#
The Nernst equation with Q built from Debye-Huckel activity-corrected concentrations.
Combines
activity_corrected_reaction_quotient()withnernst_potential()– the “reuse chemistrykit.solutions’s activity-coefficient machinery” version of the Nernst equation, as opposed tonernst_potential()’s assumption of ideal (unit activity coefficient) behavior. Real electrochemical cells, like real solution equilibria, deviate from ideal Nernstian behavior at concentrations much above dilute (Bard & Faulkner, Electrochemical Methods, 2nd ed., Ch. 2.1.3).- Parameters:
E_standard (
float) – Standard cell potential, in V.n (
int) – Electrons transferred in the balanced overall cell reaction.concentrations (array-like of float) – As in
activity_corrected_reaction_quotient().charges (array-like of float) – As in
activity_corrected_reaction_quotient().stoich_coeffs (array-like of float) – As in
activity_corrected_reaction_quotient().T (
float)extended (
bool)Ba (
float)
- Return type:
- Returns:
float
Examples
In the dilute limit, this recovers the ideal Nernst potential to high accuracy:
>>> c = [1e-6, 1e-12] >>> z = [2, 0] >>> nu = [-1.0, 1.0] >>> Q_raw = c[1] / c[0] >>> ideal = nernst_potential(0.34, n=2, Q=Q_raw) >>> corrected = nernst_potential_with_activity(0.34, n=2, concentrations=c, charges=z, stoich_coeffs=nu) >>> bool(abs(ideal - corrected) < 1e-4) True
- chemistrykit.electrochem.peukert_discharge_time(capacity_peukert, current, k=1.0)[source]#
Peukert’s-law discharge time at constant current: \(t = C_p / I^k\).
\(C_p\) (the “Peukert capacity”, in \(A^k\cdot h\)) and the Peukert exponent \(k\) are empirical constants of a given battery chemistry/construction, fitted from discharge tests at several currents (Linden & Reddy, Handbook of Batteries, 3rd ed., Ch. 3.3, eq. 3.5). \(k=1\) is the ideal case (discharge time inversely proportional to current, i.e. a current-independent delivered capacity, see
effective_capacity()); real batteries typically have \(k>1\) (lead-acid: \(k\approx1.1-1.3\)), reflecting the fact that a higher discharge rate delivers less total capacity than a lower one before the same cutoff voltage is reached.- Parameters:
- Return type:
- Returns:
float or ndarray – Discharge time, in h.
Examples
At k=1 (the ideal case), discharge time is exactly inversely proportional to current:
>>> round(float(peukert_discharge_time(capacity_peukert=10.0, current=2.0, k=1.0)), 4) 5.0 >>> round(float(peukert_discharge_time(capacity_peukert=10.0, current=4.0, k=1.0)), 4) 2.5
At k>1, doubling the current more than halves the discharge time:
>>> t1 = peukert_discharge_time(10.0, current=2.0, k=1.2) >>> t2 = peukert_discharge_time(10.0, current=4.0, k=1.2) >>> bool(t2 < t1 / 2.0) True
- chemistrykit.electrochem.polarographic_wave_current(E, E_half, i_d, n, T=298.15, R_gas=8.31446261815324, F=96485.33212331001)[source]#
Heyrovský-Ilkovič reversible wave: \(E = E_{1/2} + \frac{RT}{nF}\ln\frac{i_d-i}{i}\).
Solved for the (cathodic) current, \(i = i_d/[1+\exp(nF(E-E_{1/2})/RT)]\): a sigmoidal wave rising from 0 to the diffusion plateau \(i_d\) as the potential is swept negative through the half-wave potential (Bard & Faulkner, Electrochemical Methods, 2nd ed., eq. 5.4.22).
- Parameters:
- Returns:
float or ndarray – Current, in the unit of i_d.
Examples
At the half-wave potential the current is exactly half the plateau:
>>> polarographic_wave_current(-0.40, E_half=-0.40, i_d=8.0, n=2) 4.0
- chemistrykit.electrochem.randles_sevcik_peak_current(v, n, A, C, D, T=298.15, R_gas=8.31446261815324, F=96485.33212331001)[source]#
The Randles-Ševčík equation: \(i_p = 0.4463\,nFAC\sqrt{nFvD/(RT)}\).
Peak current of a reversible, diffusion-controlled linear-sweep or cyclic voltammogram at a planar electrode (Bard & Faulkner, Electrochemical Methods, 2nd ed., eq. 6.2.19). At 25 degC this is the familiar \(i_p = (2.69\times10^5)\,n^{3/2}AD^{1/2}Cv^{1/2}\) in cm/mol/s units.
- Parameters:
- Returns:
float or ndarray – Peak current, in A.
Examples
1 mM ferrocene-like couple (n=1, D=1e-9 m^2/s) at a 3 mm diameter disc, 100 mV/s:
>>> import math >>> A = math.pi * (1.5e-3) ** 2 >>> round(randles_sevcik_peak_current(0.1, n=1, A=A, C=1.0, D=1e-9) * 1e6, 1) # microamps 19.0
Peak current grows as the square root of scan rate:
>>> round(randles_sevcik_peak_current(0.4, 1, A, 1.0, 1e-9) / randles_sevcik_peak_current(0.1, 1, A, 1.0, 1e-9), 12) 2.0
- chemistrykit.electrochem.reversible_cell_voltage(delta_G, n, F=96485.33212331001)[source]#
Reversible (open-circuit) voltage from the reaction Gibbs energy: \(E = -\Delta G/(nF)\).
- Parameters:
- Return type:
- Returns:
float – Reversible cell voltage, in V.
Examples
Hydrogen-oxygen cell, \(H_2 + \tfrac{1}{2}O_2 \to H_2O(l)\), \(\Delta G^\circ = -237.13\) kJ/mol, n=2:
>>> round(reversible_cell_voltage(-237.13e3, n=2), 3) 1.229
- chemistrykit.electrochem.reversible_cell_voltage_at_temperature(delta_H, delta_S, n, T, F=96485.33212331001)[source]#
Reversible voltage at temperature T: \(E(T) = -(\Delta H - T\Delta S)/(nF)\).
Assumes \(\Delta H\) and \(\Delta S\) independent of temperature (a standard first approximation over modest ranges).
- Parameters:
- Returns:
float or ndarray – Reversible cell voltage, in V.
Examples
The voltage falls with temperature when \(\Delta S<0\), with slope exactly \(\Delta S/(nF)\):
>>> e1 = reversible_cell_voltage_at_temperature(-285.83e3, -163.3, 2, 300.0) >>> e2 = reversible_cell_voltage_at_temperature(-285.83e3, -163.3, 2, 301.0) >>> round((e2 - e1) * 2 * 96485.33212 / -163.3, 6) 1.0
- chemistrykit.electrochem.standard_cell_potential(cathode_name, anode_name)[source]#
Look up two half-reactions by name in
STANDARD_REDUCTION_POTENTIALSand combine them.- Parameters:
- Return type:
- Returns:
float – Standard cell potential, in V.
Examples
>>> round(standard_cell_potential("Cu2+/Cu", "Zn2+/Zn"), 2) 1.1
- chemistrykit.electrochem.tafel_overpotential(i, i0, alpha=0.5, n=1, T=298.15, R_gas=8.31446261815324, F=96485.33212331001, branch='anodic')[source]#
The linearized Tafel-equation overpotential at high \(|\eta|\).
\(\eta = b\log_{10}(i/i_0)\), dropping the Butler-Volmer equation’s back-reaction exponential term (valid once \(|\eta|\gtrsim100/n\) mV, where that term is negligible compared to the dominant one – Bard & Faulkner, Electrochemical Methods, 2nd ed., Ch. 3.4). Approximation flagged: this is the high-overpotential limit of
butler_volmer_current_density(), not the exact equation – it is inaccurate near \(\eta=0\) where both exponential terms matter (see the module examples gallery for a numerical comparison across overpotential).- Parameters:
- Returns:
float or ndarray – Overpotential, in V.
Examples
At high overpotential the Tafel approximation agrees closely with the full Butler-Volmer equation (checked here to better than 0.5% at 300 mV):
>>> i0, alpha, n = 1e-6, 0.5, 1 >>> eta_true = 0.30 >>> i_full = butler_volmer_current_density(i0, eta_true, alpha=alpha, n=n) >>> eta_tafel = tafel_overpotential(i_full, i0, alpha=alpha, n=n, branch="anodic") >>> bool(abs(eta_tafel - eta_true) / eta_true < 0.005) True
- chemistrykit.electrochem.tafel_slope(alpha, n=1, T=298.15, R_gas=8.31446261815324, F=96485.33212331001, branch='anodic')[source]#
The Tafel slope \(b\), in V per decade of current.
From linearizing the dominant (far-from-equilibrium) exponential term of the Butler-Volmer equation and rewriting in terms of \(\log_{10}i\) (Bard & Faulkner, Electrochemical Methods, 2nd ed., Ch. 3.4, eq. 3.4.11-3.4.13):
\[b_{anodic} = \frac{2.303RT}{\alpha n F}, \qquad b_{cathodic} = \frac{2.303RT}{(1-\alpha) n F}\]- Parameters:
- Return type:
- Returns:
float – Tafel slope, in V/decade (positive for the anodic branch, negative for the cathodic one, matching the sign convention that \(\eta\) increases with \(\log_{10}i\) on the anodic branch and decreases with it on the cathodic one).
Examples
With alpha=0.5 and a one-electron step at 25 degC, the anodic and cathodic slopes have equal magnitude (the symmetric-barrier case):
>>> b_a = tafel_slope(alpha=0.5, n=1, branch="anodic") >>> b_c = tafel_slope(alpha=0.5, n=1, branch="cathodic") >>> round(b_a, 4), round(b_c, 4) (0.1183, -0.1183)