hestia-earth-models 0.59.2__py3-none-any.whl → 0.59.4__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/geospatialDatabase/potentialEvapotranspirationMonthly.py +98 -0
- hestia_earth/models/geospatialDatabase/precipitationMonthly.py +98 -0
- hestia_earth/models/geospatialDatabase/temperatureAnnual.py +1 -2
- hestia_earth/models/geospatialDatabase/temperatureMonthly.py +98 -0
- hestia_earth/models/geospatialDatabase/utils.py +8 -1
- hestia_earth/models/ipcc2006/n2OToAirCropResidueDecompositionDirect.py +83 -0
- hestia_earth/models/ipcc2006/utils.py +12 -0
- hestia_earth/models/ipcc2019/nh3ToAirInorganicFertiliser.py +112 -0
- hestia_earth/models/ipcc2019/nh3ToAirOrganicFertiliser.py +6 -3
- hestia_earth/models/ipcc2019/noxToAirInorganicFertiliser.py +112 -0
- hestia_earth/models/ipcc2019/noxToAirOrganicFertiliser.py +6 -3
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +13 -28
- hestia_earth/models/mocking/search-results.json +8 -8
- hestia_earth/models/site/management.py +6 -6
- hestia_earth/models/site/pre_checks/cache_geospatialDatabase.py +27 -7
- hestia_earth/models/site/utils.py +2 -6
- hestia_earth/models/utils/__init__.py +9 -0
- hestia_earth/models/utils/site.py +8 -5
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.59.2.dist-info → hestia_earth_models-0.59.4.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.59.2.dist-info → hestia_earth_models-0.59.4.dist-info}/RECORD +34 -22
- tests/models/geospatialDatabase/test_potentialEvapotranspirationMonthly.py +20 -0
- tests/models/geospatialDatabase/test_precipitationMonthly.py +20 -0
- tests/models/geospatialDatabase/test_temperatureMonthly.py +20 -0
- tests/models/ipcc2006/test_n2OToAirCropResidueDecompositionDirect.py +50 -0
- tests/models/ipcc2019/test_nh3ToAirInorganicFertiliser.py +47 -0
- tests/models/ipcc2019/test_noxToAirInorganicFertiliser.py +47 -0
- tests/models/ipcc2019/test_organicCarbonPerHa.py +10 -22
- tests/models/site/test_management.py +13 -13
- tests/models/utils/test_blank_node.py +5 -5
- tests/models/utils/test_site.py +1 -1
- {hestia_earth_models-0.59.2.dist-info → hestia_earth_models-0.59.4.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.59.2.dist-info → hestia_earth_models-0.59.4.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.59.2.dist-info → hestia_earth_models-0.59.4.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Must be associated with at least 1 `Cycle` that has an
|
|
3
|
+
[endDate](https://hestia.earth/schema/Cycle#endDate) after `1979-01-01` and before `2020-06-01`.
|
|
4
|
+
"""
|
|
5
|
+
from hestia_earth.schema import MeasurementMethodClassification
|
|
6
|
+
from hestia_earth.utils.tools import flatten, non_empty_list
|
|
7
|
+
|
|
8
|
+
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
9
|
+
from hestia_earth.models.utils import first_day_of_month, last_day_of_month
|
|
10
|
+
from hestia_earth.models.utils.measurement import _new_measurement
|
|
11
|
+
from hestia_earth.models.utils.source import get_source
|
|
12
|
+
from hestia_earth.models.utils.site import related_years
|
|
13
|
+
from .utils import KELVIN_0, download, has_geospatial_data, should_download
|
|
14
|
+
from . import MODEL
|
|
15
|
+
|
|
16
|
+
REQUIREMENTS = {
|
|
17
|
+
"Site": {
|
|
18
|
+
"or": [
|
|
19
|
+
{"latitude": "", "longitude": ""},
|
|
20
|
+
{"boundary": {}},
|
|
21
|
+
{"region": {"@type": "Term", "termType": "region"}}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
RETURNS = {
|
|
26
|
+
"Measurement": [{
|
|
27
|
+
"value": "",
|
|
28
|
+
"dates": "",
|
|
29
|
+
"methodClassification": "geospatial dataset"
|
|
30
|
+
}]
|
|
31
|
+
}
|
|
32
|
+
TERM_ID = 'potentialEvapotranspirationMonthly'
|
|
33
|
+
EE_PARAMS = {
|
|
34
|
+
'collection': 'IDAHO_EPSCOR/TERRACLIMATE',
|
|
35
|
+
'band_name': 'pet',
|
|
36
|
+
'ee_type': 'raster',
|
|
37
|
+
'reducer': 'mean',
|
|
38
|
+
'reducer_annual': 'sum'
|
|
39
|
+
}
|
|
40
|
+
BIBLIO_TITLE = 'ERA5: Fifth generation of ECMWF atmospheric reanalyses of the global climate'
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _measurement(site: dict, value: list, dates: list):
|
|
44
|
+
measurement = _new_measurement(TERM_ID)
|
|
45
|
+
measurement['value'] = value
|
|
46
|
+
measurement['dates'] = dates
|
|
47
|
+
measurement['methodClassification'] = MeasurementMethodClassification.GEOSPATIAL_DATASET.value
|
|
48
|
+
return measurement | get_source(site, BIBLIO_TITLE)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _to_celcius(kelvin_value: int): return kelvin_value - KELVIN_0 if kelvin_value else None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _download(site: dict, start_date: str, end_date: str):
|
|
55
|
+
return download(
|
|
56
|
+
TERM_ID,
|
|
57
|
+
site,
|
|
58
|
+
{
|
|
59
|
+
**EE_PARAMS,
|
|
60
|
+
'start_date': start_date,
|
|
61
|
+
'end_date': end_date
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _run(site: dict, years: list):
|
|
67
|
+
# fetch from first year to last
|
|
68
|
+
years = range(years[0], years[-1] + 1) if len(years) > 1 else years
|
|
69
|
+
|
|
70
|
+
dates = flatten([
|
|
71
|
+
[
|
|
72
|
+
(first_day_of_month(year, month).strftime('%Y-%m-%d'), last_day_of_month(year, month).strftime('%Y-%m-%d'))
|
|
73
|
+
for month in range(1, 13)
|
|
74
|
+
] for year in years
|
|
75
|
+
])
|
|
76
|
+
values = non_empty_list([
|
|
77
|
+
(_to_celcius(_download(site, start_date, end_date)), start_date[0:7]) for start_date, end_date in dates
|
|
78
|
+
])
|
|
79
|
+
return _measurement(site, [v for v, d in values], [d for v, d in values])
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def run(site: dict):
|
|
83
|
+
contains_geospatial_data = has_geospatial_data(site)
|
|
84
|
+
below_max_area_size = should_download(TERM_ID, site)
|
|
85
|
+
|
|
86
|
+
years = related_years(site)
|
|
87
|
+
has_years = len(years) > 0
|
|
88
|
+
|
|
89
|
+
logRequirements(site, model=MODEL, term=TERM_ID,
|
|
90
|
+
contains_geospatial_data=contains_geospatial_data,
|
|
91
|
+
below_max_area_size=below_max_area_size,
|
|
92
|
+
has_years=has_years,
|
|
93
|
+
years=';'.join(map(lambda y: str(y), years)))
|
|
94
|
+
|
|
95
|
+
should_run = all([contains_geospatial_data, below_max_area_size, has_years])
|
|
96
|
+
logShouldRun(site, MODEL, TERM_ID, should_run)
|
|
97
|
+
|
|
98
|
+
return _run(site, years) if should_run else []
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Must be associated with at least 1 `Cycle` that has an
|
|
3
|
+
[endDate](https://hestia.earth/schema/Cycle#endDate) after `1979-01-01` and before `2020-06-01`.
|
|
4
|
+
"""
|
|
5
|
+
from hestia_earth.schema import MeasurementMethodClassification
|
|
6
|
+
from hestia_earth.utils.tools import flatten, non_empty_list
|
|
7
|
+
|
|
8
|
+
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
9
|
+
from hestia_earth.models.utils import first_day_of_month, last_day_of_month
|
|
10
|
+
from hestia_earth.models.utils.measurement import _new_measurement
|
|
11
|
+
from hestia_earth.models.utils.source import get_source
|
|
12
|
+
from hestia_earth.models.utils.site import related_years
|
|
13
|
+
from .utils import KELVIN_0, download, has_geospatial_data, should_download
|
|
14
|
+
from . import MODEL
|
|
15
|
+
|
|
16
|
+
REQUIREMENTS = {
|
|
17
|
+
"Site": {
|
|
18
|
+
"or": [
|
|
19
|
+
{"latitude": "", "longitude": ""},
|
|
20
|
+
{"boundary": {}},
|
|
21
|
+
{"region": {"@type": "Term", "termType": "region"}}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
RETURNS = {
|
|
26
|
+
"Measurement": [{
|
|
27
|
+
"value": "",
|
|
28
|
+
"dates": "",
|
|
29
|
+
"methodClassification": "geospatial dataset"
|
|
30
|
+
}]
|
|
31
|
+
}
|
|
32
|
+
TERM_ID = 'precipitationMonthly'
|
|
33
|
+
EE_PARAMS = {
|
|
34
|
+
'collection': 'ECMWF/ERA5/MONTHLY',
|
|
35
|
+
'band_name': 'total_precipitation',
|
|
36
|
+
'ee_type': 'raster',
|
|
37
|
+
'reducer': 'mean',
|
|
38
|
+
'reducer_annual': 'sum'
|
|
39
|
+
}
|
|
40
|
+
BIBLIO_TITLE = 'ERA5: Fifth generation of ECMWF atmospheric reanalyses of the global climate'
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _measurement(site: dict, value: list, dates: list):
|
|
44
|
+
measurement = _new_measurement(TERM_ID)
|
|
45
|
+
measurement['value'] = value
|
|
46
|
+
measurement['dates'] = dates
|
|
47
|
+
measurement['methodClassification'] = MeasurementMethodClassification.GEOSPATIAL_DATASET.value
|
|
48
|
+
return measurement | get_source(site, BIBLIO_TITLE)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _to_celcius(kelvin_value: int): return kelvin_value - KELVIN_0 if kelvin_value else None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _download(site: dict, start_date: str, end_date: str):
|
|
55
|
+
return download(
|
|
56
|
+
TERM_ID,
|
|
57
|
+
site,
|
|
58
|
+
{
|
|
59
|
+
**EE_PARAMS,
|
|
60
|
+
'start_date': start_date,
|
|
61
|
+
'end_date': end_date
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _run(site: dict, years: list):
|
|
67
|
+
# fetch from first year to last
|
|
68
|
+
years = range(years[0], years[-1] + 1) if len(years) > 1 else years
|
|
69
|
+
|
|
70
|
+
dates = flatten([
|
|
71
|
+
[
|
|
72
|
+
(first_day_of_month(year, month).strftime('%Y-%m-%d'), last_day_of_month(year, month).strftime('%Y-%m-%d'))
|
|
73
|
+
for month in range(1, 13)
|
|
74
|
+
] for year in years
|
|
75
|
+
])
|
|
76
|
+
values = non_empty_list([
|
|
77
|
+
(_to_celcius(_download(site, start_date, end_date)), start_date[0:7]) for start_date, end_date in dates
|
|
78
|
+
])
|
|
79
|
+
return _measurement(site, [v for v, d in values], [d for v, d in values])
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def run(site: dict):
|
|
83
|
+
contains_geospatial_data = has_geospatial_data(site)
|
|
84
|
+
below_max_area_size = should_download(TERM_ID, site)
|
|
85
|
+
|
|
86
|
+
years = related_years(site)
|
|
87
|
+
has_years = len(years) > 0
|
|
88
|
+
|
|
89
|
+
logRequirements(site, model=MODEL, term=TERM_ID,
|
|
90
|
+
contains_geospatial_data=contains_geospatial_data,
|
|
91
|
+
below_max_area_size=below_max_area_size,
|
|
92
|
+
has_years=has_years,
|
|
93
|
+
years=';'.join(map(lambda y: str(y), years)))
|
|
94
|
+
|
|
95
|
+
should_run = all([contains_geospatial_data, below_max_area_size, has_years])
|
|
96
|
+
logShouldRun(site, MODEL, TERM_ID, should_run)
|
|
97
|
+
|
|
98
|
+
return _run(site, years) if should_run else []
|
|
@@ -9,7 +9,7 @@ from hestia_earth.models.log import logRequirements, logShouldRun
|
|
|
9
9
|
from hestia_earth.models.utils.measurement import _new_measurement
|
|
10
10
|
from hestia_earth.models.utils.source import get_source
|
|
11
11
|
from hestia_earth.models.utils.site import related_years
|
|
12
|
-
from .utils import download, has_geospatial_data, should_download
|
|
12
|
+
from .utils import KELVIN_0, download, has_geospatial_data, should_download
|
|
13
13
|
from . import MODEL
|
|
14
14
|
|
|
15
15
|
REQUIREMENTS = {
|
|
@@ -38,7 +38,6 @@ EE_PARAMS = {
|
|
|
38
38
|
'reducer_annual': 'mean'
|
|
39
39
|
}
|
|
40
40
|
BIBLIO_TITLE = 'ERA5: Fifth generation of ECMWF atmospheric reanalyses of the global climate'
|
|
41
|
-
KELVIN_0 = 273.15
|
|
42
41
|
|
|
43
42
|
|
|
44
43
|
def _measurement(site: dict, value: float, year: int):
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Must be associated with at least 1 `Cycle` that has an
|
|
3
|
+
[endDate](https://hestia.earth/schema/Cycle#endDate) after `1979-01-01` and before `2020-06-01`.
|
|
4
|
+
"""
|
|
5
|
+
from hestia_earth.schema import MeasurementMethodClassification
|
|
6
|
+
from hestia_earth.utils.tools import flatten, non_empty_list
|
|
7
|
+
|
|
8
|
+
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
9
|
+
from hestia_earth.models.utils import first_day_of_month, last_day_of_month
|
|
10
|
+
from hestia_earth.models.utils.measurement import _new_measurement
|
|
11
|
+
from hestia_earth.models.utils.source import get_source
|
|
12
|
+
from hestia_earth.models.utils.site import related_years
|
|
13
|
+
from .utils import KELVIN_0, download, has_geospatial_data, should_download
|
|
14
|
+
from . import MODEL
|
|
15
|
+
|
|
16
|
+
REQUIREMENTS = {
|
|
17
|
+
"Site": {
|
|
18
|
+
"or": [
|
|
19
|
+
{"latitude": "", "longitude": ""},
|
|
20
|
+
{"boundary": {}},
|
|
21
|
+
{"region": {"@type": "Term", "termType": "region"}}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
RETURNS = {
|
|
26
|
+
"Measurement": [{
|
|
27
|
+
"value": "",
|
|
28
|
+
"dates": "",
|
|
29
|
+
"methodClassification": "geospatial dataset"
|
|
30
|
+
}]
|
|
31
|
+
}
|
|
32
|
+
TERM_ID = 'temperatureMonthly'
|
|
33
|
+
EE_PARAMS = {
|
|
34
|
+
'collection': 'ECMWF/ERA5/MONTHLY',
|
|
35
|
+
'band_name': 'mean_2m_air_temperature',
|
|
36
|
+
'ee_type': 'raster',
|
|
37
|
+
'reducer': 'mean',
|
|
38
|
+
'reducer_annual': 'mean'
|
|
39
|
+
}
|
|
40
|
+
BIBLIO_TITLE = 'ERA5: Fifth generation of ECMWF atmospheric reanalyses of the global climate'
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _measurement(site: dict, value: list, dates: list):
|
|
44
|
+
measurement = _new_measurement(TERM_ID)
|
|
45
|
+
measurement['value'] = value
|
|
46
|
+
measurement['dates'] = dates
|
|
47
|
+
measurement['methodClassification'] = MeasurementMethodClassification.GEOSPATIAL_DATASET.value
|
|
48
|
+
return measurement | get_source(site, BIBLIO_TITLE)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _to_celcius(kelvin_value: int): return kelvin_value - KELVIN_0 if kelvin_value else None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _download(site: dict, start_date: str, end_date: str):
|
|
55
|
+
return download(
|
|
56
|
+
TERM_ID,
|
|
57
|
+
site,
|
|
58
|
+
{
|
|
59
|
+
**EE_PARAMS,
|
|
60
|
+
'start_date': start_date,
|
|
61
|
+
'end_date': end_date
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _run(site: dict, years: list):
|
|
67
|
+
# fetch from first year to last
|
|
68
|
+
years = range(years[0], years[-1] + 1) if len(years) > 1 else years
|
|
69
|
+
|
|
70
|
+
dates = flatten([
|
|
71
|
+
[
|
|
72
|
+
(first_day_of_month(year, month).strftime('%Y-%m-%d'), last_day_of_month(year, month).strftime('%Y-%m-%d'))
|
|
73
|
+
for month in range(1, 13)
|
|
74
|
+
] for year in years
|
|
75
|
+
])
|
|
76
|
+
values = non_empty_list([
|
|
77
|
+
(_to_celcius(_download(site, start_date, end_date)), start_date[0:7]) for start_date, end_date in dates
|
|
78
|
+
])
|
|
79
|
+
return _measurement(site, [v for v, d in values], [d for v, d in values])
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def run(site: dict):
|
|
83
|
+
contains_geospatial_data = has_geospatial_data(site)
|
|
84
|
+
below_max_area_size = should_download(TERM_ID, site)
|
|
85
|
+
|
|
86
|
+
years = related_years(site)
|
|
87
|
+
has_years = len(years) > 0
|
|
88
|
+
|
|
89
|
+
logRequirements(site, model=MODEL, term=TERM_ID,
|
|
90
|
+
contains_geospatial_data=contains_geospatial_data,
|
|
91
|
+
below_max_area_size=below_max_area_size,
|
|
92
|
+
has_years=has_years,
|
|
93
|
+
years=';'.join(map(lambda y: str(y), years)))
|
|
94
|
+
|
|
95
|
+
should_run = all([contains_geospatial_data, below_max_area_size, has_years])
|
|
96
|
+
logShouldRun(site, MODEL, TERM_ID, should_run)
|
|
97
|
+
|
|
98
|
+
return _run(site, years) if should_run else []
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from hestia_earth.schema import TermTermType
|
|
3
3
|
from hestia_earth.utils.api import download_hestia
|
|
4
|
+
from hestia_earth.utils.tools import non_empty_list
|
|
4
5
|
|
|
5
6
|
from hestia_earth.models.log import debugValues, logErrorRun, logRequirements
|
|
6
7
|
from hestia_earth.models.utils.site import cached_value, region_factor, region_level_1_id
|
|
@@ -19,6 +20,7 @@ GEOPANDAS_COLLECTION_NAME = {
|
|
|
19
20
|
'gadm36_4': 'gadm/gadm36_4',
|
|
20
21
|
'gadm36_5': 'gadm/gadm36_5'
|
|
21
22
|
}
|
|
23
|
+
KELVIN_0 = 273.15
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
def use_geopandas(): return os.getenv('HEE_USE_GEOPANDAS', 'false') == 'true'
|
|
@@ -131,8 +133,13 @@ def _parse_run_query(term: str, query: dict):
|
|
|
131
133
|
|
|
132
134
|
def _get_cached_data(term: str, site: dict, data: dict):
|
|
133
135
|
cache = _cached_value(site, term)
|
|
136
|
+
cache_sub_key = '-'.join(non_empty_list([
|
|
137
|
+
data.get('year'),
|
|
138
|
+
data.get('start_date'),
|
|
139
|
+
data.get('end_date')
|
|
140
|
+
]))
|
|
134
141
|
# data can be grouped by year when required
|
|
135
|
-
value = cache.get(
|
|
142
|
+
value = cache.get(cache_sub_key) if cache_sub_key and cache is not None else cache
|
|
136
143
|
if value is not None:
|
|
137
144
|
debugValues(site, model=MODEL, term=term, value_from_cache=value)
|
|
138
145
|
return value
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from hestia_earth.schema import EmissionMethodTier, EmissionStatsDefinition, TermTermType
|
|
2
|
+
|
|
3
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues, log_as_table
|
|
4
|
+
from hestia_earth.models.utils.constant import Units, get_atomic_conversion
|
|
5
|
+
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
6
|
+
from hestia_earth.models.utils.emission import _new_emission
|
|
7
|
+
from hestia_earth.models.utils.cycle import get_crop_residue_decomposition_N_total
|
|
8
|
+
from hestia_earth.models.utils.product import has_flooded_rice
|
|
9
|
+
from .utils import N2O_FACTORS
|
|
10
|
+
from . import MODEL
|
|
11
|
+
|
|
12
|
+
REQUIREMENTS = {
|
|
13
|
+
"Cycle": {
|
|
14
|
+
"completeness.cropResidue": "True",
|
|
15
|
+
"products": [{
|
|
16
|
+
"@type": "Product",
|
|
17
|
+
"value": "",
|
|
18
|
+
"term.termType": "cropResidue",
|
|
19
|
+
"properties": [{"@type": "Property", "value": "", "term.@id": "nitrogenContent"}]
|
|
20
|
+
}],
|
|
21
|
+
"optional": {
|
|
22
|
+
"products": [{"@type": "Product", "term.@id": "riceGrainInHuskFlooded"}]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
RETURNS = {
|
|
27
|
+
"Emission": [{
|
|
28
|
+
"value": "",
|
|
29
|
+
"min": "",
|
|
30
|
+
"max": "",
|
|
31
|
+
"methodTier": "tier 1",
|
|
32
|
+
"statsDefinition": "modelled"
|
|
33
|
+
}]
|
|
34
|
+
}
|
|
35
|
+
LOOKUPS = {
|
|
36
|
+
"cropResidue": "decomposesOnField"
|
|
37
|
+
}
|
|
38
|
+
TERM_ID = 'n2OToAirCropResidueDecompositionDirect'
|
|
39
|
+
TIER = EmissionMethodTier.TIER_1.value
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _emission(value: float, min: float, max: float):
|
|
43
|
+
emission = _new_emission(TERM_ID, MODEL)
|
|
44
|
+
emission['value'] = [value]
|
|
45
|
+
emission['min'] = [min]
|
|
46
|
+
emission['max'] = [max]
|
|
47
|
+
emission['methodTier'] = TIER
|
|
48
|
+
emission['statsDefinition'] = EmissionStatsDefinition.MODELLED.value
|
|
49
|
+
return emission
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _run(cycle: dict, N_total: float):
|
|
53
|
+
flooded_rice = has_flooded_rice(cycle.get('products', []))
|
|
54
|
+
|
|
55
|
+
converted_N_total = N_total * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
|
|
56
|
+
factors = N2O_FACTORS['flooded_rice'] if flooded_rice else N2O_FACTORS['default']
|
|
57
|
+
|
|
58
|
+
debugValues(cycle, model=MODEL, term=TERM_ID,
|
|
59
|
+
has_flooded_rice=flooded_rice,
|
|
60
|
+
factors_used=log_as_table(factors))
|
|
61
|
+
|
|
62
|
+
value = converted_N_total * factors['value']
|
|
63
|
+
min = converted_N_total * factors['min']
|
|
64
|
+
max = converted_N_total * factors['max']
|
|
65
|
+
return [_emission(value, min, max)]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _should_run(cycle: dict):
|
|
69
|
+
term_type_complete = _is_term_type_complete(cycle, TermTermType.CROPRESIDUE)
|
|
70
|
+
N_crop_residue = get_crop_residue_decomposition_N_total(cycle)
|
|
71
|
+
|
|
72
|
+
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
73
|
+
term_type_cropResidue_complete=term_type_complete,
|
|
74
|
+
N_crop_residue=N_crop_residue)
|
|
75
|
+
|
|
76
|
+
should_run = all([N_crop_residue is not None, term_type_complete])
|
|
77
|
+
logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
|
|
78
|
+
return should_run, N_crop_residue
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def run(cycle: dict):
|
|
82
|
+
should_run, N_total = _should_run(cycle)
|
|
83
|
+
return _run(cycle, N_total) if should_run else []
|
|
@@ -2,6 +2,18 @@ from hestia_earth.utils.model import find_primary_product
|
|
|
2
2
|
|
|
3
3
|
COEFF_NH3NOX_N2O = 0.01
|
|
4
4
|
COEFF_NO3_N2O = 0.0075
|
|
5
|
+
N2O_FACTORS = {
|
|
6
|
+
'default': {
|
|
7
|
+
'value': 0.01,
|
|
8
|
+
'min': 0.003,
|
|
9
|
+
'max': 0.03
|
|
10
|
+
},
|
|
11
|
+
'flooded_rice': {
|
|
12
|
+
'value': 0.003,
|
|
13
|
+
'min': 0,
|
|
14
|
+
'max': 0.006
|
|
15
|
+
}
|
|
16
|
+
}
|
|
5
17
|
|
|
6
18
|
|
|
7
19
|
def get_N_N2O_excreta_coeff_from_primary_product(cycle: dict):
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
from hestia_earth.schema import EmissionMethodTier, EmissionStatsDefinition, TermTermType
|
|
2
|
+
from hestia_earth.utils.model import filter_list_term_type, find_term_match
|
|
3
|
+
from hestia_earth.utils.tools import list_sum
|
|
4
|
+
|
|
5
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues, log_as_table
|
|
6
|
+
from hestia_earth.models.utils.blank_node import get_N_total
|
|
7
|
+
from hestia_earth.models.utils.constant import Units, get_atomic_conversion
|
|
8
|
+
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
9
|
+
from hestia_earth.models.utils.emission import _new_emission
|
|
10
|
+
from hestia_earth.models.utils.cycle import get_inorganic_fertiliser_N_total
|
|
11
|
+
from hestia_earth.models.utils.term import get_lookup_value
|
|
12
|
+
from . import MODEL
|
|
13
|
+
|
|
14
|
+
REQUIREMENTS = {
|
|
15
|
+
"Cycle": {
|
|
16
|
+
"completeness.fertiliser": "True",
|
|
17
|
+
"inputs": [
|
|
18
|
+
{
|
|
19
|
+
"@type": "Input",
|
|
20
|
+
"value": "",
|
|
21
|
+
"term.termType": "inorganicFertiliser",
|
|
22
|
+
"optional": {
|
|
23
|
+
"properties": [{"@type": "Property", "value": "", "term.@id": "nitrogenContent"}]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
RETURNS = {
|
|
30
|
+
"Emission": [{
|
|
31
|
+
"value": "",
|
|
32
|
+
"sd": "",
|
|
33
|
+
"min": "",
|
|
34
|
+
"max": "",
|
|
35
|
+
"methodTier": "tier 1",
|
|
36
|
+
"statsDefinition": "modelled",
|
|
37
|
+
"methodModelDescription": ["Aggregated version", "Disaggragated version"]
|
|
38
|
+
}]
|
|
39
|
+
}
|
|
40
|
+
LOOKUPS = {
|
|
41
|
+
"inorganicFertiliser": ["IPCC_2019_FRACGASF_NH3-N", "IPCC_2019_FRACGASF_NH3-N-min", "IPCC_2019_FRACGASF_NH3-N-max"]
|
|
42
|
+
}
|
|
43
|
+
TERM_ID = 'nh3ToAirInorganicFertiliser'
|
|
44
|
+
TIER = EmissionMethodTier.TIER_1.value
|
|
45
|
+
TERM_TYPE = TermTermType.INORGANICFERTILISER
|
|
46
|
+
UNSPECIFIED_TERM_ID = 'inorganicNitrogenFertiliserUnspecifiedKgN'
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _emission(value: float, min: float, max: float, aggregated: bool = False):
|
|
50
|
+
emission = _new_emission(TERM_ID, MODEL)
|
|
51
|
+
emission['value'] = [value]
|
|
52
|
+
emission['min'] = [min]
|
|
53
|
+
emission['max'] = [max]
|
|
54
|
+
emission['methodTier'] = TIER
|
|
55
|
+
emission['statsDefinition'] = EmissionStatsDefinition.MODELLED.value
|
|
56
|
+
emission['methodModelDescription'] = 'Aggregated version' if aggregated else 'Disaggregated version'
|
|
57
|
+
return emission
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _input_values(input: dict):
|
|
61
|
+
N_total = list_sum(get_N_total([input]))
|
|
62
|
+
lookups = LOOKUPS[TERM_TYPE.value]
|
|
63
|
+
return {
|
|
64
|
+
'id': input.get('term', {}).get('@id'),
|
|
65
|
+
'N': N_total,
|
|
66
|
+
'value': get_lookup_value(input.get('term', {}), lookups[0]),
|
|
67
|
+
'min': get_lookup_value(input.get('term', {}), lookups[1]),
|
|
68
|
+
'max': get_lookup_value(input.get('term', {}), lookups[2])
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _filter_input_values(values: list, key: str): return [value for value in values if value.get(key)]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _run(cycle: dict):
|
|
76
|
+
inputs = filter_list_term_type(cycle.get('inputs', []), TERM_TYPE)
|
|
77
|
+
input_values = list(map(_input_values, inputs))
|
|
78
|
+
|
|
79
|
+
debugValues(cycle, model=MODEL, term=TERM_ID,
|
|
80
|
+
input_values=log_as_table(input_values))
|
|
81
|
+
|
|
82
|
+
value = list_sum([
|
|
83
|
+
v.get('N', 0) * v.get('value', 0) for v in _filter_input_values(input_values, 'value')
|
|
84
|
+
]) * get_atomic_conversion(Units.KG_NH3, Units.TO_N)
|
|
85
|
+
|
|
86
|
+
min = list_sum([
|
|
87
|
+
v.get('N', 0) * v.get('min', 0) for v in _filter_input_values(input_values, 'min')
|
|
88
|
+
]) * get_atomic_conversion(Units.KG_NH3, Units.TO_N)
|
|
89
|
+
|
|
90
|
+
max = list_sum([
|
|
91
|
+
v.get('N', 0) * v.get('max', 0) for v in _filter_input_values(input_values, 'max')
|
|
92
|
+
]) * get_atomic_conversion(Units.KG_NH3, Units.TO_N)
|
|
93
|
+
|
|
94
|
+
aggregated = list_sum(find_term_match(inputs, UNSPECIFIED_TERM_ID).get('value', [0])) > 0
|
|
95
|
+
|
|
96
|
+
return [_emission(value, min, max, aggregated=aggregated)]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _should_run(cycle: dict):
|
|
100
|
+
N_inorganic_fertiliser = get_inorganic_fertiliser_N_total(cycle)
|
|
101
|
+
fertiliser_complete = _is_term_type_complete(cycle, 'fertiliser')
|
|
102
|
+
|
|
103
|
+
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
104
|
+
N_inorganic_fertiliser=N_inorganic_fertiliser,
|
|
105
|
+
term_type_fertiliser_complete=fertiliser_complete)
|
|
106
|
+
|
|
107
|
+
should_run = all([N_inorganic_fertiliser is not None, fertiliser_complete])
|
|
108
|
+
logShouldRun(cycle, MODEL, TERM_ID, should_run)
|
|
109
|
+
return should_run
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def run(cycle: dict): return _run(cycle) if _should_run(cycle) else []
|
|
@@ -66,6 +66,9 @@ def _input_values(input: dict):
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
|
|
69
|
+
def _filter_input_values(values: list, key: str): return [value for value in values if value.get(key)]
|
|
70
|
+
|
|
71
|
+
|
|
69
72
|
def _run(cycle: dict):
|
|
70
73
|
inputs = filter_list_term_type(cycle.get('inputs', []), TermTermType.ORGANICFERTILISER)
|
|
71
74
|
input_values = list(map(_input_values, inputs))
|
|
@@ -74,15 +77,15 @@ def _run(cycle: dict):
|
|
|
74
77
|
input_values=log_as_table(input_values))
|
|
75
78
|
|
|
76
79
|
value = list_sum([
|
|
77
|
-
v.get('N', 0) * v.get('value', 0) for v in input_values
|
|
80
|
+
v.get('N', 0) * v.get('value', 0) for v in _filter_input_values(input_values, 'value')
|
|
78
81
|
]) * get_atomic_conversion(Units.KG_NH3, Units.TO_N)
|
|
79
82
|
|
|
80
83
|
min = list_sum([
|
|
81
|
-
v.get('N', 0) * v.get('min', 0) for v in input_values
|
|
84
|
+
v.get('N', 0) * v.get('min', 0) for v in _filter_input_values(input_values, 'min')
|
|
82
85
|
]) * get_atomic_conversion(Units.KG_NH3, Units.TO_N)
|
|
83
86
|
|
|
84
87
|
max = list_sum([
|
|
85
|
-
v.get('N', 0) * v.get('max', 0) for v in input_values
|
|
88
|
+
v.get('N', 0) * v.get('max', 0) for v in _filter_input_values(input_values, 'max')
|
|
86
89
|
]) * get_atomic_conversion(Units.KG_NH3, Units.TO_N)
|
|
87
90
|
|
|
88
91
|
return [_emission(value, min, max)]
|