Skip to content

Commit

Permalink
Extended unit consistency check to volume units
Browse files Browse the repository at this point in the history
  • Loading branch information
JR-1991 committed Mar 21, 2022
1 parent 28c6319 commit 35983d8
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 16 deletions.
90 changes: 76 additions & 14 deletions pyenzyme/enzymeml/tools/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
import yaml
import os
import pandas as pd
import warnings

from itertools import cycle
from typing import Optional, Dict, Tuple, List

from pyenzyme.enzymeml.core.unitdef import UnitDef

# Set up a logger for validation
logger = logging.getLogger("validator")
logger.setLevel(logging.WARNING)
Expand Down Expand Up @@ -520,16 +521,26 @@ def check_unit_consistency(cls, enzmldoc, strict: bool = False):
Bool: Whether the document is consistent in units
"""

# Initialize validator to use
cls = cls(scheme={})

# Now create a data structure of units per species for
# Measurements and Replicates
used_units, used_time_units = {}, {}
used_units = {}
used_time_units = {}
used_volume_units = {}
report, is_consistent = {}, True

# Check vessel unit
cls._check_vessels(enzmldoc, used_volume_units)

# Perform consistency check on parameters
cls._check_parameters(enzmldoc, used_time_units)
cls._check_parameters(enzmldoc, used_time_units, used_volume_units)

# Perform consistency check on species/replicates/measurements
cls._check_species(enzmldoc, report, used_time_units, used_units)
cls._check_species(
enzmldoc, report, used_time_units, used_units, used_volume_units
)

if strict:

Expand All @@ -549,13 +560,35 @@ def check_unit_consistency(cls, enzmldoc, strict: bool = False):
# Append to report
report["time_units"] = cls._reformat_strict_report(used_time_units)

if len(set(used_volume_units.values())) != 1:
logger.warning(
f"Inconsistent usage of volume units. All units should be the same, but found {set(used_volume_units.values())}. In order to guarantee modelling platforms based on SBML to work properly, make sure that vessel volume unit is consistent with the one used in concentration. \n\nBest done by editing vessels using 'enzmldoc.getVessel(ID)' and an appropriate 'unit' assigned along with a rescaling."
)

# Append to report
report["volume_units"] = cls._reformat_strict_report(used_volume_units)

if report:
is_consistent = False

return is_consistent, report

@staticmethod
def _check_parameters(enzmldoc, used_time_units: Dict[str, str]) -> None:
def _check_vessels(self, enzmldoc, used_volume_units: Dict[str, str]):
"""Checks volumetric unit consitency of Vessels"""

for vessel in enzmldoc.vessel_dict.values():
vessel_unit = vessel.unitdef()
volume_part = self._get_baseunit_part(vessel_unit, kind="litre")

if volume_part:
used_volume_units[vessel.id] = volume_part[0].get_name()

def _check_parameters(
self,
enzmldoc,
used_time_units: Dict[str, str],
used_volume_units: Dict[str, str],
) -> None:
"""Checks time unit consistency of Parameters"""

# Get all the time units used in parameters
Expand All @@ -574,19 +607,27 @@ def _check_parameters(enzmldoc, used_time_units: Dict[str, str]) -> None:
if param.unit:

# Get the time unit, if given
time_part = list(
filter(
lambda baseunit: baseunit.kind == "second",
param.unitdef().units,
)
)
time_part = self._get_baseunit_part(param.unitdef(), "second")

if time_part:
# When there is a time unit, store it
used_time_units[param.name] = time_part[0].get_name()

@staticmethod
def _check_species(enzmldoc, report, used_time_units, used_units):
# Get the volume unit, if given
volume_part = self._get_baseunit_part(param.unitdef(), "litre")

if volume_part:
# When there is a time unit, store it
used_volume_units[param.name] = volume_part[0].get_name()

def _check_species(
self,
enzmldoc,
report: Dict,
used_time_units: Dict[str, str],
used_units: Dict[str, str],
used_volume_units: Dict[str, str],
):
"""Checks consistency for species to measurements"""

all_species = {
Expand Down Expand Up @@ -625,6 +666,7 @@ def _check_species(enzmldoc, report, used_time_units, used_units):
meas_data = meas_species.get(id)
meas_unit = meas_data.unitdef()
meas_unit_name = meas_unit._get_unit_name()
meas_vol_unit = self._get_baseunit_part(meas_unit, "litre")

if unit is None:
# Use meas unit as the expected one
Expand All @@ -636,6 +678,10 @@ def _check_species(enzmldoc, report, used_time_units, used_units):
used_units[location] = meas_unit_name
used_time_units[location] = measurement.global_time_unit

# If there is a volumetric unit, add it
if meas_vol_unit:
used_volume_units[location] = meas_vol_unit[0].get_name()

if unit_name != meas_unit_name:
# Report if there are inconsistencies
log_dict["measurements"][measurement.id] = {
Expand All @@ -649,6 +695,7 @@ def _check_species(enzmldoc, report, used_time_units, used_units):
# Get replicate units
repl_unit = replicate.data_unitdef()
repl_unit_name = repl_unit._get_unit_name()
repl_vol_unit = self._get_baseunit_part(repl_unit, "litre")

# Add to list of already used units
location = (
Expand All @@ -657,6 +704,10 @@ def _check_species(enzmldoc, report, used_time_units, used_units):
used_units[location] = meas_unit_name
used_time_units[location] = measurement.global_time_unit

# If there is a volumetric unit, add it
if meas_vol_unit:
used_volume_units[location] = repl_vol_unit[0].get_name()

if unit_name != repl_unit_name:
# Report if there are inconsistencies
log_dict["replicates"][measurement.id] = {
Expand All @@ -682,3 +733,14 @@ def _reformat_strict_report(report: Dict[str, str]):
nu_report[value] = [loc for loc, unit in report.items() if unit == value]

return nu_report

@staticmethod
def _get_baseunit_part(unitdef: UnitDef, kind: str):
"""Gets a specific part of a unitdef"""

return list(
filter(
lambda baseunit: baseunit.kind == kind,
unitdef.units,
)
)
19 changes: 17 additions & 2 deletions tests/enzymeml/tools/test_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ def test_creation_to_conversion(self):
def test_check_unit_consistency_positive(self, enzmldoc):
"""Checks whether unit consistency checkup behaves correctly - Positive case"""

# Set vessel to "l" to get a positive result
enzmldoc.vessel_dict["v0"].unit = "l"

# Test default mode - Positive outcome
is_consistent, report = EnzymeMLValidator.check_unit_consistency(enzmldoc)

Expand Down Expand Up @@ -106,7 +109,11 @@ def test_check_unit_consistency_strict_mode_conc(self, enzmldoc):
"conc_units": {
"fmole / l": ["s0", "m0/s0", "m0/s0/repl_s0_0"],
"mmole / l": ["p0", "m0/p0", "m0/p0/repl_p0_0", "s1", "c0"],
}
},
"volume_units": {
"l": ["m0/p0", "m0/p0/repl_p0_0", "m0/s0", "m0/s0/repl_s0_0"],
"ml": ["v0"],
},
}

def test_check_unit_consistency_strict_mode_time(self, enzmldoc):
Expand Down Expand Up @@ -134,7 +141,11 @@ def test_check_unit_consistency_strict_mode_time(self, enzmldoc):
"time_units": {
"min": ["x"],
"s": ["m0/p0", "m0/p0/repl_p0_0", "m0/s0", "m0/s0/repl_s0_0"],
}
},
"volume_units": {
"l": ["m0/p0", "m0/p0/repl_p0_0", "m0/s0", "m0/s0/repl_s0_0"],
"ml": ["v0"],
},
}

def test_check_unit_consistency_default_mode_negative(self, enzmldoc):
Expand Down Expand Up @@ -180,4 +191,8 @@ def test_check_unit_consistency_default_mode_negative(self, enzmldoc):
"c0",
],
},
"volume_units": {
"l": ["m0/p0", "m0/p0/repl_p0_0", "m0/s0", "m0/s0/repl_s0_0"],
"ml": ["v0"],
},
}

0 comments on commit 35983d8

Please sign in to comment.