Skip to content

Commit

Permalink
Merge branch 'main' into consistent-labels
Browse files Browse the repository at this point in the history
  • Loading branch information
ericfell authored Jan 26, 2024
2 parents 64e0cd2 + ccb5a06 commit f6d2b4a
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 74 deletions.
128 changes: 86 additions & 42 deletions src/rfbzero/degradation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,71 +6,111 @@


class DegradationMechanism(ABC):
"""Base class to be overridden by specific degradation mechanisms."""
"""Abstract base class to be implemented by specific degradation mechanisms."""

@abstractmethod
def degrade(self, c_ox: float, c_red: float, timestep: float) -> tuple[float, float]:
def degrade(self, c_ox: float, c_red: float, time_step: float) -> tuple[float, float]:
"""Applies desired degradation mechanisms to oxidized/reduced species at each time step."""
raise NotImplementedError


class ChemicalDegradation(DegradationMechanism):
class ChemicalDegradationOxidized(DegradationMechanism):
"""
Provides an N-th order chemical degradation mechanism.
Provides an N-th order chemical degradation mechanism for an oxidized species.
Parameters
----------
rate_order : int
Rate order for chemical degradation reaction.
rate_constant : float
N-th order rate constant of chemical oxidation (unit is rate order-dependent).
species : str
Species ('red' or 'ox') undergoing chemical degradation.
N-th order rate constant of chemical degradation of oxidized species (unit is rate order-dependent).
"""

def __init__(self, rate_order: int, rate_constant: float, species: str = 'red') -> None:
def __init__(self, rate_order: int, rate_constant: float) -> None:
self.rate_order = rate_order
self.rate_constant = rate_constant
self.species = species

if not isinstance(self.rate_order, int) or self.rate_order < 0:
raise ValueError("'rate_order' must be an integer")
if self.rate_order < 0:
raise ValueError("'rate_order' must be >= 0")

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

if self.species not in ['red', 'ox']:
raise ValueError(f"Invalid value: {self.species}. 'species' options: 'red', 'ox' ")

def degrade(self, c_ox: float, c_red: float, timestep: float) -> tuple[float, float]:
def degrade(self, c_ox: float, c_red: float, time_step: float) -> tuple[float, float]:
"""
N-th order chemical degradation of N*[redox-active species] --> [redox-inactive species].
Returns updated concentrations. Concentration may be unchanged if species does not degrade.
Applies N-th order chemical degradation of N*[redox-active species] --> [redox-inactive species] at each
time step. Returns updated concentrations; concentration may be unchanged if species does not degrade.
Parameters
----------
c_ox : float
Concentration of oxidized species (M).
c_red : float
Concentration of reduced species (M).
timestep : float
time_step : float
Time interval size (s).
Returns
-------
c_ox : float
Updated concentration of oxidized species (M).
c_red : float
Unchanged concentration of reduced species (M).
"""

c_ox -= (time_step * self.rate_constant * (c_ox ** self.rate_order))
return c_ox, c_red


class ChemicalDegradationReduced(DegradationMechanism):
"""
Provides an N-th order chemical degradation mechanism for a reduced species.
Parameters
----------
rate_order : int
Rate order for chemical degradation reaction.
rate_constant : float
N-th order rate constant of chemical degradation of reduced species (unit is rate order-dependent).
"""

def __init__(self, rate_order: int, rate_constant: float) -> None:
self.rate_order = rate_order
self.rate_constant = rate_constant

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

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

def degrade(self, c_ox: float, c_red: float, time_step: float) -> tuple[float, float]:
"""
Applies N-th order chemical degradation of N*[redox-active species] --> [redox-inactive species] at each
time step. Returns updated concentrations; concentration may be unchanged if species does not degrade.
Parameters
----------
c_ox : float
Concentration of oxidized species (M).
c_red : float
Concentration of reduced species (M).
time_step : float
Time interval size (s).
"""
Returns
-------
c_ox : float
Unchanged concentration of oxidized species (M).
c_red : float
Updated concentration of reduced species (M).
if self.species == 'red':
c_red -= (timestep * self.rate_constant * (c_red**self.rate_order))
else:
c_ox -= (timestep * self.rate_constant * (c_ox**self.rate_order))
"""

c_red -= (time_step * self.rate_constant * (c_red ** self.rate_order))
return c_ox, c_red


Expand Down Expand Up @@ -110,8 +150,9 @@ def __init__(self, rate_constant: float, c_oxidant: float = 0.0, oxidant_stoich:
if (self.oxidant_stoich > 0 and c_oxidant == 0.0) or (self.oxidant_stoich == 0 and c_oxidant > 0.0):
raise ValueError("'c_oxidant' and 'oxidant_stoich' must both be zero, or both be positive")

def degrade(self, c_ox: float, c_red: float, timestep: float) -> tuple[float, float]:
def degrade(self, c_ox: float, c_red: float, time_step: float) -> tuple[float, float]:
"""
Applies an auto-oxidation mechanism to oxidized/reduced species at each time step.
Defaults to first order process: red --> ox.
Parameters
Expand All @@ -120,7 +161,7 @@ def degrade(self, c_ox: float, c_red: float, timestep: float) -> tuple[float, fl
Concentration of oxidized species (M).
c_red : float
Concentration of reduced species (M).
timestep : float
time_step : float
Time interval size (s).
Returns
Expand All @@ -132,7 +173,7 @@ def degrade(self, c_ox: float, c_red: float, timestep: float) -> tuple[float, fl
"""

delta_concentration = timestep * self.rate_constant * c_red * (self.c_oxidant ** self.oxidant_stoich)
delta_concentration = time_step * self.rate_constant * c_red * (self.c_oxidant ** self.oxidant_stoich)

c_ox += delta_concentration
c_red -= delta_concentration
Expand Down Expand Up @@ -176,8 +217,9 @@ def __init__(self, rate_constant: float, c_reductant: float = 0.0, reductant_sto
if (self.reductant_stoich > 0 and c_reductant == 0.0) or (self.reductant_stoich == 0 and c_reductant > 0.0):
raise ValueError("'c_reductant' and 'reductant_stoich' must both be zero, or both be positive")

def degrade(self, c_ox: float, c_red: float, timestep: float) -> tuple[float, float]:
def degrade(self, c_ox: float, c_red: float, time_step: float) -> tuple[float, float]:
"""
Applies an auto-reduction mechanism to oxidized/reduced species at each time step.
Defaults to first order process: ox --> red.
Parameters
Expand All @@ -186,7 +228,7 @@ def degrade(self, c_ox: float, c_red: float, timestep: float) -> tuple[float, fl
Concentration of oxidized species (M).
c_red : float
Concentration of reduced species (M).
timestep : float
time_step : float
Time interval size (s).
Returns
Expand All @@ -198,7 +240,7 @@ def degrade(self, c_ox: float, c_red: float, timestep: float) -> tuple[float, fl
"""

delta_concentration = timestep * self.rate_constant * c_ox * (self.c_reductant ** self.reductant_stoich)
delta_concentration = time_step * self.rate_constant * c_ox * (self.c_reductant ** self.reductant_stoich)

c_ox -= delta_concentration
c_red += delta_concentration
Expand Down Expand Up @@ -236,9 +278,9 @@ def __init__(self, forward_rate_constant: float, backward_rate_constant: float,
if self.c_dimer < 0.0:
raise ValueError("'c_dimer' must be >= 0.0")

def degrade(self, c_ox: float, c_red: float, timestep: float) -> tuple[float, float]:
def degrade(self, c_ox: float, c_red: float, time_step: float) -> tuple[float, float]:
"""
Reversible dimerization: ox + red <--> dimer.
Applies a reversible dimerization mechanism to oxidized/reduced species at each time step.
Returns updated concentrations.
Parameters
Expand All @@ -247,7 +289,7 @@ def degrade(self, c_ox: float, c_red: float, timestep: float) -> tuple[float, fl
Concentration of oxidized species (M).
c_red : float
Concentration of reduced species (M).
timestep : float
time_step : float
Time interval size (s).
Returns
Expand All @@ -259,20 +301,22 @@ def degrade(self, c_ox: float, c_red: float, timestep: float) -> tuple[float, fl
"""

delta = timestep * ((self.forward_rate_constant * c_ox * c_red) - (self.backward_rate_constant * self.c_dimer))
delta_concentration = time_step * (
(self.forward_rate_constant * c_ox * c_red) - (self.backward_rate_constant * self.c_dimer)
)

self.c_dimer += delta
c_red -= delta
c_ox -= delta
self.c_dimer += delta_concentration
c_red -= delta_concentration
c_ox -= delta_concentration

return c_ox, c_red


class MultiDegradationMechanism(DegradationMechanism):
"""
Provides option to input multiple degradation mechanisms available within the DegradationMechanism abstract class.
Provides usage of multiple degradation mechanisms that implement the DegradationMechanism abstract base class.
Allows for different and/or multiple mechanisms to be applied to reduced and/or oxidized species. Degradation
mechanisms are applied in the same order they are inputted.
mechanisms are applied in the same order as the input list.
Parameters
----------
Expand All @@ -287,9 +331,9 @@ def __init__(self, mechanisms: list[DegradationMechanism]) -> None:
if not isinstance(mechanism, DegradationMechanism):
raise ValueError(f"Mechanism {mechanism} is not of type DegradationMechanism")

def degrade(self, c_ox: float, c_red: float, timestep: float) -> tuple[float, float]:
def degrade(self, c_ox: float, c_red: float, time_step: float) -> tuple[float, float]:
"""
Input of different degradation mechanisms.
Applies multiple degradation mechanisms to oxidized/reduced species at each time step.
Concentration may be unchanged if species does not degrade.
Parameters
Expand All @@ -298,7 +342,7 @@ def degrade(self, c_ox: float, c_red: float, timestep: float) -> tuple[float, fl
Concentration of oxidized species (M).
c_red : float
Concentration of reduced species (M).
timestep : float
time_step : float
Time interval size (s).
Returns
Expand All @@ -311,5 +355,5 @@ def degrade(self, c_ox: float, c_red: float, timestep: float) -> tuple[float, fl
"""

for mechanism in self.mechanisms:
c_ox, c_red = mechanism.degrade(c_ox, c_red, timestep)
c_ox, c_red = mechanism.degrade(c_ox, c_red, time_step)
return c_ox, c_red
42 changes: 19 additions & 23 deletions tests/test_degradation.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import pytest
#import numpy as np

from rfbzero.degradation import (DegradationMechanism, ChemicalDegradation, AutoOxidation, AutoReduction,
Dimerization, MultiDegradationMechanism)
from rfbzero.degradation import (DegradationMechanism, ChemicalDegradationOxidized, ChemicalDegradationReduced,
AutoOxidation, AutoReduction, Dimerization, MultiDegradationMechanism)


class TestDegradationMechanism:
Expand All @@ -13,29 +13,25 @@ def test_abstract_class_init(self):


class TestChemicalDegradation:
@pytest.mark.parametrize("order,constant,spec",
[(-1, 0.1, 'red'),
('one', 0.1, 'red'),
(1, -0.1, 'red'),
(1, 0.1, 'blue')])
def test_chem_deg_init(self, order, constant, spec):
@pytest.mark.parametrize("order,constant",
[(-1, 0.1),
(-1, -0.1),
(1, -0.1,)])
def test_chem_deg_init(self, order, constant):
with pytest.raises(ValueError):
ChemicalDegradation(rate_order=order,
rate_constant=constant,
species=spec)
ChemicalDegradationReduced(rate_order=order, rate_constant=constant)

with pytest.raises(ValueError):
ChemicalDegradationReduced(rate_order=order, rate_constant=constant)

def test_chem_deg_degrade(self):
test_chemdeg = ChemicalDegradation(rate_order=1,
rate_constant=0.1,
species='red')
c_o, c_r = test_chemdeg.degrade(c_ox=1, c_red=0.5, timestep=0.1)
test_chemdeg = ChemicalDegradationReduced(rate_order=1, rate_constant=0.1)
c_o, c_r = test_chemdeg.degrade(c_ox=1, c_red=0.5, time_step=0.1)
assert c_o == 1
assert c_r == 0.495

test_chemdeg2 = ChemicalDegradation(rate_order=1,
rate_constant=0.1,
species='ox')
c_ox, c_red = test_chemdeg2.degrade(c_ox=1, c_red=0.5, timestep=0.1)
test_chemdeg2 = ChemicalDegradationOxidized(rate_order=1, rate_constant=0.1)
c_ox, c_red = test_chemdeg2.degrade(c_ox=1, c_red=0.5, time_step=0.1)
assert c_ox == 0.99
assert c_red == 0.5

Expand All @@ -54,7 +50,7 @@ def test_auto_ox_stoich(self, c_oxid, stoich):

def test_auto_ox_degrade(self):
test_autoox = AutoOxidation(rate_constant=0.1)
c_o, c_r = test_autoox.degrade(c_ox=1, c_red=0.5, timestep=0.1)
c_o, c_r = test_autoox.degrade(c_ox=1, c_red=0.5, time_step=0.1)
assert c_o == 1.005
assert c_r == 0.495

Expand All @@ -73,7 +69,7 @@ def test_auto_red_stoich(self, c_reduct, stoich):

def test_auto_red_degrade(self):
test_autored = AutoReduction(rate_constant=0.1)
c_o, c_r = test_autored.degrade(c_ox=1, c_red=0.5, timestep=0.1)
c_o, c_r = test_autored.degrade(c_ox=1, c_red=0.5, time_step=0.1)
assert c_o == 0.99
assert c_r == 0.51

Expand All @@ -90,7 +86,7 @@ def test_multi_deg_degrade(self):
mech_2 = AutoOxidation(rate_constant=0.1)
mech_list = [mech_1, mech_2]
test_multi = MultiDegradationMechanism(mechanisms=mech_list)
c_o, c_r = test_multi.degrade(c_ox=1, c_red=0.5, timestep=0.1)
c_o, c_r = test_multi.degrade(c_ox=1, c_red=0.5, time_step=0.1)
assert c_o == 0.9951
assert c_r == 0.5049

Expand All @@ -104,6 +100,6 @@ def test_dimerization_init(self, k_f, k_b, c_dim):

def test_dimerization_degrade(self):
test_dimerize = Dimerization(forward_rate_constant=2, backward_rate_constant=1, c_dimer=0.5)
c_o, c_r = test_dimerize.degrade(c_ox=1, c_red=0.5, timestep=1)
c_o, c_r = test_dimerize.degrade(c_ox=1, c_red=0.5, time_step=1)
assert c_o == 0.5
assert c_r == 0.0
Loading

0 comments on commit f6d2b4a

Please sign in to comment.