Skip to content

daggbt/pyedl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pyedl: Semianalytical Models for Electric Double Layers

pyedl (formerly pycapacitance) is a Python package for modeling the physics of Electric Double Layers (EDL) in electrochemical systems. It implements semianalytical approximations for steric models (Carnahan-Starling and Liu) to efficiently calculate properties like electrode charge density, differential capacitance, and grand potential energies, particularly in regimes of high electrode potential and high electrolyte concentration where traditional dilute solution models fail.

This codebase accompanies the manuscript:

Semianalytical approximation of Ion Adsorption Layers and Capacitance in Carnahan–Starling-like steric models
Dagmawi B. Tadesse and Drew F. Parsons
Electrochimica Acta, 531, 146266 (2025).
DOI: 10.1016/j.electacta.2025.146266
URL: ScienceDirect

Features

  • Steric Models: Implements the Carnahan-Starling (CS) and Liu equations of state for hard-sphere fluids within mean-field theory.
  • Composite Diffuse Layer (CDL): Implements a high-potential analytical approximation to the Bikerman model with a fully capped counterion concentration in the steric layer.
  • Double-Electrode Cells: Compute full-cell voltage splits, capacitance, energies, and profiles from charge neutrality rather than user-supplied electrode potentials.
  • Semianalytical Approximation: Uses a linear concentration profile approximation to solve the Poisson-Boltzmann equations analytically in the steric layer, providing rapid convergence to full numerical solutions at high potentials (>0.2 V) and concentrations (>1 M).
  • EDL Properties:
    • Charge Density: Calculate electrode surface charge density ($\sigma$).
    • Differential Capacitance: Calculate differential capacitance ($C_d$) with potential-dependent dielectric effects.
    • Profiles: Generate concentration, electric field, and potential profiles within the double layer.
  • Thermodynamics:
    • Grand Potential Energy: Calculate the total energy stored in the EDL, decomposed into:
      • Entropic Energy: Ideal ion configuration entropy.
      • Electrostatic Energy: Energy stored in the electric field.
      • Steric Energy: Excess free energy due to finite ion size (excluded volume).
  • Material Database: Includes extensible databases for common ions (alkali metals, halides, ionic liquids) and solvents (water, organic solvents).

Installation

The package requires Python 3.12+.

Using uv (Recommended)

# Clone the repository
git clone https://github.com/daggbt/pyedl.git
cd pyedl

# Run examples directly
uv run examples/capacitance.py
uv run examples/energy.py
uv run examples/fitting.py
uv run examples/double_electrode.py

Using pip

pip install .

Usage

1. Calculating Capacitance

from pyedl import ion_database, solvent_database
from pyedl import ElectrochemicalSystem, StericModel

# Define the system: NaF in Water
system = ElectrochemicalSystem(
    cation=ion_database['Na+_hydrated'],
    anion=ion_database['F-_hydrated'],
    solvent=solvent_database['water'],
    concentration=1.0,  # mol/L
    temperature=298.15, # K
    n_hydration_cation=3.5,
    n_hydration_anion=2.7
)

# Initialize the model (Carnahan-Starling)
model = StericModel(system, steric_model='cs')

# Calculate capacitance at 1.0 V
potential = 1.0 # V
capacitance = model.analytical_capacitance(potential)
print(f"Capacitance at {potential}V: {capacitance:.2f} μF/cm²")

2. Free Energy Analysis

Calculate the components of the Grand Potential Energy stored in the EDL.

from pyedl.materials import Ion, Solvent
from pyedl import ElectrochemicalSystem, StericModel

# Define custom materials (e.g., LiPF6 in Propylene Carbonate)
pc_solvent = Solvent(name='Propylene Carbonate', dielectricConstant=66.14, solventPolarizability=6.0)
li_ion = Ion(name='Li+', charge=1, radiusAng=2.82, dispersionB=0.0, ionPolarizability=0.03)
pf6_ion = Ion(name='PF6-', charge=-1, radiusAng=2.54, dispersionB=0.0, ionPolarizability=4.0)

