Skip to content

A first full cell: two half-cells and a proton-exchange membrane - #27

Merged
jorgensd merged 4 commits into
mainfrom
dokken/full-cell-version1
Nov 12, 2026
Merged

jorgensd merged 4 commits into
mainfrom
dokken/full-cell-version1

Conversation

@jorgensd

Copy link
Copy Markdown
Member

Adds FullCell, which builds a redox flow battery cell from two RedoxHalfCells and a ProtonExchangeMembrane on one mesh. It follows Shah et al. (2008), with one deliberate correction at the membrane faces (sulfate is blocked, see below), and without flow for now.

The full cell (electrochemistry/full_cell.py)

  • FullCell(domain, negative, positive, membrane, collector_conductivity): each side is a HalfCellSpec (ions, reservoir, couple, electrode properties, optional fibre distance), and the membrane is a MembraneSpec.
  • Layers and markers: the cell has five cell markers, NegativeCollector, NegativeElectrode, Membrane, PositiveElectrode, PositiveCollector, and the facet markers NegativeTerminal, PositiveTerminal, NegativeFace, PositiveFace. FullCell.mesh_data adds the unions the unknowns live on: NegativeSolid, PositiveSolid, Electrolyte.
  • Each unknown lives on a Subdomain:
    • each side's solid potential on its collector and electrode;
    • the electrolyte potential and the water on both electrodes and the membrane, so the current and the water flux are continuous across the faces without face terms;
    • each side's other ions in its own electrode.
  • Models:
    • the two half-cells, with their collector and membrane resolved as layers (collector=None, membrane=None);
    • the membrane;
    • Ohmic conduction in the collectors;
    • galvanostatic current at the positive terminal, with the negative terminal grounded (ground());
    • a proton flux at each membrane face.
  • Helpers:
    • initialize starts at the reservoir concentrations, $\phi_l = 0$, and each solid at its couple's Nernst potential;
    • voltage is the potential difference between the terminals.

Sulfate is blocked at the membrane faces (electrochemistry/membrane.py)

  • MembraneProtonFlux makes an electrode's protons carry the membrane's current through its face: $\mathbf{N}_\mathrm{H}\cdot\mathbf{n} = \mathbf{i}m\cdot\mathbf{n}/(z\mathrm{H}F)$, using the current on the membrane's side. Since sulfate follows from electroneutrality, this is what keeps it from crossing; every other ion has zero flux naturally.
  • The correction to Shah et al.: they hold the protons at $c_\mathrm{H} = c_f$ at the faces, which lets sulfate carry part of the current across. That condition is still available as ProtonExchangeMembrane.proton_conditions, but FullCell does not use it.
  • Side check: the condition is a surface term of one electrode's proton equation on one face, and it checks that its measure is an interface with the electrode on the "+" side and the membrane on the "-" side.
  • Membrane additions: current_density(), proton_flux(...), markers and constants.
  • Documented domains: the docstrings now state the domain of every membrane equation: bulk equations on the membrane's cells, whose fields may extend into the electrodes.

Core additions

  • symbolic.expression_or_parameter uses an expression as given, or makes a value the default of a named parameter with a unit. It complements input_or_parameter, which turns a number into a unitless constant. Electrolyte (temperature, density, viscosity), the membrane and the full cell's collector conductivity now use it.
  • Domain.interfaces_of(measure) gives the subdomains and marker of each interface an interior-facet measure covers. RateReaction and MembraneProtonFlux use it instead of their own marker lookups.
  • RedoxHalfCell.formal_potential is now a public property.

Tests

  • tests/electrochemistry/test_full_cell.py, on a 1D cell with Shah's dimensions:
    • consistent units, with one electrolyte potential and one water field;
    • both sides must transport protons, and both or neither water;
    • a cell at rest stays at rest, with the difference of the two Nernst potentials as its voltage;
    • on discharge, to Newton's tolerance:
      • each couple turns over $Q/F$;
      • vanadium, bisulfate and sulfate stay on their sides;
      • each side loses $Q/F$ protons;
      • water gains $Q/F$;
    • a face given from the wrong side, or a measure that knows no sides, is rejected.
  • tests/symbolic: tests for expression_or_parameter and Domain.interfaces_of.
  • Status: the full suite passes, the electrochemistry, reaction and symbolic tests also pass on 2 ranks, and the book builds without warnings.

Not in this PR

  • flow, pressure and tanks;
  • the Donnan potential jump at the faces;
  • the proton term in the positive Nernst potential;
  • a crossing, charged membrane;
  • a demo, and 2D.

🤖 Generated with Claude Code

@jorgensd
jorgensd added this pull request to stack #25 November 12, 2026 10:48
Base automatically changed from dokken/temperature-dependent-electrolyte-concentrations to main November 12, 2026 12:39
# Conflicts:
#	src/sumfem/electrochemistry/electrolyte.py
@jorgensd
jorgensd merged commit f3435af into main Nov 12, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant