From e5087dff1a30836bc3dfc4d967beccabe9ab87f6 Mon Sep 17 00:00:00 2001 From: uliw Date: Thu, 5 Nov 2020 16:21:27 -0500 Subject: [PATCH] Rate constants can now be of type quantity, or string --- README.org | 5 --- build/lib/esbmtk/base_class.py | 17 +------- build/lib/esbmtk/connections.py | 73 +++++++++++++++++++++++++-------- esbmtk.egg-info/requires.txt | 5 --- esbmtk/base_class.org | 17 +------- esbmtk/base_class.py | 17 +------- esbmtk/connections.org | 73 +++++++++++++++++++++++++-------- esbmtk/connections.py | 73 +++++++++++++++++++++++++-------- setup.py | 16 +------- 9 files changed, 176 insertions(+), 120 deletions(-) diff --git a/README.org b/README.org index 4978275f..d64add1a 100644 --- a/README.org +++ b/README.org @@ -46,14 +46,9 @@ ESBMTK relies on the following python versions and libraries - matplotlib - numpy - pandas - - logging - typing - - builtins - nptyping - - numbers - pint - - copy - - time If you work with conda, it is recommended to install the above via conda. If you work with pip, the installer should install these diff --git a/build/lib/esbmtk/base_class.py b/build/lib/esbmtk/base_class.py index 1c5449cc..a898e045 100644 --- a/build/lib/esbmtk/base_class.py +++ b/build/lib/esbmtk/base_class.py @@ -97,20 +97,6 @@ def __validateinput__(self, kwargs: Dict[str, any]) -> None: # check if key values are of correct type self.__checktypes__(self.lkk, self.kwargs) - # this wont work since we don't know the model yet. - # coulde be moved into a post init section? - - # register instance on the list of model objects - #if not type(self) == Model: - # self.mo.lmo.append(self) # register on the list of model objects - - # start log entry - #logfile = self.name+".log" - #logging.basicConfig(filename=logfile, - # filemode='w', - # format='%(message)s', - # level=logging.INFO) - #logging.info(f"{self.name} initialized on {time.ctime()}") def __checktypes__(self, av: Dict[any, any], pv: Dict[any, any]) -> None: """ this method will use the the dict key in the user provided @@ -129,8 +115,7 @@ def __checktypes__(self, av: Dict[any, any], pv: Dict[any, any]) -> None: for k, v in pv.items(): # check av if provided value v is of correct type if not isinstance(v, av[k]): - print(f"Offending Key = {k}") - raise TypeError(f"{v} must be {m[k]}, not {type(v)}") + raise TypeError(f"{type(v)} is the wrong type for '{k}', should be '{av[k]}'") def __initerrormessages__(self): """ Init the list of known error messages""" diff --git a/build/lib/esbmtk/connections.py b/build/lib/esbmtk/connections.py index b70a1772..7b754d4b 100644 --- a/build/lib/esbmtk/connections.py +++ b/build/lib/esbmtk/connections.py @@ -39,6 +39,7 @@ from .base_class import esbmtkBase + class Connect(esbmtkBase): """Name: @@ -76,7 +77,6 @@ class Connect(esbmtkBase): Currently reckonized flux properties: delta, rate, alpha, species, k_value, k_mass, k_concentration, ref_value, """ - def __init__(self, **kwargs): """ The init method of the connector obbjects performs sanity checks e.g.: - whether the reservoirs exist @@ -93,9 +93,9 @@ def __init__(self, **kwargs): pl[optional] = optional processes : list """ - + from . import ureg, Q_ - + # provide a dict of all known keywords and their type self.lkk: Dict[str, any] = { "name": str, @@ -103,7 +103,7 @@ def __init__(self, **kwargs): "source": (Source, Reservoir), "sink": (Sink, Reservoir), "delta": Number, - "rate": str, + "rate": (str, Number), "pl": list, "alpha": Number, "species": Species, @@ -112,9 +112,9 @@ def __init__(self, **kwargs): "react_with": Flux, "ratio": Number, "scale": Number, - "k_concentration": Number, - "k_mass": Number, - "ref_value": Number, + "k_concentration": (str, Number, Q_), + "k_mass": (str, Number, Q_), + "ref_value": (str, Number, Q_), "k_value": Number, "a_value": Number, "b_value": Number, @@ -138,7 +138,7 @@ def __init__(self, **kwargs): "k_mass": "a number", "k_value": "a number", "a_value": "a number", - "ref_value": "a number", + "ref_value": "a number, string, or quantity", "b_value": "a number", "name": "a string", "id": "a string", @@ -149,8 +149,6 @@ def __init__(self, **kwargs): if not 'pl' in kwargs: self.pl: list[Process] = [] - - # legacy names self.influx: int = 1 self.outflux: int = -1 @@ -215,7 +213,7 @@ def register_fluxes(self) -> None: n = self.r1.n + '_to_' + self.r2.n # derive flux unit from species obbject - funit = self.sp.mu + "/" + str(self.sp.mo.bu) # xxx + funit = self.sp.mu + "/" + str(self.sp.mo.bu) # xxx print(f"r = {r}") self.fh = Flux( @@ -405,14 +403,39 @@ def __alpha__(self) -> None: self.pl.append(ph) # def __rateconstant__(self) -> None: - """ Add rate constant process""" + """ Add rate constant type process + + """ + + from . import ureg, Q_ if "rate" not in self.kwargs: raise ValueError( "The rate constant process requires that the flux rate for this reservoir is being set explicitly" ) + # k_concentration, k_mass and ref_value can be a number, a unit string, or a quantity + # if unit - convert into qauntity + # if quantity convert into number + if "k_concentration" in self.kwargs: + if isinstance(self.k_concentration,str): + self.k_concentration = Q_(self.k_concentration) + + if "k_mass" in self.kwargs: + if isinstance(self.k_mass,str): + self.k_mass = Q_(self.k_mass) + + if "ref_value" in self.kwargs: + if isinstance(self.ref_value,str): + self.ref_value = Q_(self.ref_value) + if "k_concentration" in self.kwargs and "ref_value" in self.kwargs: + # if necessary, map units + if isinstance(self.k_concentration, Q_): + self.k_concentration = self.k_concentration.to(self.mo.c_unit).magnitude + if isinstance(self.ref_value, Q_): + self.ref_value = self.ref_value.to(self.mo.c_unit).magnitude + ph = ScaleRelativeToNormalizedConcentration( name=self.pn + "_PknC", reservoir=self.r, @@ -421,6 +444,12 @@ def __rateconstant__(self) -> None: k_value=self.k_concentration) elif "k_mass" in self.kwargs and "ref_value" in self.kwargs: + # if necessary, map units + if isinstance(self.k_mass, Q_): + self.k_mass = self.k_mass.to(self.mo.m_unit).magnitude + if isinstance(self.ref_value, Q_): + self.ref_value = self.ref_value.to(self.mo.m_unit).magnitude + ph = ScaleRelativeToNormalizedMass(name=self.pn + "_PknM", reservoir=self.r, flux=self.fh, @@ -428,12 +457,20 @@ def __rateconstant__(self) -> None: k_value=self.k_mass) elif "k_mass" in self.kwargs and not "ref_value" in self.kwargs: + # if necessary, map units + if isinstance(self.k_mass, Q_): + self.k_mass = self.k_mass.to(self.mo.m_unit).magnitude + ph = ScaleRelativeToMass(name=self.pn + "_PkM", reservoir=self.r, flux=self.fh, k_value=self.k_mass) elif "k_concentration" in self.kwargs and not "ref_value" in self.kwargs: + # if necessary, map units + if isinstance(self.k_concentration, Q_): + self.k_concentration = self.k_concentration.to(self.mo.c_unit).magnitude + ph = ScaleRelativeToConcentration(name=self.pn + "_PkC", reservoir=self.r, flux=self.fh, @@ -860,8 +897,11 @@ class RateConstant(Process): """ def __init__(self, **kwargs: Dict[str, any]) -> None: - """ Initialize this Process """ + """ Initialize this Process + """ + + from . import ureg, Q_ # Note that self.lkk values also need to be added to the lkk # list of the connector object. @@ -872,7 +912,7 @@ def __init__(self, **kwargs: Dict[str, any]) -> None: # update the allowed keywords self.lkk = { "k_value": Number, - "ref_value": Number, + "ref_value": (Number,str, Q_), "name": str, "reservoir": Reservoir, "flux": Flux, @@ -1045,7 +1085,8 @@ def __init__(self, **kwargs: Dict[str, any]) -> None: """ """ - + + from . import ureg, Q_ """ Initialize this Process """ # get default names and update list for this Process @@ -1055,7 +1096,7 @@ def __init__(self, **kwargs: Dict[str, any]) -> None: self.lkk = { "a_value": Number, "b_value": Number, - "ref_value": Number, + "ref_value": (Number,str, Q_), "name": str, "reservoir": Reservoir, "flux": Flux, diff --git a/esbmtk.egg-info/requires.txt b/esbmtk.egg-info/requires.txt index 3d3c4c83..5d649399 100644 --- a/esbmtk.egg-info/requires.txt +++ b/esbmtk.egg-info/requires.txt @@ -3,9 +3,4 @@ typing numpy pandas matplotlib -builtins -logging -numbers pint -copy -time diff --git a/esbmtk/base_class.org b/esbmtk/base_class.org index 23ca70d6..0c7f7183 100644 --- a/esbmtk/base_class.org +++ b/esbmtk/base_class.org @@ -111,20 +111,6 @@ class esbmtkBase(): # check if key values are of correct type self.__checktypes__(self.lkk, self.kwargs) - # this wont work since we don't know the model yet. - # coulde be moved into a post init section? - - # register instance on the list of model objects - #if not type(self) == Model: - # self.mo.lmo.append(self) # register on the list of model objects - - # start log entry - #logfile = self.name+".log" - #logging.basicConfig(filename=logfile, - # filemode='w', - # format='%(message)s', - # level=logging.INFO) - #logging.info(f"{self.name} initialized on {time.ctime()}") def __checktypes__(self, av: Dict[any, any], pv: Dict[any, any]) -> None: """ this method will use the the dict key in the user provided @@ -143,8 +129,7 @@ class esbmtkBase(): for k, v in pv.items(): # check av if provided value v is of correct type if not isinstance(v, av[k]): - print(f"Offending Key = {k}") - raise TypeError(f"{v} must be {m[k]}, not {type(v)}") + raise TypeError(f"{type(v)} is the wrong type for '{k}', should be '{av[k]}'") def __initerrormessages__(self): """ Init the list of known error messages""" diff --git a/esbmtk/base_class.py b/esbmtk/base_class.py index 1c5449cc..a898e045 100644 --- a/esbmtk/base_class.py +++ b/esbmtk/base_class.py @@ -97,20 +97,6 @@ def __validateinput__(self, kwargs: Dict[str, any]) -> None: # check if key values are of correct type self.__checktypes__(self.lkk, self.kwargs) - # this wont work since we don't know the model yet. - # coulde be moved into a post init section? - - # register instance on the list of model objects - #if not type(self) == Model: - # self.mo.lmo.append(self) # register on the list of model objects - - # start log entry - #logfile = self.name+".log" - #logging.basicConfig(filename=logfile, - # filemode='w', - # format='%(message)s', - # level=logging.INFO) - #logging.info(f"{self.name} initialized on {time.ctime()}") def __checktypes__(self, av: Dict[any, any], pv: Dict[any, any]) -> None: """ this method will use the the dict key in the user provided @@ -129,8 +115,7 @@ def __checktypes__(self, av: Dict[any, any], pv: Dict[any, any]) -> None: for k, v in pv.items(): # check av if provided value v is of correct type if not isinstance(v, av[k]): - print(f"Offending Key = {k}") - raise TypeError(f"{v} must be {m[k]}, not {type(v)}") + raise TypeError(f"{type(v)} is the wrong type for '{k}', should be '{av[k]}'") def __initerrormessages__(self): """ Init the list of known error messages""" diff --git a/esbmtk/connections.org b/esbmtk/connections.org index 9d78c492..e8dab64f 100644 --- a/esbmtk/connections.org +++ b/esbmtk/connections.org @@ -93,6 +93,7 @@ The init method of the connector obbjects performs sanity checks e.g.: #+BEGIN_SRC ipython :tangle connections.py from .base_class import esbmtkBase + class Connect(esbmtkBase): """Name: @@ -130,7 +131,6 @@ class Connect(esbmtkBase): Currently reckonized flux properties: delta, rate, alpha, species, k_value, k_mass, k_concentration, ref_value, """ - def __init__(self, **kwargs): """ The init method of the connector obbjects performs sanity checks e.g.: - whether the reservoirs exist @@ -147,9 +147,9 @@ class Connect(esbmtkBase): pl[optional] = optional processes : list """ - + from . import ureg, Q_ - + # provide a dict of all known keywords and their type self.lkk: Dict[str, any] = { "name": str, @@ -157,7 +157,7 @@ class Connect(esbmtkBase): "source": (Source, Reservoir), "sink": (Sink, Reservoir), "delta": Number, - "rate": str, + "rate": (str, Number), "pl": list, "alpha": Number, "species": Species, @@ -166,9 +166,9 @@ class Connect(esbmtkBase): "react_with": Flux, "ratio": Number, "scale": Number, - "k_concentration": Number, - "k_mass": Number, - "ref_value": Number, + "k_concentration": (str, Number, Q_), + "k_mass": (str, Number, Q_), + "ref_value": (str, Number, Q_), "k_value": Number, "a_value": Number, "b_value": Number, @@ -192,7 +192,7 @@ class Connect(esbmtkBase): "k_mass": "a number", "k_value": "a number", "a_value": "a number", - "ref_value": "a number", + "ref_value": "a number, string, or quantity", "b_value": "a number", "name": "a string", "id": "a string", @@ -203,8 +203,6 @@ class Connect(esbmtkBase): if not 'pl' in kwargs: self.pl: list[Process] = [] - - # legacy names self.influx: int = 1 self.outflux: int = -1 @@ -269,7 +267,7 @@ class Connect(esbmtkBase): n = self.r1.n + '_to_' + self.r2.n # derive flux unit from species obbject - funit = self.sp.mu + "/" + str(self.sp.mo.bu) # xxx + funit = self.sp.mu + "/" + str(self.sp.mo.bu) # xxx print(f"r = {r}") self.fh = Flux( @@ -459,14 +457,39 @@ class Connect(esbmtkBase): self.pl.append(ph) # def __rateconstant__(self) -> None: - """ Add rate constant process""" + """ Add rate constant type process + + """ + + from . import ureg, Q_ if "rate" not in self.kwargs: raise ValueError( "The rate constant process requires that the flux rate for this reservoir is being set explicitly" ) + # k_concentration, k_mass and ref_value can be a number, a unit string, or a quantity + # if unit - convert into qauntity + # if quantity convert into number + if "k_concentration" in self.kwargs: + if isinstance(self.k_concentration,str): + self.k_concentration = Q_(self.k_concentration) + + if "k_mass" in self.kwargs: + if isinstance(self.k_mass,str): + self.k_mass = Q_(self.k_mass) + + if "ref_value" in self.kwargs: + if isinstance(self.ref_value,str): + self.ref_value = Q_(self.ref_value) + if "k_concentration" in self.kwargs and "ref_value" in self.kwargs: + # if necessary, map units + if isinstance(self.k_concentration, Q_): + self.k_concentration = self.k_concentration.to(self.mo.c_unit).magnitude + if isinstance(self.ref_value, Q_): + self.ref_value = self.ref_value.to(self.mo.c_unit).magnitude + ph = ScaleRelativeToNormalizedConcentration( name=self.pn + "_PknC", reservoir=self.r, @@ -475,6 +498,12 @@ class Connect(esbmtkBase): k_value=self.k_concentration) elif "k_mass" in self.kwargs and "ref_value" in self.kwargs: + # if necessary, map units + if isinstance(self.k_mass, Q_): + self.k_mass = self.k_mass.to(self.mo.m_unit).magnitude + if isinstance(self.ref_value, Q_): + self.ref_value = self.ref_value.to(self.mo.m_unit).magnitude + ph = ScaleRelativeToNormalizedMass(name=self.pn + "_PknM", reservoir=self.r, flux=self.fh, @@ -482,12 +511,20 @@ class Connect(esbmtkBase): k_value=self.k_mass) elif "k_mass" in self.kwargs and not "ref_value" in self.kwargs: + # if necessary, map units + if isinstance(self.k_mass, Q_): + self.k_mass = self.k_mass.to(self.mo.m_unit).magnitude + ph = ScaleRelativeToMass(name=self.pn + "_PkM", reservoir=self.r, flux=self.fh, k_value=self.k_mass) elif "k_concentration" in self.kwargs and not "ref_value" in self.kwargs: + # if necessary, map units + if isinstance(self.k_concentration, Q_): + self.k_concentration = self.k_concentration.to(self.mo.c_unit).magnitude + ph = ScaleRelativeToConcentration(name=self.pn + "_PkC", reservoir=self.r, flux=self.fh, @@ -966,8 +1003,11 @@ class RateConstant(Process): """ def __init__(self, **kwargs: Dict[str, any]) -> None: - """ Initialize this Process """ + """ Initialize this Process + """ + + from . import ureg, Q_ # Note that self.lkk values also need to be added to the lkk # list of the connector object. @@ -978,7 +1018,7 @@ class RateConstant(Process): # update the allowed keywords self.lkk = { "k_value": Number, - "ref_value": Number, + "ref_value": (Number,str, Q_), "name": str, "reservoir": Reservoir, "flux": Flux, @@ -1155,7 +1195,8 @@ class Monod(Process): """ """ - + + from . import ureg, Q_ """ Initialize this Process """ # get default names and update list for this Process @@ -1165,7 +1206,7 @@ class Monod(Process): self.lkk = { "a_value": Number, "b_value": Number, - "ref_value": Number, + "ref_value": (Number,str, Q_), "name": str, "reservoir": Reservoir, "flux": Flux, diff --git a/esbmtk/connections.py b/esbmtk/connections.py index b70a1772..7b754d4b 100644 --- a/esbmtk/connections.py +++ b/esbmtk/connections.py @@ -39,6 +39,7 @@ from .base_class import esbmtkBase + class Connect(esbmtkBase): """Name: @@ -76,7 +77,6 @@ class Connect(esbmtkBase): Currently reckonized flux properties: delta, rate, alpha, species, k_value, k_mass, k_concentration, ref_value, """ - def __init__(self, **kwargs): """ The init method of the connector obbjects performs sanity checks e.g.: - whether the reservoirs exist @@ -93,9 +93,9 @@ def __init__(self, **kwargs): pl[optional] = optional processes : list """ - + from . import ureg, Q_ - + # provide a dict of all known keywords and their type self.lkk: Dict[str, any] = { "name": str, @@ -103,7 +103,7 @@ def __init__(self, **kwargs): "source": (Source, Reservoir), "sink": (Sink, Reservoir), "delta": Number, - "rate": str, + "rate": (str, Number), "pl": list, "alpha": Number, "species": Species, @@ -112,9 +112,9 @@ def __init__(self, **kwargs): "react_with": Flux, "ratio": Number, "scale": Number, - "k_concentration": Number, - "k_mass": Number, - "ref_value": Number, + "k_concentration": (str, Number, Q_), + "k_mass": (str, Number, Q_), + "ref_value": (str, Number, Q_), "k_value": Number, "a_value": Number, "b_value": Number, @@ -138,7 +138,7 @@ def __init__(self, **kwargs): "k_mass": "a number", "k_value": "a number", "a_value": "a number", - "ref_value": "a number", + "ref_value": "a number, string, or quantity", "b_value": "a number", "name": "a string", "id": "a string", @@ -149,8 +149,6 @@ def __init__(self, **kwargs): if not 'pl' in kwargs: self.pl: list[Process] = [] - - # legacy names self.influx: int = 1 self.outflux: int = -1 @@ -215,7 +213,7 @@ def register_fluxes(self) -> None: n = self.r1.n + '_to_' + self.r2.n # derive flux unit from species obbject - funit = self.sp.mu + "/" + str(self.sp.mo.bu) # xxx + funit = self.sp.mu + "/" + str(self.sp.mo.bu) # xxx print(f"r = {r}") self.fh = Flux( @@ -405,14 +403,39 @@ def __alpha__(self) -> None: self.pl.append(ph) # def __rateconstant__(self) -> None: - """ Add rate constant process""" + """ Add rate constant type process + + """ + + from . import ureg, Q_ if "rate" not in self.kwargs: raise ValueError( "The rate constant process requires that the flux rate for this reservoir is being set explicitly" ) + # k_concentration, k_mass and ref_value can be a number, a unit string, or a quantity + # if unit - convert into qauntity + # if quantity convert into number + if "k_concentration" in self.kwargs: + if isinstance(self.k_concentration,str): + self.k_concentration = Q_(self.k_concentration) + + if "k_mass" in self.kwargs: + if isinstance(self.k_mass,str): + self.k_mass = Q_(self.k_mass) + + if "ref_value" in self.kwargs: + if isinstance(self.ref_value,str): + self.ref_value = Q_(self.ref_value) + if "k_concentration" in self.kwargs and "ref_value" in self.kwargs: + # if necessary, map units + if isinstance(self.k_concentration, Q_): + self.k_concentration = self.k_concentration.to(self.mo.c_unit).magnitude + if isinstance(self.ref_value, Q_): + self.ref_value = self.ref_value.to(self.mo.c_unit).magnitude + ph = ScaleRelativeToNormalizedConcentration( name=self.pn + "_PknC", reservoir=self.r, @@ -421,6 +444,12 @@ def __rateconstant__(self) -> None: k_value=self.k_concentration) elif "k_mass" in self.kwargs and "ref_value" in self.kwargs: + # if necessary, map units + if isinstance(self.k_mass, Q_): + self.k_mass = self.k_mass.to(self.mo.m_unit).magnitude + if isinstance(self.ref_value, Q_): + self.ref_value = self.ref_value.to(self.mo.m_unit).magnitude + ph = ScaleRelativeToNormalizedMass(name=self.pn + "_PknM", reservoir=self.r, flux=self.fh, @@ -428,12 +457,20 @@ def __rateconstant__(self) -> None: k_value=self.k_mass) elif "k_mass" in self.kwargs and not "ref_value" in self.kwargs: + # if necessary, map units + if isinstance(self.k_mass, Q_): + self.k_mass = self.k_mass.to(self.mo.m_unit).magnitude + ph = ScaleRelativeToMass(name=self.pn + "_PkM", reservoir=self.r, flux=self.fh, k_value=self.k_mass) elif "k_concentration" in self.kwargs and not "ref_value" in self.kwargs: + # if necessary, map units + if isinstance(self.k_concentration, Q_): + self.k_concentration = self.k_concentration.to(self.mo.c_unit).magnitude + ph = ScaleRelativeToConcentration(name=self.pn + "_PkC", reservoir=self.r, flux=self.fh, @@ -860,8 +897,11 @@ class RateConstant(Process): """ def __init__(self, **kwargs: Dict[str, any]) -> None: - """ Initialize this Process """ + """ Initialize this Process + """ + + from . import ureg, Q_ # Note that self.lkk values also need to be added to the lkk # list of the connector object. @@ -872,7 +912,7 @@ def __init__(self, **kwargs: Dict[str, any]) -> None: # update the allowed keywords self.lkk = { "k_value": Number, - "ref_value": Number, + "ref_value": (Number,str, Q_), "name": str, "reservoir": Reservoir, "flux": Flux, @@ -1045,7 +1085,8 @@ def __init__(self, **kwargs: Dict[str, any]) -> None: """ """ - + + from . import ureg, Q_ """ Initialize this Process """ # get default names and update list for this Process @@ -1055,7 +1096,7 @@ def __init__(self, **kwargs: Dict[str, any]) -> None: self.lkk = { "a_value": Number, "b_value": Number, - "ref_value": Number, + "ref_value": (Number,str, Q_), "name": str, "reservoir": Reservoir, "flux": Flux, diff --git a/setup.py b/setup.py index ca7de1c4..d481cd44 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="esbmtk", # Replace with your own username - version="0.2.0", + version="0.2.0.4", author="Ulrich G. Wortmann", author_email="uli.wortmann@utoronto.ca", description="An Earth Sciences Box Modeling Toolkit", @@ -19,17 +19,5 @@ "Operating System :: OS Independent", ], python_requires=">=3.6", - install_requires=[ - "nptyping", - "typing", - "numpy", - "pandas", - "matplotlib", - "builtins", - "logging", - "numbers", - "pint", - "copy", - "time", - ], + install_requires=["nptyping", "typing", "numpy", "pandas", "matplotlib", "pint",], )