system = ElectrochemicalSystem(
    cation=li_ion, 
    anion=pf6_ion, 
    solvent=pc_solvent, 
    concentration=1.0
)

model = StericModel(system, steric_model='cs')

# Calculate energy components at 1.0 V
phi = 1.0
entropic = model.get_entropic_energy(phi)
electrostatic = model.get_electrostatic_energy(phi)
steric = model.get_steric_free_energy(phi)
total = model.get_total_energy(phi)

print(f"Total Energy: {total:.4e} J/m²")
print(f"  - Entropic: {entropic:.4e} J/m²")
print(f"  - Electrostatic: {electrostatic:.4e} J/m²")
print(f"  - Steric: {steric:.4e} J/m²")

3. Sampling and Plotting

The Phase 0 plotting API lives in pyedl.plotting, with the legacy root-level pyedl.plot_capacitance_vs_potential kept as a compatibility wrapper.

from pyedl.plotting import (
    plot_capacitance_vs_potential,
    plot_energy_components_vs_potential,
    sample_profiles,
)

# Plot capacitance vs potential and keep the sampled data
fig, ax, capacitance_data = plot_capacitance_vs_potential(
    system=system,
    potential_range=(-1.5, 1.5),
    save_path='capacitance_curve.png',
)

# Plot grand-potential components with the same public API style
fig, ax, energy_data = plot_energy_components_vs_potential(
    system=system,
    potential_range=(0.1, 1.0),
    save_path='energy_components.png',
)

# Sample steric-layer profiles without creating a figure
profile_data = sample_profiles(system=system, potential=0.8)
print(profile_data['steric_layer_thickness'])

If you need the older two-array return shape, pyedl.plot_capacitance_vs_potential(...) still returns (potentials, capacitance).

For complete runnable examples, see examples/capacitance.py and examples/energy.py.

4. Fitting a Counterion Permittivity

For inversion or optimization workflows, the JIT sweep path can evaluate an ordered capacitance curve efficiently while scanning candidate parameters.

import numpy as np
from pyedl import ion_database, solvent_database
from pyedl import ElectrochemicalSystem, StericModel, fit_counterion_permittivity_curve

system = ElectrochemicalSystem(
    cation=ion_database['Na+_hydrated'],
    anion=ion_database['F-_hydrated'],
    solvent=solvent_database['water'],
    concentration=1.0,
    temperature=298.15,
)

potentials = np.linspace(0.02, 1.0, 251)
target_model = StericModel(system, steric_model='cs')
fit_model = StericModel(system, steric_model='cs')

# Synthetic target curve for a positive-potential sweep.
target_epsilon = 4.75
target_model.ion_permitivities[1] = target_epsilon
target_model.invalidate_caches()
target_capacitance = target_model.analytical_capacitance_sweep_jit(potentials)

fit_result = fit_counterion_permittivity_curve(
    fit_model,
    potentials,
    target_capacitance,
    epsilon_bounds=(1.0, 10.0),
    use_jit_sweep=True,
)

print(f"Recovered permittivity: {fit_result.fitted_permittivity:.3f}")
print(f"Curve-fit RMSE: {fit_result.rmse:.3e} μF/cm²")

For a complete runnable example, see examples/fitting.py.

5. Composite Diffuse Layer Approximation

The CDLModel provides the analytical composite diffuse layer approximation: below the steric threshold it follows the Gouy-Chapman branch, and above the threshold it uses a capped counterion layer matched to a diffuse tail.

from pyedl import CDLModel

cdl_model = CDLModel(system)
phi = 1.0

print(cdl_model.get_steric_layer_thickness(phi))
print(cdl_model.charge_density(phi))
print(cdl_model.analytical_capacitance(phi))
print(cdl_model.get_total_energy(phi))

For a complete comparison against the Carnahan-Starling model, see examples/cdl.py.

