hestia-earth-models 0.61.8__py3-none-any.whl → 0.62.1__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/blonkConsultants2016/utils.py +3 -2
- hestia_earth/models/cycle/completeness/electricityFuel.py +5 -1
- hestia_earth/models/cycle/post_checks/__init__.py +3 -2
- hestia_earth/models/cycle/post_checks/otherSites.py +40 -0
- hestia_earth/models/cycle/pre_checks/__init__.py +2 -1
- hestia_earth/models/cycle/pre_checks/otherSites.py +42 -0
- hestia_earth/models/cycle/pre_checks/site.py +1 -1
- hestia_earth/models/cycle/product/economicValueShare.py +47 -31
- hestia_earth/models/ecoinventV3AndEmberClimate/utils.py +1 -1
- hestia_earth/models/emepEea2019/utils.py +4 -3
- hestia_earth/models/geospatialDatabase/heavyWinterPrecipitation.py +1 -1
- hestia_earth/models/ipcc2019/animal/pastureGrass.py +36 -30
- hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChangeManagementChange.py +30 -4
- hestia_earth/models/ipcc2019/n2OToAirExcretaDirect.py +6 -2
- hestia_earth/models/ipcc2019/n2OToAirExcretaIndirect.py +1 -1
- hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserDirect.py +1 -1
- hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserIndirect.py +1 -1
- hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserIndirect.py +1 -1
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +4 -6
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +4 -2
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +12 -11
- hestia_earth/models/ipcc2019/pastureGrass.py +40 -22
- hestia_earth/models/ipcc2019/pastureGrass_utils.py +43 -60
- hestia_earth/models/mocking/search-results.json +261 -257
- hestia_earth/models/schererPfister2015/utils.py +2 -2
- hestia_earth/models/site/brackishWater.py +1 -1
- hestia_earth/models/site/flowingWater.py +1 -1
- hestia_earth/models/site/freshWater.py +1 -1
- hestia_earth/models/site/management.py +29 -11
- hestia_earth/models/site/pre_checks/cache_sources.py +9 -13
- hestia_earth/models/site/salineWater.py +1 -1
- hestia_earth/models/stehfestBouwman2006/n2OToAirCropResidueDecompositionDirect.py +12 -2
- hestia_earth/models/stehfestBouwman2006/n2OToAirExcretaDirect.py +12 -2
- hestia_earth/models/stehfestBouwman2006/n2OToAirInorganicFertiliserDirect.py +11 -1
- hestia_earth/models/stehfestBouwman2006/n2OToAirOrganicFertiliserDirect.py +11 -1
- hestia_earth/models/stehfestBouwman2006/noxToAirCropResidueDecomposition.py +12 -2
- hestia_earth/models/stehfestBouwman2006/noxToAirExcreta.py +12 -2
- hestia_earth/models/stehfestBouwman2006/noxToAirInorganicFertiliser.py +11 -1
- hestia_earth/models/stehfestBouwman2006/noxToAirOrganicFertiliser.py +11 -1
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirCropResidueDecomposition.py +12 -2
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirExcreta.py +12 -2
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirInorganicFertiliser.py +11 -1
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirOrganicFertiliser.py +11 -1
- hestia_earth/models/utils/blank_node.py +159 -146
- hestia_earth/models/utils/constant.py +2 -0
- hestia_earth/models/utils/lookup.py +19 -6
- hestia_earth/models/utils/source.py +1 -1
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.61.8.dist-info → hestia_earth_models-0.62.1.dist-info}/METADATA +3 -3
- {hestia_earth_models-0.61.8.dist-info → hestia_earth_models-0.62.1.dist-info}/RECORD +64 -59
- tests/models/cycle/post_checks/test_otherSites.py +15 -0
- tests/models/cycle/pre_checks/test_otherSites.py +21 -0
- tests/models/cycle/product/test_economicValueShare.py +8 -0
- tests/models/ipcc2019/animal/test_pastureGrass.py +2 -2
- tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChangeManagementChange.py +5 -2
- tests/models/ipcc2019/test_organicCarbonPerHa.py +2 -1
- tests/models/ipcc2019/test_pastureGrass.py +0 -16
- tests/models/site/pre_checks/test_cache_sources.py +6 -10
- tests/models/site/test_management.py +162 -2
- tests/models/utils/test_blank_node.py +154 -296
- tests/models/utils/test_lookup.py +10 -0
- {hestia_earth_models-0.61.8.dist-info → hestia_earth_models-0.62.1.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.61.8.dist-info → hestia_earth_models-0.62.1.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.61.8.dist-info → hestia_earth_models-0.62.1.dist-info}/top_level.txt +0 -0
|
@@ -25,5 +25,6 @@ def get_emission_factor(term_id: str, cycle: dict, factor: str):
|
|
|
25
25
|
data = safe_parse_float(value, None)
|
|
26
26
|
# fallback to site.siteType data if possible
|
|
27
27
|
return data if data is not None else safe_parse_float(
|
|
28
|
-
extract_grouped_data(
|
|
29
|
-
|
|
28
|
+
extract_grouped_data(get_table_value(lookup, 'termid', country_id, column_name('NONE')), site.get('siteType')),
|
|
29
|
+
None
|
|
30
|
+
)
|
|
@@ -9,10 +9,12 @@ from hestia_earth.utils.model import filter_list_term_type
|
|
|
9
9
|
|
|
10
10
|
from hestia_earth.models.log import logRequirements, log_as_table
|
|
11
11
|
from hestia_earth.models.utils.blank_node import get_lookup_value
|
|
12
|
+
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
12
13
|
from . import MODEL
|
|
13
14
|
|
|
14
15
|
REQUIREMENTS = {
|
|
15
16
|
"Cycle": {
|
|
17
|
+
"completeness.operation": "True",
|
|
16
18
|
"completeness.electricityFuel": "False",
|
|
17
19
|
"practices": [
|
|
18
20
|
{"@type": "Practice", "value": "", "term.termType": "operation"}
|
|
@@ -42,6 +44,7 @@ def _practice_value(practice: dict):
|
|
|
42
44
|
|
|
43
45
|
|
|
44
46
|
def run(cycle: dict):
|
|
47
|
+
operation_complete = _is_term_type_complete(cycle, TermTermType.OPERATION)
|
|
45
48
|
practices = filter_list_term_type(cycle.get('practices', []), TermTermType.OPERATION)
|
|
46
49
|
combustion_practices = [
|
|
47
50
|
p for p in practices if _lookup_value(p, LOOKUPS['operation'][1]) in _VALID_COMBUSTION_TYPES
|
|
@@ -49,8 +52,9 @@ def run(cycle: dict):
|
|
|
49
52
|
practices_values = list(map(_practice_value, combustion_practices))
|
|
50
53
|
|
|
51
54
|
logRequirements(cycle, model=MODEL, term=None, key=MODEL_KEY,
|
|
55
|
+
term_type_operation_complete=operation_complete,
|
|
52
56
|
values=log_as_table(practices_values))
|
|
53
57
|
|
|
54
|
-
is_complete = all([p.get('fuel_use') for p in practices_values])
|
|
58
|
+
is_complete = all([operation_complete] + [p.get('fuel_use') for p in practices_values])
|
|
55
59
|
|
|
56
60
|
return is_complete
|
|
@@ -2,14 +2,15 @@ from os.path import dirname, abspath
|
|
|
2
2
|
import sys
|
|
3
3
|
|
|
4
4
|
from hestia_earth.models.utils import _run_in_serie
|
|
5
|
-
from . import cache, site
|
|
5
|
+
from . import cache, site, otherSites
|
|
6
6
|
|
|
7
7
|
CURRENT_DIR = dirname(abspath(__file__)) + '/'
|
|
8
8
|
sys.path.append(CURRENT_DIR)
|
|
9
9
|
|
|
10
10
|
MODELS = [
|
|
11
11
|
cache.run,
|
|
12
|
-
site.run
|
|
12
|
+
site.run,
|
|
13
|
+
otherSites.run
|
|
13
14
|
]
|
|
14
15
|
|
|
15
16
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Post Checks Other Sites
|
|
3
|
+
|
|
4
|
+
This model is run only if the [pre model](../pre_checks/cycle.md) has been run before.
|
|
5
|
+
This model will restore the `cycle.otherSites` as a list of "linked node"
|
|
6
|
+
(i.e. it will be set with only `@type`, `@id` and `name` keys).
|
|
7
|
+
"""
|
|
8
|
+
from hestia_earth.utils.model import linked_node
|
|
9
|
+
|
|
10
|
+
REQUIREMENTS = {
|
|
11
|
+
"Cycle": {
|
|
12
|
+
"otherSites": [{
|
|
13
|
+
"@type": "Site",
|
|
14
|
+
"@id": ""
|
|
15
|
+
}]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
RETURNS = {
|
|
19
|
+
"Cycle": {
|
|
20
|
+
"otherSites": [{"@type": "Site"}]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
MODEL_KEY = 'otherSites'
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _run_site(site: dict): return linked_node(site)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _should_run_site(site: dict): return site.get('@id') is not None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _should_run(cycle: dict): return len(cycle.get(MODEL_KEY, [])) > 0
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def run(cycle: dict):
|
|
36
|
+
return cycle | (
|
|
37
|
+
({
|
|
38
|
+
MODEL_KEY: [_run_site(site) if _should_run_site(site) else site for site in cycle.get(MODEL_KEY, [])]
|
|
39
|
+
}) if _should_run(cycle) else {}
|
|
40
|
+
)
|
|
@@ -2,12 +2,13 @@ from os.path import dirname, abspath
|
|
|
2
2
|
import sys
|
|
3
3
|
|
|
4
4
|
from hestia_earth.models.utils import _run_in_serie
|
|
5
|
-
from . import site, cache_sources
|
|
5
|
+
from . import site, cache_sources, otherSites
|
|
6
6
|
|
|
7
7
|
CURRENT_DIR = dirname(abspath(__file__)) + '/'
|
|
8
8
|
sys.path.append(CURRENT_DIR)
|
|
9
9
|
|
|
10
10
|
MODELS = [
|
|
11
|
+
otherSites.run,
|
|
11
12
|
site.run,
|
|
12
13
|
cache_sources.run
|
|
13
14
|
]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pre Checks Other Sites
|
|
3
|
+
|
|
4
|
+
Some Cycle models need a full version of the linked [Site](https://hestia.earth/schema/Site) to run.
|
|
5
|
+
This model will fetch the complete version of the [Cycle Sites](https://hestia.earth/schema/Cycle#otherSites)
|
|
6
|
+
and include them.
|
|
7
|
+
"""
|
|
8
|
+
from hestia_earth.schema import SchemaType
|
|
9
|
+
|
|
10
|
+
from hestia_earth.models.utils import _load_calculated_node
|
|
11
|
+
|
|
12
|
+
REQUIREMENTS = {
|
|
13
|
+
"Cycle": {
|
|
14
|
+
"otherSites": [{
|
|
15
|
+
"@type": "Site",
|
|
16
|
+
"@id": ""
|
|
17
|
+
}]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
RETURNS = {
|
|
21
|
+
"Cycle": {
|
|
22
|
+
"otherSites": [{"@type": "Site"}]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
MODEL_KEY = 'otherSites'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _run_site(site: dict): return _load_calculated_node(site, SchemaType.SITE)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _should_run_site(site: dict): return site.get('@id') is not None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _should_run(cycle: dict): return len(cycle.get(MODEL_KEY, [])) > 0
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def run(cycle: dict):
|
|
38
|
+
return cycle | (
|
|
39
|
+
({
|
|
40
|
+
MODEL_KEY: [_run_site(site) if _should_run_site(site) else site for site in cycle.get(MODEL_KEY, [])]
|
|
41
|
+
}) if _should_run(cycle) else {}
|
|
42
|
+
)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Pre Checks Site
|
|
3
3
|
|
|
4
4
|
Some Cycle models need a full version of the linked [Site](https://hestia.earth/schema/Site) to run.
|
|
5
|
-
This model will fetch the complete version of the [Site](https://hestia.earth/schema/
|
|
5
|
+
This model will fetch the complete version of the [Cycle Site](https://hestia.earth/schema/Cycle#site) and include it.
|
|
6
6
|
"""
|
|
7
7
|
from hestia_earth.schema import SchemaType
|
|
8
8
|
|
|
@@ -2,20 +2,21 @@
|
|
|
2
2
|
Product Economic Value Share
|
|
3
3
|
|
|
4
4
|
This model quantifies the relative economic value share of each marketable Product in a Cycle.
|
|
5
|
-
Marketable Products are all Products
|
|
5
|
+
Marketable Products are all Products identified with the lookup "generateImpactAssessment = TRUE".
|
|
6
6
|
|
|
7
7
|
It works in the following order:
|
|
8
|
-
1.
|
|
8
|
+
1. For every product with no `value`, the `economicValueShare` is set to `0`.
|
|
9
|
+
2. If revenue data are provided for all marketable products,
|
|
9
10
|
the `economicValueShare` is directly calculated as the share of revenue of each Product;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
3. Based on a lookup table containing typical global average economic value shares
|
|
12
|
+
drawn from [Poore & Nemecek (2018)](https://science.sciencemag.org/content/360/6392/987), if all products have a
|
|
13
|
+
corresponding value, the `economicValueShare` will be proportionally distributed among the products.
|
|
13
14
|
"""
|
|
14
|
-
from hestia_earth.schema import TermTermType
|
|
15
15
|
from hestia_earth.utils.model import find_term_match
|
|
16
16
|
from hestia_earth.utils.tools import list_sum
|
|
17
17
|
|
|
18
18
|
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
19
|
+
from hestia_earth.models.utils.blank_node import get_lookup_value
|
|
19
20
|
from hestia_earth.models.utils.cycle import unique_currencies
|
|
20
21
|
from .utils import lookup_share
|
|
21
22
|
from .. import MODEL
|
|
@@ -25,9 +26,11 @@ REQUIREMENTS = {
|
|
|
25
26
|
"products": [{
|
|
26
27
|
"@type": "Product",
|
|
27
28
|
"value": "",
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
"optional": {
|
|
30
|
+
"economicValueShare": "",
|
|
31
|
+
"revenue": "",
|
|
32
|
+
"currency": ""
|
|
33
|
+
}
|
|
31
34
|
}],
|
|
32
35
|
"optional": {
|
|
33
36
|
"completeness.product": ""
|
|
@@ -42,7 +45,8 @@ RETURNS = {
|
|
|
42
45
|
LOOKUPS = {
|
|
43
46
|
"@doc": "Depending on the primary product [termType](https://hestia.earth/schema/Product#term)",
|
|
44
47
|
"crop": "global_economic_value_share",
|
|
45
|
-
"excreta": "global_economic_value_share"
|
|
48
|
+
"excreta": "global_economic_value_share",
|
|
49
|
+
"animalProduct": "global_economic_value_share"
|
|
46
50
|
}
|
|
47
51
|
MODEL_KEY = 'economicValueShare'
|
|
48
52
|
MAX_VALUE = 100.5
|
|
@@ -60,7 +64,7 @@ def _is_complete(cycle: dict): return cycle.get('completeness', {}).get('product
|
|
|
60
64
|
def _no_yield(product): return list_sum(product.get('value', [-1]), -1) == 0
|
|
61
65
|
|
|
62
66
|
|
|
63
|
-
def
|
|
67
|
+
def _total_evs(products: list): return sum([p.get(MODEL_KEY, 0) for p in products])
|
|
64
68
|
|
|
65
69
|
|
|
66
70
|
def _product_with_value(product: dict):
|
|
@@ -72,18 +76,33 @@ def _rescale_value(products: list, total: float):
|
|
|
72
76
|
return list(map(lambda p: {**p, MODEL_KEY: p.get(MODEL_KEY) * 100 / total}, products))
|
|
73
77
|
|
|
74
78
|
|
|
79
|
+
def _should_run_by_default(cycle: dict, products: list):
|
|
80
|
+
run_by = 'default'
|
|
81
|
+
# only run if all products have the lookup data or value will be incorrect
|
|
82
|
+
products = list(map(_product_with_value, products))
|
|
83
|
+
all_with_economicValueShare = all([p.get(MODEL_KEY) is not None for p in products])
|
|
84
|
+
|
|
85
|
+
should_run = all([all_with_economicValueShare])
|
|
86
|
+
|
|
87
|
+
for p in products:
|
|
88
|
+
term_id = p.get('term', {}).get('@id')
|
|
89
|
+
logRequirements(cycle, model=MODEL, term=term_id, key=MODEL_KEY, by=run_by,
|
|
90
|
+
all_with_economicValueShare=all_with_economicValueShare)
|
|
91
|
+
logShouldRun(cycle, MODEL, term_id, should_run, key=MODEL_KEY, by=run_by)
|
|
92
|
+
|
|
93
|
+
return should_run
|
|
94
|
+
|
|
95
|
+
|
|
75
96
|
def _run_by_default(cycle: dict, products: list):
|
|
76
97
|
run_by = 'default'
|
|
77
98
|
is_complete = _is_complete(cycle)
|
|
78
99
|
products = list(map(_product_with_value, products))
|
|
79
|
-
# remove results where lookup share was not found
|
|
80
|
-
results = list(filter(lambda p: p.get(MODEL_KEY) is not None, products))
|
|
81
100
|
# only return list if the new total of evs is not above threshold
|
|
82
|
-
|
|
83
|
-
below_max_threshold =
|
|
84
|
-
should_rescale = is_complete and MIN_COMPLETE_VALUE <=
|
|
85
|
-
above_min_threshold = True if should_rescale else
|
|
86
|
-
results = _rescale_value(
|
|
101
|
+
total_economicValueShare = _total_evs(products)
|
|
102
|
+
below_max_threshold = total_economicValueShare <= MAX_VALUE
|
|
103
|
+
should_rescale = is_complete and MIN_COMPLETE_VALUE <= total_economicValueShare <= MAX_VALUE
|
|
104
|
+
above_min_threshold = True if should_rescale else total_economicValueShare >= MIN_VALUE if is_complete else True
|
|
105
|
+
results = _rescale_value(products, total_economicValueShare) if should_rescale else products
|
|
87
106
|
|
|
88
107
|
should_run = all([below_max_threshold, above_min_threshold])
|
|
89
108
|
|
|
@@ -93,7 +112,7 @@ def _run_by_default(cycle: dict, products: list):
|
|
|
93
112
|
logRequirements(cycle, model=MODEL, term=term_id, key=MODEL_KEY, by=run_by,
|
|
94
113
|
below_max_threshold=below_max_threshold,
|
|
95
114
|
above_min_threshold=above_min_threshold,
|
|
96
|
-
|
|
115
|
+
total_economicValueShare=total_economicValueShare)
|
|
97
116
|
logShouldRun(cycle, MODEL, term_id, p_should_run, key=MODEL_KEY, by=run_by)
|
|
98
117
|
|
|
99
118
|
return results if should_run else []
|
|
@@ -109,8 +128,8 @@ def _run_by_revenue(products: list):
|
|
|
109
128
|
def _should_run_by_revenue(cycle: dict, products: list):
|
|
110
129
|
run_by = 'revenue'
|
|
111
130
|
is_complete = _is_complete(cycle)
|
|
112
|
-
|
|
113
|
-
below_threshold =
|
|
131
|
+
total_economicValueShare = _total_evs(products)
|
|
132
|
+
below_threshold = total_economicValueShare < MAX_VALUE
|
|
114
133
|
# ignore products with no yield
|
|
115
134
|
products = list(filter(lambda p: not _no_yield(p), products))
|
|
116
135
|
currencies = unique_currencies(cycle)
|
|
@@ -122,7 +141,7 @@ def _should_run_by_revenue(cycle: dict, products: list):
|
|
|
122
141
|
term_id = p.get('term', {}).get('@id')
|
|
123
142
|
logRequirements(cycle, model=MODEL, term=term_id, key=MODEL_KEY, by=run_by,
|
|
124
143
|
is_complete=is_complete,
|
|
125
|
-
|
|
144
|
+
total_economicValueShare=total_economicValueShare,
|
|
126
145
|
below_threshold=below_threshold,
|
|
127
146
|
all_with_revenue=all_with_revenue,
|
|
128
147
|
currencies=';'.join(currencies),
|
|
@@ -133,14 +152,14 @@ def _should_run_by_revenue(cycle: dict, products: list):
|
|
|
133
152
|
|
|
134
153
|
|
|
135
154
|
def _run_single_missing_evs(products: list):
|
|
136
|
-
total_value =
|
|
155
|
+
total_value = _total_evs(products)
|
|
137
156
|
return list(map(lambda p: _product(p, 100 - total_value) if p.get(MODEL_KEY) is None else p, products))
|
|
138
157
|
|
|
139
158
|
|
|
140
159
|
def _should_run_single_missing_evs(cycle: dict, products: list):
|
|
141
160
|
run_by = '1-missing-evs'
|
|
142
161
|
is_complete = _is_complete(cycle)
|
|
143
|
-
total_value =
|
|
162
|
+
total_value = _total_evs(products)
|
|
144
163
|
# ignore products with no yield
|
|
145
164
|
products = list(filter(lambda p: not _no_yield(p), products))
|
|
146
165
|
missing_values = [p for p in products if p.get(MODEL_KEY) is None]
|
|
@@ -175,20 +194,17 @@ def _should_run_no_value(cycle: dict, product: dict):
|
|
|
175
194
|
|
|
176
195
|
|
|
177
196
|
def _should_have_evs(product: dict):
|
|
178
|
-
|
|
179
|
-
return
|
|
180
|
-
TermTermType.CROPRESIDUE.value,
|
|
181
|
-
TermTermType.EXCRETA.value
|
|
182
|
-
]
|
|
197
|
+
should_generate_ia = get_lookup_value(product.get('term', {}), 'generateImpactAssessment')
|
|
198
|
+
return str(should_generate_ia).lower() != 'false'
|
|
183
199
|
|
|
184
200
|
|
|
185
201
|
def run(cycle: dict):
|
|
186
202
|
products = cycle.get('products', [])
|
|
187
|
-
# skip any product that
|
|
203
|
+
# skip any product that is not marketable
|
|
188
204
|
products = list(filter(_should_have_evs, products))
|
|
189
205
|
products = list(map(lambda p: _product(p, 0) if _should_run_no_value(cycle, p) else p, products))
|
|
190
206
|
return (
|
|
191
207
|
_run_single_missing_evs(products) if _should_run_single_missing_evs(cycle, products) else
|
|
192
208
|
_run_by_revenue(products) if _should_run_by_revenue(cycle, products) else
|
|
193
|
-
_run_by_default(cycle, products)
|
|
209
|
+
_run_by_default(cycle, products) if _should_run_by_default(cycle, products) else []
|
|
194
210
|
)
|
|
@@ -33,7 +33,7 @@ def _extract_emission_value(value_iter: Any) -> Union[float, None]:
|
|
|
33
33
|
value_list = list(value_iter)
|
|
34
34
|
try:
|
|
35
35
|
if len(list(value_list)) > 0 and len(list(value_list)[0]) > 1:
|
|
36
|
-
return safe_parse_float(list(value_list)[0][1])
|
|
36
|
+
return safe_parse_float(list(value_list)[0][1], None)
|
|
37
37
|
except ValueError:
|
|
38
38
|
return None
|
|
39
39
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from hestia_earth.schema import TermTermType
|
|
2
2
|
from hestia_earth.utils.model import filter_list_term_type
|
|
3
3
|
from hestia_earth.utils.lookup import extract_grouped_data
|
|
4
|
-
from hestia_earth.utils.tools import list_sum, safe_parse_float
|
|
4
|
+
from hestia_earth.utils.tools import list_sum, safe_parse_float, non_empty_list
|
|
5
5
|
|
|
6
6
|
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
7
7
|
from hestia_earth.models.utils.term import get_lookup_value
|
|
@@ -19,14 +19,15 @@ def _get_fuel_input_value(term_id: str, lookup_col: str):
|
|
|
19
19
|
get_lookup_value(operation_term, lookup_col, model=MODEL, term=term_id), input_term_id
|
|
20
20
|
) if operation_term else None
|
|
21
21
|
input_factor = operation_factor or get_lookup_value(input_term, lookup_col, model=MODEL, term=term_id)
|
|
22
|
+
factor = safe_parse_float(input_factor, None)
|
|
22
23
|
|
|
23
|
-
return input_value *
|
|
24
|
+
return input_value * factor if factor is not None else None
|
|
24
25
|
return get_value
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
def get_fuel_values(term_id: str, cycle: dict, lookup_col: str):
|
|
28
29
|
inputs = filter_list_term_type(cycle.get('inputs', []), TermTermType.FUEL)
|
|
29
|
-
values =
|
|
30
|
+
values = non_empty_list(map(_get_fuel_input_value(term_id, lookup_col), inputs))
|
|
30
31
|
|
|
31
32
|
return [0] if all([
|
|
32
33
|
len(values) == 0,
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Animal Pasture Grass
|
|
3
3
|
|
|
4
4
|
This model estimates the energetic requirements of ruminants and can be used to estimate the amount of grass they graze.
|
|
5
5
|
Source:
|
|
6
6
|
[IPCC 2019, Vol.4, Chapter 10](https://www.ipcc-nggip.iges.or.jp/public/2019rf/pdf/4_Volume4/19R_V4_Ch10_Livestock.pdf).
|
|
7
|
+
|
|
8
|
+
This version of the model will run at the Animal Blank Node level, if none of the Cycle Input are given as feed
|
|
9
|
+
(see https://www.hestia.earth/schema/Input#isAnimalFeed).
|
|
7
10
|
"""
|
|
8
11
|
from hestia_earth.schema import TermTermType
|
|
9
12
|
from hestia_earth.utils.model import filter_list_term_type
|
|
@@ -138,7 +141,7 @@ RETURNS = {
|
|
|
138
141
|
}]
|
|
139
142
|
}]
|
|
140
143
|
}
|
|
141
|
-
MODEL_KEY = '
|
|
144
|
+
MODEL_KEY = 'pastureGrass'
|
|
142
145
|
|
|
143
146
|
|
|
144
147
|
def _input(term_id: str, value: float):
|
|
@@ -173,7 +176,7 @@ def calculate_NEwool(cycle: dict, animal: dict, products: list, total_weight: fl
|
|
|
173
176
|
])
|
|
174
177
|
animal_weight = _sum_liveWeightPerHead([animal])
|
|
175
178
|
|
|
176
|
-
debugValues(
|
|
179
|
+
debugValues(animal, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
177
180
|
total_energy=total_energy,
|
|
178
181
|
animal_liveWeightPerHead=animal_weight,
|
|
179
182
|
total_liveWeightPerHead=total_weight)
|
|
@@ -186,29 +189,35 @@ def _calculate_GE(
|
|
|
186
189
|
) -> float:
|
|
187
190
|
term_id = animal.get('term', {}).get('@id')
|
|
188
191
|
|
|
189
|
-
NEm, NEa, NEl, NEwork, NEp, NEg = get_animal_values(cycle, animal, system)
|
|
192
|
+
NEm, NEa, NEl, NEwork, NEp, NEg = get_animal_values(cycle, animal, system, log_node=animal)
|
|
190
193
|
|
|
191
194
|
NEm_feed, NEg_feed = calculate_NEfeed(animal)
|
|
192
|
-
debugValues(
|
|
195
|
+
debugValues(animal, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
196
|
+
NEm=NEm,
|
|
197
|
+
NEa=NEa,
|
|
198
|
+
NEl=NEl,
|
|
199
|
+
NEwork=NEwork,
|
|
200
|
+
NEp=NEp,
|
|
201
|
+
NEg=NEg,
|
|
193
202
|
NEm_feed=NEm_feed,
|
|
194
203
|
NEg_feed=NEg_feed)
|
|
195
204
|
|
|
196
205
|
return (NEm + NEa + NEl + NEwork + NEp - NEm_feed)/REM + (NEg + NEwool - NEg_feed)/REG
|
|
197
206
|
|
|
198
207
|
|
|
199
|
-
def _run_practice(
|
|
208
|
+
def _run_practice(animal: dict, GE: float, meanECHHV: float):
|
|
200
209
|
def run(practice: dict):
|
|
201
210
|
key = practice.get('key', {})
|
|
202
211
|
key_id = key.get('@id')
|
|
203
212
|
input_term_id = practice_input_id(practice)
|
|
204
213
|
value = (GE / meanECHHV) * (list_sum(practice.get('value', [0])) / 100)
|
|
205
214
|
|
|
206
|
-
logRequirements(
|
|
215
|
+
logRequirements(animal, model=MODEL, term=input_term_id, model_key=MODEL_KEY,
|
|
207
216
|
GE=GE,
|
|
208
217
|
meanECHHV=meanECHHV,
|
|
209
|
-
|
|
218
|
+
practice_key_id=key_id)
|
|
210
219
|
|
|
211
|
-
logShouldRun(
|
|
220
|
+
logShouldRun(animal, MODEL, input_term_id, True, model_key=MODEL_KEY)
|
|
212
221
|
|
|
213
222
|
return _input(input_term_id, value)
|
|
214
223
|
|
|
@@ -233,14 +242,14 @@ def _run_animal(cycle: dict, meanDE: float, meanECHHV: float, system: dict, prac
|
|
|
233
242
|
) else 0
|
|
234
243
|
GE = (_calculate_GE(cycle, animal, REM, REG, NEwool, system) / (meanDE/100)) if all([REM, REG]) else 0
|
|
235
244
|
|
|
236
|
-
debugValues(
|
|
245
|
+
debugValues(animal, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
237
246
|
REM=REM,
|
|
238
247
|
REG=REG,
|
|
239
248
|
NEwool=NEwool,
|
|
240
249
|
GE=GE,
|
|
241
250
|
meanDE=meanDE)
|
|
242
251
|
|
|
243
|
-
inputs = list(map(_run_practice(
|
|
252
|
+
inputs = list(map(_run_practice(animal, GE, meanECHHV), practices))
|
|
244
253
|
return animal | {
|
|
245
254
|
'inputs': animal.get('inputs', []) + inputs
|
|
246
255
|
}
|
|
@@ -248,17 +257,12 @@ def _run_animal(cycle: dict, meanDE: float, meanECHHV: float, system: dict, prac
|
|
|
248
257
|
return run
|
|
249
258
|
|
|
250
259
|
|
|
251
|
-
def
|
|
252
|
-
animals = get_animals(cycle)
|
|
253
|
-
return list(map(_run_animal(cycle, meanDE, meanECHHV, system, practices), animals))
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
def _should_run(cycle: dict, practices: dict):
|
|
260
|
+
def _should_run(cycle: dict, animals: list, practices: dict):
|
|
257
261
|
systems = filter_list_term_type(cycle.get('practices', []), TermTermType.SYSTEM)
|
|
258
262
|
animalFeed_complete = _is_term_type_complete(cycle, 'animalFeed')
|
|
259
263
|
animalPopulation_complete = _is_term_type_complete(cycle, 'animalPopulation')
|
|
260
264
|
freshForage_incomplete = _is_term_type_incomplete(cycle, 'freshForage')
|
|
261
|
-
all_animals_have_value = all([a.get('value', 0) > 0 for a in
|
|
265
|
+
all_animals_have_value = all([a.get('value', 0) > 0 for a in animals])
|
|
262
266
|
|
|
263
267
|
no_cycle_inputs_feed = all([not input.get('isAnimalFeed', False) for input in cycle.get('inputs', [])])
|
|
264
268
|
|
|
@@ -278,21 +282,23 @@ def _should_run(cycle: dict, practices: dict):
|
|
|
278
282
|
])
|
|
279
283
|
|
|
280
284
|
for term_id in [MODEL_KEY] + [practice_input_id(p) for p in practices]:
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
285
|
+
for animal in animals:
|
|
286
|
+
logRequirements(animal, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
287
|
+
term_type_animalFeed_complete=animalFeed_complete,
|
|
288
|
+
term_type_animalPopulation_complete=animalPopulation_complete,
|
|
289
|
+
term_type_freshForage_incomplete=freshForage_incomplete,
|
|
290
|
+
no_cycle_inputs_feed=no_cycle_inputs_feed,
|
|
291
|
+
all_animals_have_value=all_animals_have_value,
|
|
292
|
+
meanDE=meanDE,
|
|
293
|
+
meanECHHV=meanECHHV)
|
|
294
|
+
|
|
295
|
+
logShouldRun(animal, MODEL, term_id, should_run, model_key=MODEL_KEY)
|
|
291
296
|
|
|
292
297
|
return should_run, meanDE, meanECHHV, systems[0] if systems else None
|
|
293
298
|
|
|
294
299
|
|
|
295
300
|
def run(cycle: dict):
|
|
301
|
+
animals = get_animals(cycle)
|
|
296
302
|
practices = list(filter(should_run_practice(cycle), cycle.get('practices', [])))
|
|
297
|
-
should_run, meanDE, meanECHHV, system = _should_run(cycle, practices)
|
|
298
|
-
return
|
|
303
|
+
should_run, meanDE, meanECHHV, system = _should_run(cycle, animals, practices)
|
|
304
|
+
return list(map(_run_animal(cycle, meanDE, meanECHHV, system, practices), animals)) if should_run else []
|
|
@@ -7,7 +7,7 @@ from pydash.objects import merge
|
|
|
7
7
|
from typing import NamedTuple, Optional, Union
|
|
8
8
|
|
|
9
9
|
from hestia_earth.schema import (
|
|
10
|
-
CycleFunctionalUnit, EmissionMethodTier, MeasurementMethodClassification
|
|
10
|
+
CycleFunctionalUnit, EmissionMethodTier, MeasurementMethodClassification, SiteSiteType
|
|
11
11
|
)
|
|
12
12
|
from hestia_earth.utils.date import diff_in_days
|
|
13
13
|
from hestia_earth.utils.tools import flatten, non_empty_list, safe_parse_date
|
|
@@ -15,7 +15,8 @@ from hestia_earth.utils.tools import flatten, non_empty_list, safe_parse_date
|
|
|
15
15
|
from hestia_earth.models.log import log_as_table, logRequirements, logShouldRun
|
|
16
16
|
from hestia_earth.models.utils import pairwise
|
|
17
17
|
from hestia_earth.models.utils.blank_node import (
|
|
18
|
-
_get_datestr_format, _gapfill_datestr, DatestrGapfillMode, DatestrFormat, group_nodes_by_year, node_term_match
|
|
18
|
+
_get_datestr_format, _gapfill_datestr, DatestrGapfillMode, DatestrFormat, group_nodes_by_year, node_term_match,
|
|
19
|
+
cumulative_nodes_term_match
|
|
19
20
|
)
|
|
20
21
|
from hestia_earth.models.utils.constant import Units, get_atomic_conversion
|
|
21
22
|
from hestia_earth.models.utils.emission import _new_emission, min_emission_method_tier
|
|
@@ -82,6 +83,13 @@ The list of `MeasurementMethodClassification`s that can be used to calculate SOC
|
|
|
82
83
|
order from strongest to weakest.
|
|
83
84
|
"""
|
|
84
85
|
|
|
86
|
+
_SITE_TYPE_SYSTEMS_MAPPING = {
|
|
87
|
+
SiteSiteType.GLASS_OR_HIGH_ACCESSIBLE_COVER.value: [
|
|
88
|
+
"protectedCroppingSystemSoilBased",
|
|
89
|
+
"protectedCroppingSystemSoilAndSubstrateBased"
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
|
|
85
93
|
|
|
86
94
|
class _InventoryKey(Enum):
|
|
87
95
|
"""
|
|
@@ -207,19 +215,37 @@ def _should_run(cycle: dict) -> tuple[bool, str, dict]:
|
|
|
207
215
|
soc_measurements = [node for node in site.get("measurements", []) if _validate_soc_measurement(node)]
|
|
208
216
|
cycles = related_cycles(site)
|
|
209
217
|
|
|
218
|
+
site_type = site.get("siteType")
|
|
219
|
+
has_soil = site_type not in _SITE_TYPE_SYSTEMS_MAPPING or all(
|
|
220
|
+
cumulative_nodes_term_match(
|
|
221
|
+
cycle.get("practices", []),
|
|
222
|
+
target_term_ids=_SITE_TYPE_SYSTEMS_MAPPING[site_type],
|
|
223
|
+
cumulative_threshold=0
|
|
224
|
+
) for cycle in cycles
|
|
225
|
+
)
|
|
226
|
+
|
|
210
227
|
has_soc_measurements = len(soc_measurements) > 0
|
|
211
228
|
has_cycles = len(cycles) > 0
|
|
212
229
|
has_functional_unit_1_ha = all(cycle.get('functionalUnit') == CycleFunctionalUnit._1_HA.value for cycle in cycles)
|
|
213
230
|
|
|
214
|
-
should_compile_inventory = all([
|
|
231
|
+
should_compile_inventory = all([
|
|
232
|
+
has_soil,
|
|
233
|
+
has_cycles,
|
|
234
|
+
has_functional_unit_1_ha,
|
|
235
|
+
has_soc_measurements,
|
|
236
|
+
])
|
|
215
237
|
|
|
216
|
-
inventory, logs =
|
|
238
|
+
inventory, logs = (
|
|
239
|
+
_compile_inventory(cycle_id, cycles, soc_measurements) if should_compile_inventory else ({}, {})
|
|
240
|
+
)
|
|
217
241
|
|
|
218
242
|
has_valid_inventory = len(inventory) > 0
|
|
219
243
|
has_consecutive_years = check_consecutive(inventory.keys())
|
|
220
244
|
|
|
221
245
|
logRequirements(
|
|
222
246
|
cycle, model=MODEL, term=TERM_ID,
|
|
247
|
+
site_type=site_type,
|
|
248
|
+
has_soil=has_soil,
|
|
223
249
|
has_soc_measurements=has_soc_measurements,
|
|
224
250
|
has_cycles=has_cycles,
|
|
225
251
|
has_functional_unit_1_ha=has_functional_unit_1_ha,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
from hestia_earth.schema import EmissionMethodTier
|
|
1
|
+
from hestia_earth.schema import EmissionMethodTier, TermTermType
|
|
2
2
|
|
|
3
3
|
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
4
4
|
from hestia_earth.models.utils.constant import Units, get_atomic_conversion
|
|
5
|
+
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
5
6
|
from hestia_earth.models.utils.emission import _new_emission
|
|
6
7
|
from hestia_earth.models.utils.input import total_excreta
|
|
7
8
|
from hestia_earth.models.utils.excretaManagement import get_lookup_factor
|
|
@@ -9,6 +10,7 @@ from . import MODEL
|
|
|
9
10
|
|
|
10
11
|
REQUIREMENTS = {
|
|
11
12
|
"Cycle": {
|
|
13
|
+
"completeness.excreta": "True",
|
|
12
14
|
"practices": [
|
|
13
15
|
{"@type": "Practice", "value": "", "term.termType": "excretaManagement"}
|
|
14
16
|
]
|
|
@@ -42,10 +44,12 @@ def _run(excretaKgN: float, N2O_N_EF: float):
|
|
|
42
44
|
def _should_run(cycle: dict):
|
|
43
45
|
excretaKgN = total_excreta(cycle.get('inputs', []))
|
|
44
46
|
N2O_N_EF = get_lookup_factor(cycle.get('practices', []), LOOKUPS['excretaManagement'])
|
|
47
|
+
term_type_complete = _is_term_type_complete(cycle, TermTermType.EXCRETA)
|
|
45
48
|
|
|
46
49
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
47
50
|
excretaKgN=excretaKgN,
|
|
48
|
-
N2O_N_EF=N2O_N_EF
|
|
51
|
+
N2O_N_EF=N2O_N_EF,
|
|
52
|
+
term_type_excreta_complete=term_type_complete)
|
|
49
53
|
|
|
50
54
|
should_run = all([excretaKgN, N2O_N_EF])
|
|
51
55
|
logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
|
|
@@ -88,7 +88,7 @@ def _should_run(cycle: dict):
|
|
|
88
88
|
no3_n=no3_n,
|
|
89
89
|
nh3_n=nh3_n,
|
|
90
90
|
nox_n=nox_n,
|
|
91
|
-
|
|
91
|
+
term_type_excreta_complete=term_type_complete)
|
|
92
92
|
|
|
93
93
|
should_run = all([no3_n is not None, nh3_n is not None, nox_n is not None, term_type_complete])
|
|
94
94
|
logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
|
|
@@ -97,7 +97,7 @@ def _should_run(cycle: dict):
|
|
|
97
97
|
flooded_rice = has_flooded_rice(cycle.get('products', []))
|
|
98
98
|
|
|
99
99
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
100
|
-
|
|
100
|
+
term_type_fertiliser_complete=term_type_complete,
|
|
101
101
|
N_total=N_total,
|
|
102
102
|
has_flooded_rice=flooded_rice,
|
|
103
103
|
ecoClimateZone=ecoClimateZone)
|