hestia-earth-models 0.64.1__py3-none-any.whl → 0.64.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hestia-earth-models might be problematic. Click here for more details.
- hestia_earth/models/agribalyse2016/machineryInfrastructureDepreciatedAmountPerCycle.py +2 -2
- hestia_earth/models/cycle/animal/input/hestiaAggregatedData.py +5 -2
- hestia_earth/models/cycle/animal/input/properties.py +2 -1
- hestia_earth/models/cycle/animal/milkYield.py +2 -1
- hestia_earth/models/cycle/concentrateFeed.py +19 -10
- hestia_earth/models/cycle/cycleDuration.py +4 -5
- hestia_earth/models/cycle/siteDuration.py +15 -5
- hestia_earth/models/cycle/startDateDefinition.py +3 -4
- hestia_earth/models/cycle/stockingDensityAnimalHousingAverage.py +52 -0
- hestia_earth/models/ipcc2019/aboveGroundBiomass.py +762 -0
- hestia_earth/models/ipcc2019/aboveGroundBiomass_utils.py +180 -0
- hestia_earth/models/ipcc2019/animal/liveweightGain.py +88 -0
- hestia_earth/models/ipcc2019/animal/liveweightPerHead.py +88 -0
- hestia_earth/models/ipcc2019/animal/pastureGrass.py +51 -42
- hestia_earth/models/ipcc2019/animal/utils.py +20 -0
- hestia_earth/models/ipcc2019/animal/weightAtMaturity.py +10 -15
- hestia_earth/models/ipcc2019/ch4ToAirAquacultureSystems.py +96 -0
- hestia_earth/models/ipcc2019/ch4ToAirExcreta.py +2 -2
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +37 -50
- hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +0 -19
- hestia_earth/models/ipcc2019/pastureGrass.py +44 -31
- hestia_earth/models/ipcc2019/pastureGrass_utils.py +38 -22
- hestia_earth/models/mocking/search-results.json +489 -249
- hestia_earth/models/poschEtAl2008/terrestrialEutrophicationPotentialAccumulatedExceedance.py +40 -0
- hestia_earth/models/utils/blank_node.py +20 -1
- hestia_earth/models/utils/crop.py +4 -0
- hestia_earth/models/utils/ecoClimateZone.py +99 -0
- hestia_earth/models/utils/productivity.py +1 -1
- hestia_earth/models/utils/property.py +2 -2
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.64.1.dist-info → hestia_earth_models-0.64.3.dist-info}/METADATA +1 -1
- {hestia_earth_models-0.64.1.dist-info → hestia_earth_models-0.64.3.dist-info}/RECORD +47 -31
- tests/models/cycle/test_siteDuration.py +22 -0
- tests/models/cycle/test_stockingDensityAnimalHousingAverage.py +42 -0
- tests/models/ipcc2019/animal/test_liveweightGain.py +20 -0
- tests/models/ipcc2019/animal/test_liveweightPerHead.py +20 -0
- tests/models/ipcc2019/animal/test_pastureGrass.py +1 -1
- tests/models/ipcc2019/test_aboveGroundBiomass.py +182 -0
- tests/models/ipcc2019/test_aboveGroundBiomass_utils.py +92 -0
- tests/models/ipcc2019/test_ch4ToAirAquacultureSystems.py +60 -0
- tests/models/ipcc2019/test_organicCarbonPerHa_tier_1_utils.py +3 -2
- tests/models/ipcc2019/test_pastureGrass.py +2 -2
- tests/models/poschEtAl2008/test_terrestrialEutrophicationPotentialAccumulatedExceedance.py +44 -0
- tests/models/utils/test_ecoClimateZone.py +152 -0
- {hestia_earth_models-0.64.1.dist-info → hestia_earth_models-0.64.3.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.64.1.dist-info → hestia_earth_models-0.64.3.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.64.1.dist-info → hestia_earth_models-0.64.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from numpy import random
|
|
3
|
+
from numpy.typing import NDArray
|
|
4
|
+
from typing import Callable, Optional, Union
|
|
5
|
+
|
|
6
|
+
from hestia_earth.models.utils.array_builders import repeat_single, truncated_normal_1d
|
|
7
|
+
from hestia_earth.models.utils.ecoClimateZone import EcoClimateZone, get_ecoClimateZone_lookup_grouped_value
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BiomassCategory(Enum):
|
|
11
|
+
"""
|
|
12
|
+
Enum representing biomass categories, sourced from IPCC (2006), IPCC (2019) and European Commission (2010).
|
|
13
|
+
|
|
14
|
+
Enum values formatted for logging as table.
|
|
15
|
+
"""
|
|
16
|
+
ANNUAL_CROPS = "annual-crops"
|
|
17
|
+
COCONUT = "coconut" # European Commission (2010)
|
|
18
|
+
FOREST = "forest" # IPCC (2019) recalculated per eco-climate zone
|
|
19
|
+
GRASSLAND = "grassland"
|
|
20
|
+
JATROPHA = "jatropha" # European Commission (2010)
|
|
21
|
+
JOJOBA = "jojoba" # European Commission (2010)
|
|
22
|
+
NATURAL_FOREST = "natural-forest" # IPCC (2019) recalculated per eco-climate zone
|
|
23
|
+
OIL_PALM = "oil palm" # IPCC (2019)
|
|
24
|
+
OLIVE = "olive" # IPCC (2019)
|
|
25
|
+
ORCHARD = "orchard" # IPCC (2019)
|
|
26
|
+
OTHER = "other"
|
|
27
|
+
PLANTATION_FOREST = "plantation-forest" # IPCC (2019) recalculated per eco-climate zone
|
|
28
|
+
RUBBER = "rubber" # IPCC (2019)
|
|
29
|
+
SHORT_ROTATION_COPPICE = "short-rotation-coppice" # IPCC (2019)
|
|
30
|
+
TEA = "tea" # IPCC (2019)
|
|
31
|
+
VINE = "vine" # IPCC (2019)
|
|
32
|
+
WOODY_PERENNIAL = "woody-perennial" # IPCC (2006)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
BIOMASS_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE = {
|
|
36
|
+
BiomassCategory.ANNUAL_CROPS: "Annual crops",
|
|
37
|
+
BiomassCategory.COCONUT: "Coconut",
|
|
38
|
+
BiomassCategory.FOREST: "Forest",
|
|
39
|
+
BiomassCategory.GRASSLAND: "Grassland",
|
|
40
|
+
BiomassCategory.JATROPHA: "Jatropha",
|
|
41
|
+
BiomassCategory.JOJOBA: "Jojoba",
|
|
42
|
+
BiomassCategory.NATURAL_FOREST: "Natural forest",
|
|
43
|
+
BiomassCategory.OIL_PALM: "Oil palm",
|
|
44
|
+
BiomassCategory.OLIVE: "Olive",
|
|
45
|
+
BiomassCategory.ORCHARD: "Orchard",
|
|
46
|
+
BiomassCategory.OTHER: "Other",
|
|
47
|
+
BiomassCategory.PLANTATION_FOREST: "Plantation forest",
|
|
48
|
+
BiomassCategory.RUBBER: "Rubber",
|
|
49
|
+
BiomassCategory.SHORT_ROTATION_COPPICE: "Short rotation coppice",
|
|
50
|
+
BiomassCategory.TEA: "Tea",
|
|
51
|
+
BiomassCategory.VINE: "Vine",
|
|
52
|
+
BiomassCategory.WOODY_PERENNIAL: "Woody perennial"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def assign_biomass_category(lookup_value: str) -> BiomassCategory:
|
|
57
|
+
"""
|
|
58
|
+
Return the `BiomassCategory` enum member associated with the input lookup value. If lookup value is missing or
|
|
59
|
+
doesn't map to any category, return `None`.
|
|
60
|
+
"""
|
|
61
|
+
return next(
|
|
62
|
+
(key for key, value in BIOMASS_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE.items() if value == lookup_value),
|
|
63
|
+
None
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def sample_biomass_equilibrium(
|
|
68
|
+
iterations: int,
|
|
69
|
+
biomass_category: BiomassCategory,
|
|
70
|
+
eco_climate_zone: EcoClimateZone,
|
|
71
|
+
seed: Union[int, random.Generator, None] = None
|
|
72
|
+
) -> dict:
|
|
73
|
+
"""
|
|
74
|
+
Sample a biomass equilibrium using the function specified in `KWARGS_TO_SAMPLE_FUNC`.
|
|
75
|
+
|
|
76
|
+
Parameters
|
|
77
|
+
----------
|
|
78
|
+
iterations : int
|
|
79
|
+
The number of samples to take.
|
|
80
|
+
biomass_category : BiomassCategory
|
|
81
|
+
The biomass category of the land cover.
|
|
82
|
+
eco_climate_zone : EcoClimateZone
|
|
83
|
+
The eco-climate zone of the site.
|
|
84
|
+
seed : int | Generator | None, optional
|
|
85
|
+
A seed to initialize the BitGenerator. If passed a Generator, it will be returned unaltered. If `None`, then
|
|
86
|
+
fresh, unpredictable entropy will be pulled from the OS.
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
90
|
+
NDArray
|
|
91
|
+
The sampled parameter as a numpy array with shape `(1, iterations)`.
|
|
92
|
+
"""
|
|
93
|
+
kwargs = _get_biomass_equilibrium(biomass_category, eco_climate_zone)
|
|
94
|
+
func = _get_sample_func(kwargs)
|
|
95
|
+
return func(iterations=iterations, seed=seed, **kwargs)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _get_biomass_equilibrium(biomass_category: BiomassCategory, eco_climate_zone: EcoClimateZone) -> dict:
|
|
99
|
+
"""
|
|
100
|
+
Retrieve the biomass equilibrium data for a specific combination of biomass category and eco-climate zone.
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
biomass_category : BiomassCategory
|
|
105
|
+
The biomass category of the land cover.
|
|
106
|
+
eco_climate_zone : EcoClimateZone
|
|
107
|
+
The eco-climate zone of the site.
|
|
108
|
+
|
|
109
|
+
Returns
|
|
110
|
+
-------
|
|
111
|
+
dict
|
|
112
|
+
The biomass equilibrium data.
|
|
113
|
+
"""
|
|
114
|
+
return get_ecoClimateZone_lookup_grouped_value(
|
|
115
|
+
eco_climate_zone.value,
|
|
116
|
+
_build_col_name(biomass_category),
|
|
117
|
+
default={"value": 0}
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _build_col_name(biomass_category: BiomassCategory) -> str:
|
|
122
|
+
"""
|
|
123
|
+
Get the column name for the `ecoClimateZone-lookup.csv` for a specific biomass category equilibrium.
|
|
124
|
+
"""
|
|
125
|
+
COL_NAME_ROOT = "AG_BIOMASS_EQUILIBRIUM_KG_C_HECTARE_"
|
|
126
|
+
return (
|
|
127
|
+
f"{COL_NAME_ROOT}{biomass_category.name}" if isinstance(biomass_category, BiomassCategory)
|
|
128
|
+
else f"{COL_NAME_ROOT}OTHER"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _get_sample_func(kwargs: dict) -> Callable:
|
|
133
|
+
"""
|
|
134
|
+
Select the correct sample function for a parameter based on the distribution data available. All possible
|
|
135
|
+
parameters for the model should have, at a minimum, a `value`, meaning that no default function needs to be
|
|
136
|
+
specified.
|
|
137
|
+
|
|
138
|
+
This function has been extracted into it's own method to allow for mocking of sample function.
|
|
139
|
+
|
|
140
|
+
Keyword Args
|
|
141
|
+
------------
|
|
142
|
+
value : float
|
|
143
|
+
The distribution mean.
|
|
144
|
+
sd : float
|
|
145
|
+
The standard deviation of the distribution.
|
|
146
|
+
uncertainty : float
|
|
147
|
+
The +/- uncertainty of the 95% confidence interval expressed as a percentage of the mean.
|
|
148
|
+
error : float
|
|
149
|
+
Two standard deviations expressed as a percentage of the mean.
|
|
150
|
+
|
|
151
|
+
Returns
|
|
152
|
+
-------
|
|
153
|
+
Callable
|
|
154
|
+
The sample function for the distribution.
|
|
155
|
+
"""
|
|
156
|
+
return next(
|
|
157
|
+
sample_func for required_kwargs, sample_func in _KWARGS_TO_SAMPLE_FUNC.items()
|
|
158
|
+
if all(kwarg in kwargs.keys() for kwarg in required_kwargs)
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def sample_plus_minus_error(
|
|
163
|
+
*, iterations: int, value: float, error: float, seed: Optional[int] = None, **_
|
|
164
|
+
) -> NDArray:
|
|
165
|
+
"""Randomly sample a model parameter with a truncated normal distribution described using plus/minus error."""
|
|
166
|
+
sd = value * (error / 200)
|
|
167
|
+
low = value - (value * (error / 100))
|
|
168
|
+
high = value + (value * (error / 100))
|
|
169
|
+
return truncated_normal_1d(shape=(1, iterations), mu=value, sigma=sd, low=low, high=high, seed=seed)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def sample_constant(*, iterations: int, value: float, **_) -> NDArray:
|
|
173
|
+
"""Sample a constant model parameter."""
|
|
174
|
+
return repeat_single(shape=(1, iterations), value=value)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
_KWARGS_TO_SAMPLE_FUNC = {
|
|
178
|
+
("value", "error"): sample_plus_minus_error,
|
|
179
|
+
("value",): sample_constant
|
|
180
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from hestia_earth.schema import TermTermType
|
|
2
|
+
from hestia_earth.utils.model import filter_list_term_type
|
|
3
|
+
|
|
4
|
+
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
5
|
+
from hestia_earth.models.utils.blank_node import merge_blank_nodes
|
|
6
|
+
from hestia_earth.models.utils.property import _new_property, node_has_no_property
|
|
7
|
+
from .utils import productivity_lookup_value
|
|
8
|
+
from .. import MODEL
|
|
9
|
+
|
|
10
|
+
REQUIREMENTS = {
|
|
11
|
+
"Cycle": {
|
|
12
|
+
"site": {
|
|
13
|
+
"@type": "Site",
|
|
14
|
+
"country": {"@type": "Term", "termType": "region"}
|
|
15
|
+
},
|
|
16
|
+
"animals": [{
|
|
17
|
+
"@type": "Animal",
|
|
18
|
+
"term.termType": "liveAnimal",
|
|
19
|
+
"none": {
|
|
20
|
+
"properties": [{
|
|
21
|
+
"@type": "Property",
|
|
22
|
+
"value": "",
|
|
23
|
+
"term.@id": "liveweightGain"
|
|
24
|
+
}]
|
|
25
|
+
}
|
|
26
|
+
}]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
LOOKUPS = {
|
|
30
|
+
"region-liveAnimal-liveweightGain": "liveweight gain"
|
|
31
|
+
}
|
|
32
|
+
RETURNS = {
|
|
33
|
+
"Animal": [{
|
|
34
|
+
"properties": [{
|
|
35
|
+
"@type": "Property",
|
|
36
|
+
"value": ""
|
|
37
|
+
}]
|
|
38
|
+
}]
|
|
39
|
+
}
|
|
40
|
+
TERM_ID = 'liveweightGain'
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _property(value: float):
|
|
44
|
+
prop = _new_property(TERM_ID, MODEL)
|
|
45
|
+
prop['value'] = value
|
|
46
|
+
return prop
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _run_animal(data: dict):
|
|
50
|
+
animal = data.get('animal')
|
|
51
|
+
value = data.get('value')
|
|
52
|
+
return animal | {
|
|
53
|
+
'properties': merge_blank_nodes(animal.get('properties', []), [_property(value)])
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _should_run(cycle: dict):
|
|
58
|
+
country = cycle.get('site', {}).get('country', {})
|
|
59
|
+
country_id = country.get('@id')
|
|
60
|
+
live_animals = filter_list_term_type(cycle.get('animals', []), TermTermType.LIVEANIMAL)
|
|
61
|
+
live_animals = list(filter(node_has_no_property(TERM_ID), live_animals))
|
|
62
|
+
live_animals_with_value = [{
|
|
63
|
+
'animal': animal,
|
|
64
|
+
'value': productivity_lookup_value(TERM_ID, list(LOOKUPS.keys())[0], country, animal)
|
|
65
|
+
} for animal in live_animals]
|
|
66
|
+
|
|
67
|
+
def _should_run_animal(value: dict):
|
|
68
|
+
lookup_value = value.get('value')
|
|
69
|
+
term_id = value.get('animal').get('term').get('@id')
|
|
70
|
+
|
|
71
|
+
logRequirements(cycle, model=MODEL, term=term_id,
|
|
72
|
+
country_id=country_id,
|
|
73
|
+
liveweightGain=lookup_value)
|
|
74
|
+
|
|
75
|
+
should_run = all([
|
|
76
|
+
country_id,
|
|
77
|
+
lookup_value is not None
|
|
78
|
+
])
|
|
79
|
+
logShouldRun(cycle, MODEL, term_id, should_run)
|
|
80
|
+
|
|
81
|
+
return should_run
|
|
82
|
+
|
|
83
|
+
return list(filter(_should_run_animal, live_animals_with_value))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def run(cycle: dict):
|
|
87
|
+
animals = _should_run(cycle)
|
|
88
|
+
return list(map(_run_animal, animals))
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from hestia_earth.schema import TermTermType
|
|
2
|
+
from hestia_earth.utils.model import filter_list_term_type
|
|
3
|
+
|
|
4
|
+
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
5
|
+
from hestia_earth.models.utils.blank_node import merge_blank_nodes
|
|
6
|
+
from hestia_earth.models.utils.property import _new_property, node_has_no_property
|
|
7
|
+
from .utils import productivity_lookup_value
|
|
8
|
+
from .. import MODEL
|
|
9
|
+
|
|
10
|
+
REQUIREMENTS = {
|
|
11
|
+
"Cycle": {
|
|
12
|
+
"site": {
|
|
13
|
+
"@type": "Site",
|
|
14
|
+
"country": {"@type": "Term", "termType": "region"}
|
|
15
|
+
},
|
|
16
|
+
"animals": [{
|
|
17
|
+
"@type": "Animal",
|
|
18
|
+
"term.termType": "liveAnimal",
|
|
19
|
+
"none": {
|
|
20
|
+
"properties": [{
|
|
21
|
+
"@type": "Property",
|
|
22
|
+
"value": "",
|
|
23
|
+
"term.@id": "liveweightPerHead"
|
|
24
|
+
}]
|
|
25
|
+
}
|
|
26
|
+
}]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
LOOKUPS = {
|
|
30
|
+
"region-liveAnimal-liveweightPerHead": "liveweight per head"
|
|
31
|
+
}
|
|
32
|
+
RETURNS = {
|
|
33
|
+
"Animal": [{
|
|
34
|
+
"properties": [{
|
|
35
|
+
"@type": "Property",
|
|
36
|
+
"value": ""
|
|
37
|
+
}]
|
|
38
|
+
}]
|
|
39
|
+
}
|
|
40
|
+
TERM_ID = 'liveweightPerHead'
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _property(value: float):
|
|
44
|
+
prop = _new_property(TERM_ID, MODEL)
|
|
45
|
+
prop['value'] = value
|
|
46
|
+
return prop
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _run_animal(data: dict):
|
|
50
|
+
animal = data.get('animal')
|
|
51
|
+
value = data.get('value')
|
|
52
|
+
return animal | {
|
|
53
|
+
'properties': merge_blank_nodes(animal.get('properties', []), [_property(value)])
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _should_run(cycle: dict):
|
|
58
|
+
country = cycle.get('site', {}).get('country', {})
|
|
59
|
+
country_id = country.get('@id')
|
|
60
|
+
live_animals = filter_list_term_type(cycle.get('animals', []), TermTermType.LIVEANIMAL)
|
|
61
|
+
live_animals = list(filter(node_has_no_property(TERM_ID), live_animals))
|
|
62
|
+
live_animals_with_value = [{
|
|
63
|
+
'animal': animal,
|
|
64
|
+
'value': productivity_lookup_value(TERM_ID, list(LOOKUPS.keys())[0], country, animal)
|
|
65
|
+
} for animal in live_animals]
|
|
66
|
+
|
|
67
|
+
def _should_run_animal(value: dict):
|
|
68
|
+
lookup_value = value.get('value')
|
|
69
|
+
term_id = value.get('animal').get('term').get('@id')
|
|
70
|
+
|
|
71
|
+
logRequirements(cycle, model=MODEL, term=term_id,
|
|
72
|
+
country_id=country_id,
|
|
73
|
+
liveweightPerHead=lookup_value)
|
|
74
|
+
|
|
75
|
+
should_run = all([
|
|
76
|
+
country_id,
|
|
77
|
+
lookup_value is not None
|
|
78
|
+
])
|
|
79
|
+
logShouldRun(cycle, MODEL, term_id, should_run)
|
|
80
|
+
|
|
81
|
+
return should_run
|
|
82
|
+
|
|
83
|
+
return list(filter(_should_run_animal, live_animals_with_value))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def run(cycle: dict):
|
|
87
|
+
animals = _should_run(cycle)
|
|
88
|
+
return list(map(_run_animal, animals))
|
|
@@ -10,10 +10,10 @@ This version of the model will run at the Animal Blank Node level, if none of th
|
|
|
10
10
|
"""
|
|
11
11
|
from hestia_earth.schema import TermTermType
|
|
12
12
|
from hestia_earth.utils.model import filter_list_term_type
|
|
13
|
-
from hestia_earth.utils.tools import list_sum
|
|
13
|
+
from hestia_earth.utils.tools import list_sum, non_empty_list
|
|
14
14
|
|
|
15
15
|
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues, log_as_table
|
|
16
|
-
from hestia_earth.models.utils.blank_node import lookups_logs, properties_logs
|
|
16
|
+
from hestia_earth.models.utils.blank_node import lookups_logs, properties_logs, merge_blank_nodes
|
|
17
17
|
from hestia_earth.models.utils.input import _new_input
|
|
18
18
|
from hestia_earth.models.utils.term import get_wool_terms, get_lookup_value
|
|
19
19
|
from hestia_earth.models.utils.completeness import _is_term_type_complete, _is_term_type_incomplete
|
|
@@ -42,15 +42,22 @@ REQUIREMENTS = {
|
|
|
42
42
|
"@type": "Site",
|
|
43
43
|
"siteType": "permanent pasture"
|
|
44
44
|
},
|
|
45
|
-
"practices": [
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
"practices": [
|
|
46
|
+
{
|
|
47
|
+
"@type": "Practice",
|
|
48
|
+
"value": "",
|
|
49
|
+
"term.termType": "system"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"@type": "Practice",
|
|
53
|
+
"value": "",
|
|
54
|
+
"term.@id": "pastureGrass",
|
|
55
|
+
"key": {
|
|
56
|
+
"@type": "Term",
|
|
57
|
+
"term.termType": "landCover"
|
|
58
|
+
}
|
|
52
59
|
}
|
|
53
|
-
|
|
60
|
+
],
|
|
54
61
|
"animals": [{
|
|
55
62
|
"@type": "Animal",
|
|
56
63
|
"value": "> 0",
|
|
@@ -140,7 +147,8 @@ RETURNS = {
|
|
|
140
147
|
"inputs": [{
|
|
141
148
|
"@type": "Input",
|
|
142
149
|
"term.termType": ["crop", "forage"],
|
|
143
|
-
"value": ""
|
|
150
|
+
"value": "",
|
|
151
|
+
"isAnimalFeed": "True"
|
|
144
152
|
}]
|
|
145
153
|
}]
|
|
146
154
|
}
|
|
@@ -150,6 +158,7 @@ MODEL_KEY = 'pastureGrass'
|
|
|
150
158
|
def _input(term_id: str, value: float):
|
|
151
159
|
node = _new_input(term_id, MODEL)
|
|
152
160
|
node['value'] = [value]
|
|
161
|
+
node['isAnimalFeed'] = True
|
|
153
162
|
return node
|
|
154
163
|
|
|
155
164
|
|
|
@@ -187,10 +196,9 @@ def calculate_NEwool(cycle: dict, animal: dict, products: list, total_weight: fl
|
|
|
187
196
|
return total_energy * animal_weight/total_weight
|
|
188
197
|
|
|
189
198
|
|
|
190
|
-
def _run_practice(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
):
|
|
199
|
+
def _run_practice(animal: dict, values: dict, meanDE: float, meanECHHV: float, REM: float, REG: float, NEwool: float):
|
|
200
|
+
NEm_feed, NEg_feed, log_feed = calculate_NEfeed(animal)
|
|
201
|
+
|
|
194
202
|
def run(practice: dict):
|
|
195
203
|
key = practice.get('key', {})
|
|
196
204
|
key_id = key.get('@id')
|
|
@@ -202,18 +210,15 @@ def _run_practice(
|
|
|
202
210
|
|
|
203
211
|
value = (GE / meanECHHV) * (list_sum(practice.get('value', [0])) / 100)
|
|
204
212
|
|
|
205
|
-
logs = log_as_table(
|
|
206
|
-
'
|
|
207
|
-
|
|
208
|
-
'GE': GE,
|
|
209
|
-
'NEmFeed': NEm_feed,
|
|
210
|
-
'NEgFeed': NEg_feed,
|
|
211
|
-
'REM': REM,
|
|
212
|
-
'REG': REG,
|
|
213
|
+
logs = log_as_table([{
|
|
214
|
+
'id': animal.get('term', {}).get('@id')
|
|
215
|
+
} | values | {
|
|
213
216
|
'NEwool': NEwool,
|
|
214
|
-
'
|
|
215
|
-
'
|
|
216
|
-
|
|
217
|
+
'total-feed-NEm': NEm_feed,
|
|
218
|
+
'total-feed-NEg': NEg_feed,
|
|
219
|
+
'practiceKeyId': key_id,
|
|
220
|
+
'GE': GE
|
|
221
|
+
}])
|
|
217
222
|
animal_lookups = lookups_logs(MODEL, [animal], LOOKUPS, model_key=MODEL_KEY, term=input_term_id)
|
|
218
223
|
animal_properties = properties_logs([animal], properties=[
|
|
219
224
|
'liveweightPerHead',
|
|
@@ -226,23 +231,24 @@ def _run_practice(
|
|
|
226
231
|
'weightAtOneYear',
|
|
227
232
|
'weightAtSlaughter'
|
|
228
233
|
])
|
|
234
|
+
has_positive_feed_values = all([NEm_feed > 0, NEg_feed > 0])
|
|
229
235
|
|
|
230
236
|
logRequirements(animal, model=MODEL, term=input_term_id, model_key=MODEL_KEY,
|
|
237
|
+
feed_logs=log_as_table(log_feed),
|
|
238
|
+
has_positive_feed_values=has_positive_feed_values,
|
|
231
239
|
animal_logs=logs,
|
|
232
240
|
animal_lookups=animal_lookups,
|
|
233
241
|
animal_properties=animal_properties)
|
|
234
242
|
|
|
235
|
-
|
|
243
|
+
should_run = all([has_positive_feed_values])
|
|
244
|
+
logShouldRun(animal, MODEL, input_term_id, should_run, model_key=MODEL_KEY)
|
|
236
245
|
|
|
237
|
-
return _input(input_term_id, value)
|
|
246
|
+
return _input(input_term_id, value) if should_run else None
|
|
238
247
|
|
|
239
248
|
return run
|
|
240
249
|
|
|
241
250
|
|
|
242
|
-
def _run_animal(cycle: dict, meanDE: float, meanECHHV: float,
|
|
243
|
-
REM = calculate_REM(meanDE)
|
|
244
|
-
REG = calculate_REG(meanDE)
|
|
245
|
-
|
|
251
|
+
def _run_animal(cycle: dict, meanDE: float, meanECHHV: float, REM: float, REG: float, systems: list, practices: list):
|
|
246
252
|
wool_term_ids = get_wool_terms()
|
|
247
253
|
# list of animal product
|
|
248
254
|
wool_products = [p for p in cycle.get('products', []) if p.get('term', {}).get('@id') in wool_term_ids]
|
|
@@ -253,15 +259,14 @@ def _run_animal(cycle: dict, meanDE: float, meanECHHV: float, system: dict, prac
|
|
|
253
259
|
NEwool = calculate_NEwool(cycle, animal, wool_products, total_liveWeightPerHead) if (
|
|
254
260
|
total_liveWeightPerHead > 0
|
|
255
261
|
) else 0
|
|
256
|
-
|
|
257
|
-
animal_values = get_animal_values(cycle, animal, system)
|
|
262
|
+
animal_values = get_animal_values(cycle, animal, systems)
|
|
258
263
|
|
|
259
|
-
inputs =
|
|
260
|
-
_run_practice(animal, animal_values, meanDE, meanECHHV, REM, REG, NEwool
|
|
264
|
+
inputs = non_empty_list(map(
|
|
265
|
+
_run_practice(animal, animal_values, meanDE, meanECHHV, REM, REG, NEwool),
|
|
261
266
|
practices
|
|
262
267
|
))
|
|
263
268
|
return animal | {
|
|
264
|
-
'inputs': animal.get('inputs', [])
|
|
269
|
+
'inputs': merge_blank_nodes(animal.get('inputs', []), inputs)
|
|
265
270
|
}
|
|
266
271
|
|
|
267
272
|
return run
|
|
@@ -278,6 +283,8 @@ def _should_run(cycle: dict, animals: list, practices: dict):
|
|
|
278
283
|
|
|
279
284
|
meanDE = calculate_meanDE(practices)
|
|
280
285
|
meanECHHV = calculate_meanECHHV(practices)
|
|
286
|
+
REM = calculate_REM(meanDE)
|
|
287
|
+
REG = calculate_REG(meanDE)
|
|
281
288
|
|
|
282
289
|
should_run = all([
|
|
283
290
|
animalFeed_complete,
|
|
@@ -299,16 +306,18 @@ def _should_run(cycle: dict, animals: list, practices: dict):
|
|
|
299
306
|
term_type_freshForage_incomplete=freshForage_incomplete,
|
|
300
307
|
no_cycle_inputs_feed=no_cycle_inputs_feed,
|
|
301
308
|
all_animals_have_value=all_animals_have_value,
|
|
302
|
-
|
|
303
|
-
|
|
309
|
+
grass_MeanDE=calculate_meanDE(practices, term=term_id),
|
|
310
|
+
grass_MeanECHHV=calculate_meanECHHV(practices, term=term_id),
|
|
311
|
+
grass_REM=REM,
|
|
312
|
+
grass_REG=REG)
|
|
304
313
|
|
|
305
314
|
logShouldRun(animal, MODEL, term_id, should_run, model_key=MODEL_KEY)
|
|
306
315
|
|
|
307
|
-
return should_run, meanDE, meanECHHV,
|
|
316
|
+
return should_run, meanDE, meanECHHV, REM, REG, systems
|
|
308
317
|
|
|
309
318
|
|
|
310
319
|
def run(cycle: dict):
|
|
311
320
|
animals = get_animals_by_period(cycle)
|
|
312
321
|
practices = list(filter(should_run_practice(cycle), cycle.get('practices', [])))
|
|
313
|
-
should_run, meanDE, meanECHHV,
|
|
314
|
-
return list(map(_run_animal(cycle, meanDE, meanECHHV,
|
|
322
|
+
should_run, meanDE, meanECHHV, REM, REG, systems = _should_run(cycle, animals, practices)
|
|
323
|
+
return list(map(_run_animal(cycle, meanDE, meanECHHV, REM, REG, systems, practices), animals)) if should_run else []
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name, extract_grouped_data
|
|
2
|
+
from hestia_earth.utils.tools import safe_parse_float
|
|
3
|
+
|
|
4
|
+
from hestia_earth.models.log import debugMissingLookup
|
|
5
|
+
from hestia_earth.models.utils.productivity import PRODUCTIVITY, get_productivity
|
|
6
|
+
from .. import MODEL
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def productivity_lookup_value(term_id: str, lookup: str, country: dict, animal: dict):
|
|
10
|
+
country_id = country.get('@id')
|
|
11
|
+
productivity_key = get_productivity(country)
|
|
12
|
+
lookup_name = f"{lookup}.csv"
|
|
13
|
+
lookup = download_lookup(lookup_name)
|
|
14
|
+
column = column_name(animal.get('term').get('@id'))
|
|
15
|
+
value = get_table_value(lookup, 'termid', country_id, column)
|
|
16
|
+
debugMissingLookup(lookup_name, 'termid', country_id, column, value, model=MODEL, term=term_id)
|
|
17
|
+
return safe_parse_float(
|
|
18
|
+
extract_grouped_data(value, productivity_key.value) or
|
|
19
|
+
extract_grouped_data(value, PRODUCTIVITY.HIGH.value) # defaults to high if low is not found
|
|
20
|
+
)
|
|
@@ -4,11 +4,11 @@ greater than or equal to `liveweightPerHead` value.
|
|
|
4
4
|
"""
|
|
5
5
|
from hestia_earth.schema import TermTermType
|
|
6
6
|
from hestia_earth.utils.model import filter_list_term_type, find_term_match
|
|
7
|
-
from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name
|
|
8
|
-
from hestia_earth.utils.tools import safe_parse_float
|
|
9
7
|
|
|
10
|
-
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
8
|
+
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
9
|
+
from hestia_earth.models.utils.blank_node import merge_blank_nodes
|
|
11
10
|
from hestia_earth.models.utils.property import _new_property, node_has_no_property
|
|
11
|
+
from .utils import productivity_lookup_value
|
|
12
12
|
from .. import MODEL
|
|
13
13
|
|
|
14
14
|
REQUIREMENTS = {
|
|
@@ -61,24 +61,19 @@ def _run_animal(data: dict):
|
|
|
61
61
|
animal = data.get('animal')
|
|
62
62
|
value = data.get('value')
|
|
63
63
|
return animal | {
|
|
64
|
-
'properties': animal.get('properties', [])
|
|
64
|
+
'properties': merge_blank_nodes(animal.get('properties', []), [_property(value)])
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
def _lookup_value(country_id: str, animal: dict):
|
|
69
|
-
lookup_name = f"{list(LOOKUPS.keys())[0]}.csv"
|
|
70
|
-
lookup = download_lookup(lookup_name)
|
|
71
|
-
column = column_name(animal.get('term').get('@id'))
|
|
72
|
-
value = get_table_value(lookup, 'termid', country_id, column)
|
|
73
|
-
debugMissingLookup(lookup_name, 'termid', country_id, column, value, model=MODEL, term=TERM_ID)
|
|
74
|
-
return safe_parse_float(value)
|
|
75
|
-
|
|
76
|
-
|
|
77
68
|
def _should_run(cycle: dict):
|
|
78
|
-
|
|
69
|
+
country = cycle.get('site', {}).get('country', {})
|
|
70
|
+
country_id = country.get('@id')
|
|
79
71
|
live_animals = filter_list_term_type(cycle.get('animals', []), TermTermType.LIVEANIMAL)
|
|
80
72
|
live_animals = list(filter(node_has_no_property(TERM_ID), live_animals))
|
|
81
|
-
live_animals_with_value = [{
|
|
73
|
+
live_animals_with_value = [{
|
|
74
|
+
'animal': animal,
|
|
75
|
+
'value': productivity_lookup_value(TERM_ID, list(LOOKUPS.keys())[0], country, animal)
|
|
76
|
+
} for animal in live_animals]
|
|
82
77
|
|
|
83
78
|
def _should_run_animal(value: dict):
|
|
84
79
|
lookup_value = value.get('value')
|