Skip to content

Commit

Permalink
Merge pull request #26 from ericfell/consistent-labels
Browse files Browse the repository at this point in the history
Consistent labels
  • Loading branch information
ericfell authored Jan 26, 2024
2 parents ccb5a06 + f6d2b4a commit 591e40d
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 208 deletions.
37 changes: 20 additions & 17 deletions src/rfbzero/crossover.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,21 @@ class Crossover:
"""

def __init__(self, membrane_thickness: float, permeability_ox: float, permeability_red: float) -> None:
self.membrane_thickness = membrane_thickness / 10000 # convert microns to cm
self.membrane_thickness_cm = membrane_thickness / 10000 # convert microns to cm
self.permeability_ox = permeability_ox
self.permeability_red = permeability_red

if self.membrane_thickness <= 0.0:
raise ValueError("'membrane_thickness' must be > 0")
if self.membrane_thickness_cm <= 0.0:
raise ValueError("'membrane_thickness' must be > 0.0")

if self.permeability_ox < 0.0 or self.permeability_red < 0.0:
raise ValueError("'permeability_ox' and 'permeability_red' cannot be negative")
if self.permeability_ox < 0.0:
raise ValueError("'permeability_ox' must be >= 0.0")

if self.permeability_red < 0.0:
raise ValueError("'permeability_red' must be >= 0.0")

if self.permeability_ox == 0.0 and self.permeability_red == 0.0:
raise ValueError("'permeability_ox' and 'permeability_red' cannot both be zero")
raise ValueError("'permeability_ox' and 'permeability_red' cannot both be 0.0")

def crossover(
self,
Expand All @@ -46,8 +49,8 @@ def crossover(
c_red_cls: float,
c_ox_ncls: float,
c_red_ncls: float,
vol_cls: float,
vol_ncls: float,
volume_cls: float,
volume_ncls: float,
timestep: float
) -> tuple[float, float, float, float, float, float]:
"""
Expand All @@ -65,9 +68,9 @@ def crossover(
NCLS concentration of oxidized species (M).
c_red_ncls : float
NCLS concentration of reduced species (M).
vol_cls : float
volume_cls : float
Volume of CLS reservoir (L).
vol_ncls : float
volume_ncls : float
Volume of NCLS reservoir (L).
timestep : float
Time interval size (s).
Expand All @@ -83,14 +86,14 @@ def crossover(
c_red_ncls : float
Updated NCLS concentration of reduced species (M).
delta_ox_mols : float
Oxidized species crossing at given timestep (mol).
Oxidized species crossing at given time step (mol).
delta_red_mols : float
Reduced species crossing at given timestep (mol).
Reduced species crossing at given time step (mol).
"""

# Cell geometric area divided by membrane thickness (cm^2/cm)
membrane_constant = geometric_area / self.membrane_thickness
membrane_constant = geometric_area / self.membrane_thickness_cm

# driving force from concentration differences (M)
c_ox_difference = c_ox_cls - c_ox_ncls
Expand All @@ -101,10 +104,10 @@ def crossover(
delta_red_mols = timestep * self.permeability_red * membrane_constant * (c_red_difference / 1000)

# update concentrations (M)
c_ox_cls -= delta_ox_mols / vol_cls
c_ox_ncls += delta_ox_mols / vol_ncls
c_ox_cls -= delta_ox_mols / volume_cls
c_ox_ncls += delta_ox_mols / volume_ncls

c_red_cls -= delta_red_mols / vol_cls
c_red_ncls += delta_red_mols / vol_ncls
c_red_cls -= delta_red_mols / volume_cls
c_red_ncls += delta_red_mols / volume_ncls

return c_ox_cls, c_red_cls, c_ox_ncls, c_red_ncls, delta_ox_mols, delta_red_mols
59 changes: 35 additions & 24 deletions src/rfbzero/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,10 @@ def __init__(self, duration: float, time_increment: float, charge_first: bool =
self.c_ox_ncls: list[float] = [0.0] * self.max_steps
#: The NCLS concentration of reduced species (M), at each time step.
self.c_red_ncls: list[float] = [0.0] * self.max_steps
#: The concentration difference (CLS-NCLS) of oxidized species (M), at each time step.
#: Only meaningful for symmetric cell.
self.delta_ox: list[float] = [0.0] * self.max_steps
#: The concentration difference (CLS-NCLS) of reduced species (M), at each time step.
#: Only meaningful for symmetric cell.
self.delta_red: list[float] = [0.0] * self.max_steps
#: Oxidized species crossing (mols), at each time step. Only meaningful for symmetric cell.
self.delta_ox_mols: list[float] = [0.0] * self.max_steps
#: Reduced species crossing (mols), at each time step. Only meaningful for symmetric cell.
self.delta_red_mols: list[float] = [0.0] * self.max_steps
#: The CLS state of charge, at each time step.
self.soc_cls: list[float] = [0.0] * self.max_steps
#: The NCLS state of charge, at each time step.
Expand All @@ -74,7 +72,7 @@ def __init__(self, duration: float, time_increment: float, charge_first: bool =
#: The combined (CLS+NCLS) mass transport overpotential (V), at each time step.
self.mt: list[float] = [0.0] * self.max_steps
#: The total cell overpotential (V), at each time step.
self.loss: list[float] = [0.0] * self.max_steps
self.total_overpotential: list[float] = [0.0] * self.max_steps

# Total number of cycles is unknown at start, thus list sizes are undetermined
self.capacity = 0.0
Expand Down Expand Up @@ -107,7 +105,7 @@ def _record_step(
ocv: float,
n_act: float = 0.0,
n_mt: float = 0.0,
losses: float = 0.0
total_overpotential: float = 0.0
) -> None:
"""Records simulation data at valid time steps."""
# Update capacity
Expand All @@ -119,10 +117,10 @@ def _record_step(
self.ocv[self.step] = ocv
self.step_is_charge[self.step] = charge

# Record overpotentials and total loss
# Record overpotentials and total total_overpotential
self.act[self.step] = n_act
self.mt[self.step] = n_mt
self.loss[self.step] = losses
self.total_overpotential[self.step] = total_overpotential

# Record species concentrations
cls_ox = cell_model.c_ox_cls
Expand All @@ -133,8 +131,8 @@ def _record_step(
self.c_red_cls[self.step] = cls_red
self.c_ox_ncls[self.step] = ncls_ox
self.c_red_ncls[self.step] = ncls_red
self.delta_ox[self.step] = cell_model.delta_ox
self.delta_red[self.step] = cell_model.delta_red
self.delta_ox_mols[self.step] = cell_model.delta_ox_mols
self.delta_red_mols[self.step] = cell_model.delta_red_mols

# Compute state-of-charge
if self.compute_soc:
Expand Down Expand Up @@ -178,14 +176,14 @@ def _finalize(self) -> None:
self.c_red_cls = self.c_red_cls[:self.step]
self.c_ox_ncls = self.c_ox_ncls[:self.step]
self.c_red_ncls = self.c_red_ncls[:self.step]
self.delta_ox = self.delta_ox[:self.step]
self.delta_red = self.delta_red[:self.step]
self.delta_ox_mols = self.delta_ox_mols[:self.step]
self.delta_red_mols = self.delta_red_mols[:self.step]
self.soc_cls = self.soc_cls[:self.step]
self.soc_ncls = self.soc_ncls[:self.step]

self.act = self.act[:self.step]
self.mt = self.mt[:self.step]
self.loss = self.loss[:self.step]
self.total_overpotential = self.total_overpotential[:self.step]


class CycleStatus(str, Enum):
Expand Down Expand Up @@ -317,9 +315,13 @@ def validate(self) -> CycleStatus:
if abs(self.current) >= min(self.current_lim_cls, self.current_lim_ncls):
return CycleStatus.LIMITING_CURRENT_REACHED

losses, *_ = self.cell_model._total_overpotential(self.current, self.current_lim_cls, self.current_lim_ncls)
total_overpotential, *_ = self.cell_model._total_overpotential(
self.current,
self.current_lim_cls,
self.current_lim_ncls
)
ocv = self.cell_model._open_circuit_voltage()
cell_v = self.cell_model._cell_voltage(ocv, losses, self.charge)
cell_v = self.cell_model._cell_voltage(ocv, total_overpotential, self.charge)

if self.charge and cell_v >= self.voltage_limit or not self.charge and cell_v <= self.voltage_limit:
return CycleStatus.VOLTAGE_LIMIT_REACHED
Expand All @@ -344,10 +346,10 @@ def cycle_step(self) -> CycleStatus:
return self._check_capacity(CycleStatus.NEGATIVE_CONCENTRATIONS)

# Calculate overpotentials and the resulting cell voltage
losses, n_act, n_mt = self.cell_model._total_overpotential(
total_overpotential, n_act, n_mt = self.cell_model._total_overpotential(
self.current, self.current_lim_cls, self.current_lim_ncls)
ocv = self.cell_model._open_circuit_voltage()
cell_v = self.cell_model._cell_voltage(ocv, losses, self.charge)
cell_v = self.cell_model._cell_voltage(ocv, total_overpotential, self.charge)

# Check if the voltage limit is reached
if self.charge and cell_v >= self.voltage_limit or not self.charge and cell_v <= self.voltage_limit:
Expand All @@ -356,7 +358,16 @@ def cycle_step(self) -> CycleStatus:
cycle_status = self._check_capacity(cycle_status)

# Update results
self.results._record_step(self.cell_model, self.charge, self.current, cell_v, ocv, n_act, n_mt, losses)
self.results._record_step(
self.cell_model,
self.charge,
self.current,
cell_v,
ocv,
n_act,
n_mt,
total_overpotential
)

return self._check_time(cycle_status)

Expand Down Expand Up @@ -441,8 +452,8 @@ def __current_direction(self) -> int:

def __find_min_current(self, ocv: float) -> None:
"""
Solves the current at a given timestep of constant voltage cycling.
Attempts to minimize the difference of voltage, OCV, and losses (function of current).
Solves the current at a given time step of constant voltage cycling.
Attempts to minimize the difference of voltage, OCV, and total overpotential (function of current).
"""

Expand Down Expand Up @@ -600,7 +611,7 @@ class ConstantCurrent(CyclingProtocol):
voltage_limit_discharge : float
Voltage below which cell will switch to charge (V).
current : float
Instantaneous current flowing (A).
Current (A) value used for charging. The negative of this value is used for discharging current.
current_charge : float
Desired charging current for CC cycling (A).
current_discharge : float
Expand Down Expand Up @@ -823,7 +834,7 @@ class ConstantCurrentConstantVoltage(CyclingProtocol):
current_cutoff_discharge : float
Current above which CV discharging will switch to CC portion of CCCV charge (A).
current : float
Instantaneous current flowing (A).
Current (A) value used for charging. The negative of this value is used for discharging current.
current_charge : float
Desired charging current for CC cycling (A).
current_discharge : float
Expand Down
Loading

0 comments on commit 591e40d

Please sign in to comment.