# -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: 2022-2023 Tanguy Fardet
# SPDX-License-Identifier: GPL-3.0-or-later
# orgmatt/nutrients/all_nutrients.py
from typing import Optional, Union, cast
import numpy as np
from pint import Quantity
from .._utils import _filter_dataframe, return_values, auto_format
from ..agrifood import nutrient_intake
from ..data import excr_content, food, nutrients_excr
from ..metabolism import fraction_nutrient_excreta
from ..typing import NumericArrayLike, NumericOrArray
from ..units import ureg, check_dim
__all__ = [
"nutrient_from_biowaste",
"nutrient_from_compound",
"nutrient_from_population",
]
[docs]
@auto_format
@check_dim(arglist=('duration', 1, '[time]'), result='[mass]*[X]')
def nutrient_from_population(
pop: NumericOrArray,
duration: Quantity,
nutrient: str,
excreta: str = "all",
ci: int = 0,
q: Optional[NumericArrayLike] = None,
**kwargs
) -> Quantity:
'''
Compute the amount of a nutrient generated by a given population over a
certain duration using the ``nutrients_intake.csv`` or
``nutrients_excreted.csv`` database.
.. warning ::
Current results only work for adults, results for children and
adolescents will be (strongly) overestimated.
Parameters
----------
pop : float or numpy array
Number of inhabitants.
duration : float [time]
Time interval over which the nitrogen is excreted.
nutrient : str
Chemical symbol for the nutrient (among 'N', 'P', 'K', 'Mg', 'Ca')
excreta : str (optional, default: "all")
Type of excreta ("urine", "feces", or "all").
ci : int (optional, default: 0)
Confidence interval (CI). If non-zero, the function will also return
the low and high expected values corresponding to that CI.
q : array-like of floats (optional: default: None)
Percentiles to compute (supersedes `ci`).
**kwargs : arguments to use a subset of the database
Additional arguments can be use to restrict the results to a subset of
the full database. E.g. one can add `sex="male"` to return only
nitrogen excretion from male subjects, or `region="South Asia"` to use
only values obtained from studies in South Asian countries.
Returns
-------
nutrient_mass : float or array of dimension D [mass]*[nutrient]
Mean mass of excreted nitrogen if `q` is None or `ci` is 0. If `q` is
provided, returns the values associated to each percentile in `q` for
each entry in `pop`; otherwise one (low, mean, high) result per entry
in `pop`, giving the masses expected for the requested confidence
interval.
The dimension D is either 3, len(q), (len(pop), 3), or
(len(pop), len(q)) depending on `ci` and `q`.
'''
if excreta.lower() == "all":
# account for nutrient pathways besides exretions
factor = fraction_nutrient_excreta(nutrient, **kwargs)
return factor * nutrient_intake(
pop, duration, nutrient, ci=ci, **kwargs)
df = nutrients_excr[nutrients_excr.compound == nutrient]
unit = df.iloc[0].unit
df = _filter_dataframe(df, kwargs)
if excreta.lower() == "urine":
res = return_values(df[df.flow == "urine"], "amount", pop, ci, q)
else:
res = return_values(df[df.flow == "feces"], "amount", pop, ci, q)
return ureg(unit) * duration * res
[docs]
@auto_format(default_group=None)
@check_dim(arglist=('mass', 0, '[mass]'), result='[mass]*[X]')
def nutrient_from_biowaste(
mass: Quantity,
nutrient: str,
waste_type: str = "BiodegradableWaste",
ci: int = 0,
q: Optional[NumericArrayLike] = None,
**kwargs
) -> Quantity:
'''
Compute the amount of a nutrient contained in biowaste (food residues, i.e.
kitchen and food waste, by default) using the ``food.csv`` database.
Parameters
----------
mass : pint.Quantity [mass]
Mass of biowaste.
nutrient : str
Chemical symbol for the nutrient (among 'N', 'P', 'K')
waste_type : str (optional, default: "BiodegradableWaste")
Type of biowaste, among "BiodegradableWaste" for all household
biowaste, including some paper; cardboard, compostable plastics, and
some green waste, "GreenWaste", for plants and garden wastes,
"FoodWaste", for all unconsumed edible food, "KitchenWaste" (peels,
bones...), and "FoodResidues", which includes food and kitchen waste.
ci : int (optional, default: 0)
Confidence interval (CI). If non-zero, the function will also return
the low and high expected values corresponding to that CI.
q : array-like of floats (optional: default: None)
Percentiles to compute (supersedes `ci`).
**kwargs : arguments to use a subset of the database
Additional arguments can be use to restrict the results to a subset of
the full database. E.g. one can add `region="Europe"` to use only
values obtained from studies in European countries.
Returns
-------
nutrient_mass : float or array of dimension D [mass]*[nutrient]
Mean mass of excreted nutrient if `q` is None or `ci` is 0. If `q` is
provided, returns the values associated to each percentile in `q` for
each entry in `amount`; otherwise one (low, mean, high) result per
entry in `amount`, giving the masses expected for the requested
confidence interval.
The dimension D is either 3, len(q), (len(amount), 3), or
(len(amount), len(q)) depending on `ci` and `q`.
'''
assert nutrient in ("TOC", "N", "P", "K"), \
"`nutrient` must be either 'N', 'P', 'K', or 'TOC'."
df = food[
(food.property == nutrient) & (food.type == _bw[waste_type])
]
unit = df.iloc[0].unit
# remove potential duplicate entry for type vs waste_type argument
kwargs = kwargs.copy()
if "type" in kwargs:
del kwargs["type"]
df = _filter_dataframe(df, kwargs)
return ureg(unit) * return_values(df, "value", mass, ci, q)
[docs]
@auto_format
@check_dim(arglist=('amount', 0, '[length]**3|[mass]'), result='[mass]*[X]')
def nutrient_from_compound(
amount: Quantity,
nutrient: str,
compound_type: str,
ci: int = 0,
q: Optional[NumericArrayLike] = None,
**kwargs
) -> Quantity:
'''
Compute the amount of nutrients contained in a specific compound.
Parameters
----------
amount: pint.Quantity
Amount of compound.
nutrient: str
Type of nutrient to return.
compound_type: str
Type of compound among "urine", "feces" = "feces (wet)",
"feces (dry)", "biowate" = "BiodegradableWaste",
"food residues" = "FoodResidues", "food waste" = "FoodWaste".
ci : int (optional, default: 0)
Confidence interval (CI). If non-zero, the function will also return
the low and high expected values corresponding to that CI.
q : array-like of floats (optional: default: None)
Percentiles to compute (supersedes `ci`).
**kwargs : arguments to use a subset of the database
Additional arguments can be use to restrict the results to a subset of
the full database. E.g. one can add `region="Europe"` to use only
values obtained from studies in European countries.
Returns
-------
nutrient_mass : float or array of dimension D [mass]*[nutrient]
Mean mass of excreted nutrient if `q` is None or `ci` is 0. If `q` is
provided, returns the values associated to each percentile in `q` for
each entry in `amount`; otherwise one (low, mean, high) result per
entry in `amount`, giving the masses expected for the requested
confidence interval.
The dimension D is either 3, len(q), (len(amount), 3), or
(len(amount), len(q)) depending on `ci` and `q`.
'''
assert compound_type in _valid_compounds, \
f"Invalid `compound_type`: {compound_type}."
kwargs = kwargs.copy()
if compound_type in _bw:
if "group" in kwargs:
del kwargs["group"]
return nutrient_from_biowaste(
amount, nutrient, waste_type=_bw[compound_type], ci=ci, **kwargs)
ct = _excr.get(compound_type, compound_type)
df = excr_content[
(excr_content.compound == nutrient) & (excr_content.excreta == ct)
]
if df.empty:
return np.NaN*ureg(f"g{nutrient}")
unit = df.iloc[0].unit
for k, v in kwargs.items():
assert k in df, f"Unknown database entry '{k}'."
df = df[df[k] == v]
# compute nutrient mass
nutrient_content = return_values(df, "content", amount, ci, q)
return ureg(unit) * nutrient_content
# argument conversion
_bw = {
"biowaste": "BiodegradableWaste",
"BiodegradableWaste": "BiodegradableWaste",
"food": "FoodResidues",
"FoodResidues": "FoodResidues",
"food waste": "FoodWaste",
"FoodWaste": "FoodWaste"
}
_excr = {"feces": "feces (wet)"}
_valid_compounds = set(_bw).union(
tuple(_bw.values()) +
("feces", "feces (wet)", "feces (dry)", "urine")
)