6. Double-Electrode Full Cells

For a double-electrode cell, provide the full-cell voltage. The left and right electrode potentials are computed automatically from charge neutrality, so they should not be supplied by the user.

from pyedl import CDLModel, DoubleElectrodeCell

single_interface_model = CDLModel(system)
cell = DoubleElectrodeCell(single_interface_model)

cell_voltage = 1.0
split = cell.get_potential_split(cell_voltage)

print(split.left_potential, split.right_potential)
print(split.left_charge_density + split.right_charge_density)
print(cell.analytical_capacitance(cell_voltage))
print(cell.get_energy_components(cell_voltage))

DoubleElectrodeCell works as a wrapper around single-interface models. It uses the analytical CDL split when available and otherwise solves the charge-neutrality equation directly, which also supports the semianalytical CS/Liu models.

For a complete runnable example, see examples/double_electrode.py.

Package Structure

  • pyedl.cells: Full-cell wrappers and potential-split helpers.
    • DoubleElectrodeCell: Computes double-electrode voltage splits and combines single-interface observables.
    • PotentialSplit: Dataclass containing left/right electrode potentials and charge-neutrality residuals.
  • pyedl.models: Core physics implementation.
    • StericModel: Implementation of Carnahan-Starling and Liu models.
    • CDLModel: Composite Diffuse Layer approximation with a capped counterion steric layer.
    • ElectrochemicalSystem: Container for system properties.
  • pyedl.materials: Chemical property definitions.
    • Ion: Properties like radius, charge, polarizability.
    • Solvent: Dielectric constant, polarizability.
  • pyedl.fitting: Inversion helpers for fitting counterion permittivities to capacitance curves.
  • pyedl.plotting: Sampling and plotting helpers for capacitance, energy components, and steric-layer profiles.
  • pyedl.utils: Data export helpers and compatibility wrappers for older plotting imports.

Citation

If you use this code in your research, please cite the following paper:

@article{TADESSE2025146266,
title = {Semianalytical approximation of Ion Adsorption Layers and Capacitance in Carnahan–Starling-like steric models},
journal = {Electrochimica Acta},
volume = {531},
pages = {146266},
year = {2025},
issn = {0013-4686},
doi = {10.1016/j.electacta.2025.146266},
url = {https://www.sciencedirect.com/science/article/pii/S0013468625006279},
author = {Dagmawi B. Tadesse and Drew F. Parsons},
keywords = {Carnahan–Starling equation, Steric forces, Semianalytical Carnahan–Starling approximations, Electric double layers, Electric double layer capacitors},
abstract = {The Carnahan–Starling (CS) steric model is the best description of hard-sphere fluids within the mean-field theory. Here we introduce an approximation of the near-linear adsorption concentration profile of a counterion near an electrode for a CS model and derive the subsequent electric field and electrostatic potential profile in a double layer. This enables the derivation of a semianalytical approximation of the electrode charge density, differential capacitance, and total energies (grand potentials) of an electric double-layer capacitor. These semianalytical equations are valid for electrode potentials between 0.2–4 V and converge to the full numerical solutions of the CS model at high potentials of 1V and bulk concentration of 1M with relative errors less than 2% for the electrode charge densities, and less than 5% for the capacitance and total energies. We find the steric contribution comprises approximately one-quarter of the total energy at high electrode potentials, while the contribution from ideal ion entropies becomes insignificant. The model shows very good agreement with experimental measurements of an aqueous electrolyte, and good agreement at high potentials with computer simulations of an ionic liquid. These semianalytical approximations are effective for applications with concentrated solutions or ionic liquids at high applied voltages where the full numerical solution is computationally expensive or in some cases impossible.}
}

License

MIT License

About

pyedl is a Python package for semianalytical electric double layer modeling, with fast steric-model calculations of capacitance, charge density, free energy, and interfacial profiles for concentrated electrolytes and ionic liquids.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages