"""
=====
Shape
=====
``Shape`` is used to calculate shape properties using 3D capsule geometry.
Outputs `length and `surface_area` are determined from inputs `volume` and `width`.
These variables are required to plug into a `Lattice Environment`
"""
import math
from scipy.constants import N_A
from vivarium.core.process import Step
from vivarium.library.units import units, Quantity
PI = math.pi
AVOGADRO = N_A / units.mol
[docs]
def length_from_volume(volume, width):
"""
get cell length from volume, using the following equation for capsule volume, with V=volume, r=radius,
a=length of cylinder without rounded caps, l=total length:
:math:`V = (4/3)*PI*r^3 + PI*r^2*a`
:math:`l = a + 2*r`
"""
radius = width / 2
cylinder_length = (volume - (4 / 3) * PI * radius**3) / (PI * radius**2)
total_length = cylinder_length + 2 * radius
return total_length
[docs]
def volume_from_length(length, width):
"""
get volume from length and width, using 3D capsule geometry
"""
radius = width / 2
cylinder_length = length - width
volume = cylinder_length * (PI * radius**2) + (4 / 3) * PI * radius**3
return volume
[docs]
def surface_area_from_length(length, width):
"""
get surface area from length and width, using 3D capsule geometry
:math:`SA = 4*PI*r^2 + 2*PI*r*a`
"""
radius = width / 2
cylinder_length = length - width
surface_area = 4 * PI * radius**2 + 2 * PI * radius * cylinder_length
return surface_area
[docs]
def mmol_to_counts_from_volume(volume):
"""mmol_to_counts has units L/mmol"""
return (volume * AVOGADRO).to(units.L / units.mmol)
[docs]
class Shape(Step):
"""Shape Step
Derives cell length and surface area from width and volume.
Ports:
* **cell_global**: Should be given the agent's boundary store.
Contains variables: **volume**, **width**, **length**, and
**surface_area**.
* **periplasm_global**: Contains the **volume** variable for the
volume of the periplasm.
Arguments:
parameters (dict): A dictionary that can contain the
following configuration options:
* **width** (:py:class:`float`): Initial width of the cell in
microns
"""
name = "ecoli-shape"
defaults = {
"width": 1.0 * units.um,
"periplasm_fraction": 0.2,
"cytoplasm_fraction": 0.8,
"initial_cell_volume": 1.2 * units.fL,
"initial_mass": 1339 * units.fg,
}
def __init__(self, parameters=None):
super().__init__(parameters)
self.outer_to_inner_area = (
math.pow(self.parameters["cytoplasm_fraction"], 1 / 3) ** 2
)
[docs]
def ports_schema(self):
schema = {
"cell_global": {
"volume": {
"_default": 0 * units.fL,
"_updater": "set",
"_emit": True,
"_divider": "split",
},
"width": {
"_default": 0 * units.um,
"_updater": "set",
"_emit": True,
"_divider": "set",
},
"length": {
"_default": 0 * units.um,
"_updater": "set",
"_emit": True,
"_divider": "split",
},
"outer_surface_area": {
"_default": 0 * units.um**2,
"_updater": "set",
"_emit": True,
"_divider": "split",
},
"inner_surface_area": {
"_default": 0 * units.um**2,
"_updater": "set",
"_emit": True,
"_divider": "split",
},
"mmol_to_counts": {
"_default": 0 / units.millimolar,
"_emit": True,
"_divider": "split",
"_updater": "set",
},
"mass": {
"_default": 0 * units.fg,
"_updater": "set",
"_emit": True,
"_divider": "split",
},
},
"listener_cell_mass": {
"_default": self.parameters["initial_mass"].magnitude, # fg
},
"listener_cell_volume": {
"_default": self.parameters["initial_cell_volume"].magnitude, # fL
},
"periplasm_global": {
"volume": {
"_default": self.parameters["initial_cell_volume"]
* self.parameters["periplasm_fraction"], # fL
"_emit": True,
"_divider": "split",
"_updater": "set",
},
"mmol_to_counts": {
"_default": 0 / units.millimolar,
"_emit": True,
"_divider": "split",
"_updater": "set",
},
},
"cytoplasm_global": {
"volume": {
"_default": self.parameters["initial_cell_volume"]
* self.parameters["cytoplasm_fraction"], # fL
"_emit": True,
"_divider": "split",
"_updater": "set",
},
"mmol_to_counts": {
"_default": 0 / units.millimolar,
"_emit": True,
"_divider": "split",
"_updater": "set",
},
},
}
return schema
[docs]
def initial_state(self, config=None):
cell_volume = self.parameters["initial_cell_volume"]
assert isinstance(cell_volume, Quantity)
width = self.parameters["width"]
assert isinstance(width, Quantity)
length = length_from_volume(cell_volume, width)
outer_surface_area = surface_area_from_length(length, width)
inner_surface_area = self.outer_to_inner_area * outer_surface_area
assert (
self.parameters["periplasm_fraction"]
+ self.parameters["cytoplasm_fraction"]
== 1
)
periplasm_volume = cell_volume * self.parameters["periplasm_fraction"]
cytoplasm_volume = cell_volume * self.parameters["cytoplasm_fraction"]
mass = self.parameters["initial_mass"]
assert isinstance(mass, Quantity)
return {
"cell_global": {
"volume": cell_volume,
"width": width,
"length": length,
"outer_surface_area": outer_surface_area,
"inner_surface_area": inner_surface_area,
"mmol_to_counts": mmol_to_counts_from_volume(cell_volume),
"mass": mass,
},
"listener_cell_mass": mass.magnitude,
"listener_cell_volume": cell_volume.magnitude,
"periplasm_global": {
"volume": periplasm_volume,
"mmol_to_counts": mmol_to_counts_from_volume(periplasm_volume),
},
"cytoplasm_global": {
"volume": cytoplasm_volume,
"mmol_to_counts": mmol_to_counts_from_volume(cytoplasm_volume),
},
}
[docs]
def next_update(self, timestep, states):
for port in ("cell_global", "periplasm_global", "cytoplasm_global"):
for variable, value in states[port].items():
assert isinstance(
value, Quantity
), f"{variable}={value} is not a Quantity"
width = states["cell_global"]["width"]
cell_volume = states["listener_cell_volume"] * units.fL
assert (
self.parameters["periplasm_fraction"]
+ self.parameters["cytoplasm_fraction"]
== 1
)
periplasm_volume = cell_volume * self.parameters["periplasm_fraction"]
cytoplasm_volume = cell_volume * self.parameters["cytoplasm_fraction"]
# calculate length and surface area
length = length_from_volume(cell_volume, width)
outer_surface_area = surface_area_from_length(length, width)
inner_surface_area = self.outer_to_inner_area * outer_surface_area
update = {
"cell_global": {
"length": length,
"outer_surface_area": outer_surface_area,
"inner_surface_area": inner_surface_area,
"mmol_to_counts": mmol_to_counts_from_volume(cell_volume),
"mass": states["listener_cell_mass"] * units.fg,
"volume": cell_volume,
},
"periplasm_global": {
"volume": periplasm_volume,
"mmol_to_counts": mmol_to_counts_from_volume(periplasm_volume),
},
"cytoplasm_global": {
"volume": cytoplasm_volume,
"mmol_to_counts": mmol_to_counts_from_volume(cytoplasm_volume),
},
}
return update