hestia-earth-models 0.58.0__py3-none-any.whl → 0.59.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.

Files changed (35) hide show
  1. hestia_earth/models/cycle/{irrigated.py → irrigatedTypeUnspecified.py} +4 -4
  2. hestia_earth/models/cycle/residueIncorporated.py +1 -1
  3. hestia_earth/models/emepEea2019/nh3ToAirInorganicFertiliser.py +2 -2
  4. hestia_earth/models/geospatialDatabase/clayContent.py +17 -4
  5. hestia_earth/models/geospatialDatabase/sandContent.py +17 -4
  6. hestia_earth/models/impact_assessment/irrigated.py +0 -3
  7. hestia_earth/models/ipcc2019/co2ToAirSoilCarbonStockChangeManagementChange.py +10 -9
  8. hestia_earth/models/ipcc2019/n2OToAirCropResidueDecompositionDirect.py +4 -51
  9. hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserDirect.py +104 -0
  10. hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserDirect.py +105 -0
  11. hestia_earth/models/ipcc2019/organicCarbonPerHa.py +1059 -1220
  12. hestia_earth/models/ipcc2019/utils.py +82 -1
  13. hestia_earth/models/mocking/search-results.json +165 -95
  14. hestia_earth/models/site/management.py +12 -9
  15. hestia_earth/models/site/organicCarbonPerHa.py +251 -89
  16. hestia_earth/models/utils/blank_node.py +157 -34
  17. hestia_earth/models/utils/cycle.py +6 -3
  18. hestia_earth/models/utils/measurement.py +1 -1
  19. hestia_earth/models/utils/site.py +1 -1
  20. hestia_earth/models/utils/term.py +46 -1
  21. hestia_earth/models/version.py +1 -1
  22. {hestia_earth_models-0.58.0.dist-info → hestia_earth_models-0.59.1.dist-info}/METADATA +4 -8
  23. {hestia_earth_models-0.58.0.dist-info → hestia_earth_models-0.59.1.dist-info}/RECORD +35 -31
  24. tests/models/cycle/{test_irrigated.py → test_irrigatedTypeUnspecified.py} +1 -1
  25. tests/models/geospatialDatabase/test_clayContent.py +9 -3
  26. tests/models/geospatialDatabase/test_sandContent.py +9 -3
  27. tests/models/ipcc2019/test_n2OToAirInorganicFertiliserDirect.py +74 -0
  28. tests/models/ipcc2019/test_n2OToAirOrganicFertiliserDirect.py +74 -0
  29. tests/models/ipcc2019/test_organicCarbonPerHa.py +303 -1044
  30. tests/models/site/test_organicCarbonPerHa.py +51 -5
  31. tests/models/utils/test_blank_node.py +102 -42
  32. tests/models/utils/test_term.py +17 -3
  33. {hestia_earth_models-0.58.0.dist-info → hestia_earth_models-0.59.1.dist-info}/LICENSE +0 -0
  34. {hestia_earth_models-0.58.0.dist-info → hestia_earth_models-0.59.1.dist-info}/WHEEL +0 -0
  35. {hestia_earth_models-0.58.0.dist-info → hestia_earth_models-0.59.1.dist-info}/top_level.txt +0 -0
@@ -24,7 +24,7 @@ RETURNS = {
24
24
  "value": ""
25
25
  }]
26
26
  }
27
- TERM_ID = 'irrigated'
27
+ TERM_ID = 'irrigatedTypeUnspecified'
28
28
  MIN_IRRIGATION_M3 = 250
29
29
 
30
30
 
@@ -34,6 +34,9 @@ def _practice(value: float):
34
34
  return practice
35
35
 
36
36
 
37
+ def _is_irrigation_practice(practice: dict): return practice.get('term', {}).get('units', '') in ['%', '% area']
38
+
39
+
37
40
  def _run(irrigation_m3: float):
38
41
  value = 100 if irrigation_m3 > MIN_IRRIGATION_M3 else 0
39
42
  return [_practice(value)]
@@ -61,9 +64,6 @@ def _should_run(cycle: dict):
61
64
  return should_run, irrigation_value_m3
62
65
 
63
66
 
64
- def _is_irrigation_practice(practice: dict): return practice.get('term', {}).get('units', '') in ['%', '% area']
65
-
66
-
67
67
  def run(cycle: dict):
68
68
  should_run, irrigation_m3 = _should_run(cycle)
69
69
  return _run(irrigation_m3) if should_run else []
@@ -15,7 +15,7 @@ REQUIREMENTS = {
15
15
  {"@type": "Product", "term.@id": "aboveGroundCropResidueTotal", "value": "> 0"},
16
16
  {"@type": "Product", "term.@id": "aboveGroundCropResidueIncorporated", "value": "> 0"}
17
17
  ],
18
- "not": {
18
+ "none": {
19
19
  "practices": [{
20
20
  "@type": "Practice",
21
21
  "term.@id": [
@@ -42,11 +42,11 @@ LOOKUPS = {
42
42
  RETURNS = {
43
43
  "Emission": [{
44
44
  "value": "",
45
- "methodTier": "tier 1"
45
+ "methodTier": "tier 2"
46
46
  }]
47
47
  }
48
48
  TERM_ID = 'nh3ToAirInorganicFertiliser'
49
- TIER = EmissionMethodTier.TIER_1.value
49
+ TIER = EmissionMethodTier.TIER_2.value
50
50
  UNSPECIFIED_TERM_ID = 'inorganicNitrogenFertiliserUnspecifiedKgN'
51
51
 
52
52
 
@@ -1,7 +1,8 @@
1
1
  from hestia_earth.schema import MeasurementMethodClassification
2
2
 
3
3
  from hestia_earth.models.log import logRequirements, logShouldRun
4
- from hestia_earth.models.utils.measurement import _new_measurement
4
+ from hestia_earth.models.utils.blank_node import has_original_by_ids
5
+ from hestia_earth.models.utils.measurement import SOIL_TEXTURE_IDS, _new_measurement
5
6
  from hestia_earth.models.utils.source import get_source
6
7
  from .utils import download, has_geospatial_data, should_download
7
8
  from . import MODEL
@@ -12,7 +13,17 @@ REQUIREMENTS = {
12
13
  {"latitude": "", "longitude": ""},
13
14
  {"boundary": {}},
14
15
  {"region": {"@type": "Term", "termType": "region"}}
15
- ]
16
+ ],
17
+ "none": {
18
+ "measurements": [{
19
+ "@type": "Measurement",
20
+ "term.@id": [
21
+ "clayContent",
22
+ "sandContent",
23
+ "siltContent"
24
+ ]
25
+ }]
26
+ }
16
27
  }
17
28
  }
18
29
  RETURNS = {
@@ -54,12 +65,14 @@ def _run(site: dict):
54
65
  def _should_run(site: dict):
55
66
  contains_geospatial_data = has_geospatial_data(site)
56
67
  below_max_area_size = should_download(TERM_ID, site)
68
+ has_original_texture_measurements = has_original_by_ids(site.get('measurements', []), SOIL_TEXTURE_IDS)
57
69
 
58
70
  logRequirements(site, model=MODEL, term=TERM_ID,
59
71
  contains_geospatial_data=contains_geospatial_data,
60
- below_max_area_size=below_max_area_size)
72
+ below_max_area_size=below_max_area_size,
73
+ has_original_texture_measurements=has_original_texture_measurements)
61
74
 
62
- should_run = all([contains_geospatial_data, below_max_area_size])
75
+ should_run = all([contains_geospatial_data, below_max_area_size, not has_original_texture_measurements])
63
76
  logShouldRun(site, MODEL, TERM_ID, should_run)
64
77
  return should_run
65
78
 
@@ -1,7 +1,8 @@
1
1
  from hestia_earth.schema import MeasurementMethodClassification
2
2
 
3
3
  from hestia_earth.models.log import logRequirements, logShouldRun
4
- from hestia_earth.models.utils.measurement import _new_measurement
4
+ from hestia_earth.models.utils.blank_node import has_original_by_ids
5
+ from hestia_earth.models.utils.measurement import SOIL_TEXTURE_IDS, _new_measurement
5
6
  from hestia_earth.models.utils.source import get_source
6
7
  from .utils import download, has_geospatial_data, should_download
7
8
  from . import MODEL
@@ -12,7 +13,17 @@ REQUIREMENTS = {
12
13
  {"latitude": "", "longitude": ""},
13
14
  {"boundary": {}},
14
15
  {"region": {"@type": "Term", "termType": "region"}}
15
- ]
16
+ ],
17
+ "none": {
18
+ "measurements": [{
19
+ "@type": "Measurement",
20
+ "term.@id": [
21
+ "clayContent",
22
+ "sandContent",
23
+ "siltContent"
24
+ ]
25
+ }]
26
+ }
16
27
  }
17
28
  }
18
29
  RETURNS = {
@@ -54,12 +65,14 @@ def _run(site: dict):
54
65
  def _should_run(site: dict):
55
66
  contains_geospatial_data = has_geospatial_data(site)
56
67
  below_max_area_size = should_download(TERM_ID, site)
68
+ has_original_texture_measurements = has_original_by_ids(site.get('measurements', []), SOIL_TEXTURE_IDS)
57
69
 
58
70
  logRequirements(site, model=MODEL, term=TERM_ID,
59
71
  contains_geospatial_data=contains_geospatial_data,
60
- below_max_area_size=below_max_area_size)
72
+ below_max_area_size=below_max_area_size,
73
+ has_original_texture_measurements=has_original_texture_measurements)
61
74
 
62
- should_run = all([contains_geospatial_data, below_max_area_size])
75
+ should_run = all([contains_geospatial_data, below_max_area_size, not has_original_texture_measurements])
63
76
  logShouldRun(site, MODEL, TERM_ID, should_run)
64
77
  return should_run
65
78
 
@@ -18,9 +18,6 @@ REQUIREMENTS = {
18
18
  RETURNS = {
19
19
  "`true` if the `Cycle` was irrigated, `false` otherwise": ""
20
20
  }
21
- LOOKUPS = {
22
- "waterRegime": "irrigated"
23
- }
24
21
  MODEL_KEY = 'irrigated'
25
22
 
26
23
 
@@ -580,22 +580,22 @@ def _calc_grouped_soc_stock_changes(grouped_soc_stocks: dict) -> dict:
580
580
 
581
581
  def _calc_sum_cycle_occupancy(cycles: list[dict]) -> float:
582
582
  """
583
- Calculate the sum of cycle occupancies based on the `fraction_of_year` field added by the `group_nodes_by_year`
584
- function.
583
+ Calculate the sum of cycle occupancies based on the `fraction_of_group_duration` field added by the
584
+ `group_nodes_by_year` function.
585
585
 
586
- If a cycle does not have the "fraction_of_year" key, it is treated as zero occupancy for that year.
586
+ If a cycle does not have the "fraction_of_group_duration" key, it is treated as zero occupancy for that year.
587
587
 
588
588
  Parameters
589
589
  ----------
590
590
  cycles : List[dict]
591
- List of cycles, where each cycle dictionary should contain a "fraction_of_year" key.
591
+ List of cycles, where each cycle dictionary should contain a "fraction_of_group_duration" key.
592
592
 
593
593
  Returns
594
594
  -------
595
595
  float
596
596
  The sum of cycle occupancies based on the fraction of the year.
597
597
  """
598
- return sum(cycle.get("fraction_of_year", 0) for cycle in cycles)
598
+ return sum(cycle.get("fraction_of_group_duration", 0) for cycle in cycles)
599
599
 
600
600
 
601
601
  def _calc_grouped_share_of_emissions(cycles: list[dict]) -> dict:
@@ -604,14 +604,14 @@ def _calc_grouped_share_of_emissions(cycles: list[dict]) -> dict:
604
604
  of an inventory year.
605
605
 
606
606
  This function groups cycles by year, then calculates the share of emissions for each cycle based on the
607
- "fraction_of_year" value. The share of emissions is normalized by the sum of cycle occupancies for the entire
608
- dataset to ensure the values represent a valid share.
607
+ "fraction_of_group_duration" value. The share of emissions is normalized by the sum of cycle occupancies for the
608
+ entire dataset to ensure the values represent a valid share.
609
609
 
610
610
  Parameters
611
611
  ----------
612
612
  cycles : List[dict]
613
613
  List of [Cycle nodes](https://www.hestia.earth/schema/Cycle), where each cycle dictionary should contain a
614
- "fraction_of_year" key added by the `group_nodes_by_year` function.
614
+ "fraction_of_group_duration" key added by the `group_nodes_by_year` function.
615
615
 
616
616
  Returns
617
617
  -------
@@ -623,7 +623,8 @@ def _calc_grouped_share_of_emissions(cycles: list[dict]) -> dict:
623
623
  return {
624
624
  year: {
625
625
  INNER_KEY: {
626
- cycle["@id"]: cycle.get("fraction_of_year", 0) / _calc_sum_cycle_occupancy(cycles) for cycle in cycles
626
+ cycle["@id"]: cycle.get("fraction_of_group_duration", 0) / _calc_sum_cycle_occupancy(cycles)
627
+ for cycle in cycles
627
628
  }
628
629
  } for year, cycles in grouped_cycles.items()
629
630
  }
@@ -1,15 +1,12 @@
1
1
  from hestia_earth.schema import EmissionMethodTier, EmissionStatsDefinition, TermTermType
2
- from hestia_earth.utils.model import filter_list_term_type
3
- from hestia_earth.utils.tools import safe_parse_float
4
2
 
5
3
  from hestia_earth.models.log import logRequirements, logShouldRun, debugValues, log_as_table
6
4
  from hestia_earth.models.utils.constant import Units, get_atomic_conversion
7
5
  from hestia_earth.models.utils.completeness import _is_term_type_complete
8
6
  from hestia_earth.models.utils.emission import _new_emission
9
7
  from hestia_earth.models.utils.cycle import get_ecoClimateZone, get_crop_residue_decomposition_N_total
10
- from hestia_earth.models.utils.ecoClimateZone import get_ecoClimateZone_lookup_value
11
- from hestia_earth.models.utils.term import get_lookup_value
12
8
  from hestia_earth.models.utils.product import has_flooded_rice
9
+ from .utils import get_N2O_factors
13
10
  from . import MODEL
14
11
 
15
12
  REQUIREMENTS = {
@@ -51,28 +48,6 @@ LOOKUPS = {
51
48
  }
52
49
  TERM_ID = 'n2OToAirCropResidueDecompositionDirect'
53
50
  TIER = EmissionMethodTier.TIER_1.value
54
- FACTORS = {
55
- 'dry': {
56
- 'value': 0.005,
57
- 'min': 0,
58
- 'max': 0.011
59
- },
60
- 'wet': {
61
- 'value': 0.006,
62
- 'min': 0.001,
63
- 'max': 0.011
64
- },
65
- 'default': {
66
- 'value': 0.01,
67
- 'min': 0.001,
68
- 'max': 0.018
69
- },
70
- 'flooded_rice': {
71
- 'value': 0.004,
72
- 'min': 0,
73
- 'max': 0.029
74
- }
75
- }
76
51
 
77
52
 
78
53
  def _emission(value: float, min: float, max: float, sd: float, aggregated: bool = False):
@@ -87,33 +62,11 @@ def _emission(value: float, min: float, max: float, sd: float, aggregated: bool
87
62
  return emission
88
63
 
89
64
 
90
- def _get_waterRegime_lookup(practice: dict, col: str):
91
- return safe_parse_float(get_lookup_value(practice.get('term', {}), col, model=MODEL, term=TERM_ID), None)
92
-
93
-
94
- def _flooded_rice_factors(cycle: dict):
95
- practices = filter_list_term_type(cycle.get('practices', []), TermTermType.WATERREGIME)
96
- practice = next((p for p in practices if _get_waterRegime_lookup(p, LOOKUPS['waterRegime'][0]) is not None), None)
97
-
98
- factors = {
99
- 'value': _get_waterRegime_lookup(practice, LOOKUPS['waterRegime'][0]),
100
- 'min': _get_waterRegime_lookup(practice, LOOKUPS['waterRegime'][1]),
101
- 'max': _get_waterRegime_lookup(practice, LOOKUPS['waterRegime'][2])
102
- } if practice else FACTORS['flooded_rice']
103
-
104
- return (factors, practice is None)
105
-
106
-
107
- def _is_wet(ecoClimateZone: str = None):
108
- return get_ecoClimateZone_lookup_value(ecoClimateZone, 'wet') == 1 if ecoClimateZone else None
109
-
110
-
111
65
  def _run(cycle: dict, N_total: float, ecoClimateZone: str = None, flooded_rice: bool = False):
112
66
  converted_N_total = N_total * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
113
- is_wet = _is_wet(ecoClimateZone)
114
- factors_key = 'default' if is_wet is None else 'wet' if is_wet else 'dry'
115
-
116
- factors, aggregated = _flooded_rice_factors(cycle) if flooded_rice else (FACTORS[factors_key], is_wet is None)
67
+ factors, aggregated = get_N2O_factors(
68
+ TERM_ID, cycle, TermTermType.CROPRESIDUE, ecoClimateZone=ecoClimateZone, flooded_rice=flooded_rice
69
+ )
117
70
 
118
71
  debugValues(cycle, model=MODEL, term=TERM_ID,
119
72
  factors_used=log_as_table(factors),
@@ -0,0 +1,104 @@
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_ecoClimateZone, get_inorganic_fertiliser_N_total
8
+ from hestia_earth.models.utils.product import has_flooded_rice
9
+ from .utils import get_N2O_factors
10
+ from . import MODEL
11
+
12
+ REQUIREMENTS = {
13
+ "Cycle": {
14
+ "completeness.fertiliser": "True",
15
+ "inputs": [{
16
+ "@type": "Input",
17
+ "value": "",
18
+ "term.units": ["kg", "kg N"],
19
+ "term.termType": "inorganicFertiliser",
20
+ "optional": {
21
+ "properties": [{"@type": "Property", "value": "", "term.@id": "nitrogenContent"}]
22
+ }
23
+ }],
24
+ "optional": {
25
+ "endDate": "",
26
+ "site": {
27
+ "@type": "Site",
28
+ "measurements": [
29
+ {"@type": "Measurement", "value": "", "term.@id": "ecoClimateZone"}
30
+ ]
31
+ },
32
+ "products": [{"@type": "Product", "term.@id": "riceGrainInHuskFlooded"}],
33
+ "practices": [{"@type": "Practice", "term.termType": "waterRegime"}]
34
+ }
35
+ }
36
+ }
37
+ RETURNS = {
38
+ "Emission": [{
39
+ "value": "",
40
+ "min": "",
41
+ "max": "",
42
+ "sd": "",
43
+ "methodTier": "tier 1",
44
+ "statsDefinition": "modelled",
45
+ "methodModelDescription": ["Aggregated version", "Disaggregated version"]
46
+ }]
47
+ }
48
+ LOOKUPS = {
49
+ "waterRegime": ["IPCC_2019_N2O_rice", "IPCC_2019_N2O_rice-min", "IPCC_2019_N2O_rice-max"]
50
+ }
51
+ TERM_ID = 'n2OToAirInorganicFertiliserDirect'
52
+ TIER = EmissionMethodTier.TIER_1.value
53
+
54
+
55
+ def _emission(value: float, min: float, max: float, sd: float, aggregated: bool = False):
56
+ emission = _new_emission(TERM_ID, MODEL)
57
+ emission['value'] = [value]
58
+ emission['min'] = [min]
59
+ emission['max'] = [max]
60
+ emission['sd'] = [sd]
61
+ emission['methodTier'] = TIER
62
+ emission['statsDefinition'] = EmissionStatsDefinition.MODELLED.value
63
+ emission['methodModelDescription'] = 'Aggregated version' if aggregated else 'Disaggregated version'
64
+ return emission
65
+
66
+
67
+ def _run(cycle: dict, N_total: float, ecoClimateZone: str = None, flooded_rice: bool = False):
68
+ converted_N_total = N_total * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
69
+ factors, aggregated = get_N2O_factors(
70
+ TERM_ID, cycle, TermTermType.INORGANICFERTILISER, ecoClimateZone=ecoClimateZone, flooded_rice=flooded_rice
71
+ )
72
+
73
+ debugValues(cycle, model=MODEL, term=TERM_ID,
74
+ factors_used=log_as_table(factors),
75
+ aggregated=aggregated)
76
+
77
+ value = converted_N_total * factors['value']
78
+ min = converted_N_total * factors['min']
79
+ max = converted_N_total * factors['max']
80
+ sd = converted_N_total * (factors['max'] - factors['min'])/4
81
+ return [_emission(value, min, max, sd, aggregated=aggregated)]
82
+
83
+
84
+ def _should_run(cycle: dict):
85
+ term_type_complete = _is_term_type_complete(cycle, 'fertiliser')
86
+ N_total = get_inorganic_fertiliser_N_total(cycle)
87
+ ecoClimateZone = get_ecoClimateZone(cycle)
88
+
89
+ flooded_rice = has_flooded_rice(cycle.get('products', []))
90
+
91
+ logRequirements(cycle, model=MODEL, term=TERM_ID,
92
+ term_type_cropResidue_complete=term_type_complete,
93
+ N_total=N_total,
94
+ has_flooded_rice=flooded_rice,
95
+ ecoClimateZone=ecoClimateZone)
96
+
97
+ should_run = all([N_total is not None, term_type_complete])
98
+ logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
99
+ return should_run, N_total, ecoClimateZone, flooded_rice
100
+
101
+
102
+ def run(cycle: dict):
103
+ should_run, N_total, ecoClimateZone, flooded_rice = _should_run(cycle)
104
+ return _run(cycle, N_total, ecoClimateZone, flooded_rice) if should_run else []
@@ -0,0 +1,105 @@
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_ecoClimateZone, get_organic_fertiliser_N_total
8
+ from hestia_earth.models.utils.product import has_flooded_rice
9
+ from .utils import get_N2O_factors
10
+ from . import MODEL
11
+
12
+ REQUIREMENTS = {
13
+ "Cycle": {
14
+ "completeness.fertiliser": "True",
15
+ "inputs": [{
16
+ "@type": "Input",
17
+ "value": "",
18
+ "term.units": ["kg", "kg N"],
19
+ "term.termType": "organicFertiliser",
20
+ "optional": {
21
+ "properties": [{"@type": "Property", "value": "", "term.@id": "nitrogenContent"}]
22
+ }
23
+ }],
24
+ "optional": {
25
+ "endDate": "",
26
+ "site": {
27
+ "@type": "Site",
28
+ "measurements": [
29
+ {"@type": "Measurement", "value": "", "term.@id": "ecoClimateZone"}
30
+ ]
31
+ },
32
+ "products": [{"@type": "Product", "term.@id": "riceGrainInHuskFlooded"}],
33
+ "practices": [{"@type": "Practice", "term.termType": "waterRegime"}]
34
+ }
35
+ }
36
+ }
37
+ RETURNS = {
38
+ "Emission": [{
39
+ "value": "",
40
+ "min": "",
41
+ "max": "",
42
+ "sd": "",
43
+ "methodTier": "tier 1",
44
+ "statsDefinition": "modelled",
45
+ "methodModelDescription": ["Aggregated version", "Disaggregated version"]
46
+ }]
47
+ }
48
+ LOOKUPS = {
49
+ "waterRegime": ["IPCC_2019_N2O_rice", "IPCC_2019_N2O_rice-min", "IPCC_2019_N2O_rice-max"]
50
+ }
51
+ TERM_ID = 'n2OToAirOrganicFertiliserDirect'
52
+ TIER = EmissionMethodTier.TIER_1.value
53
+
54
+
55
+ def _emission(value: float, min: float, max: float, sd: float, aggregated: bool = False):
56
+ emission = _new_emission(TERM_ID, MODEL)
57
+ emission['value'] = [value]
58
+ emission['min'] = [min]
59
+ emission['max'] = [max]
60
+ emission['sd'] = [sd]
61
+ emission['methodTier'] = TIER
62
+ emission['statsDefinition'] = EmissionStatsDefinition.MODELLED.value
63
+ emission['methodModelDescription'] = 'Aggregated version' if aggregated else 'Disaggregated version'
64
+ return emission
65
+
66
+
67
+ def _run(cycle: dict, N_total: float, ecoClimateZone: str = None, flooded_rice: bool = False):
68
+ converted_N_total = N_total * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
69
+ factors, aggregated = get_N2O_factors(
70
+ TERM_ID, cycle, TermTermType.ORGANICFERTILISER, ecoClimateZone=ecoClimateZone, flooded_rice=flooded_rice
71
+ )
72
+
73
+ debugValues(cycle, model=MODEL, term=TERM_ID,
74
+ factors_used=log_as_table(factors),
75
+ aggregated=aggregated)
76
+
77
+ print(N_total, get_atomic_conversion(Units.KG_N2O, Units.TO_N), factors['value'])
78
+ value = converted_N_total * factors['value']
79
+ min = converted_N_total * factors['min']
80
+ max = converted_N_total * factors['max']
81
+ sd = converted_N_total * (factors['max'] - factors['min'])/4
82
+ return [_emission(value, min, max, sd, aggregated=aggregated)]
83
+
84
+
85
+ def _should_run(cycle: dict):
86
+ term_type_complete = _is_term_type_complete(cycle, 'fertiliser')
87
+ N_total = get_organic_fertiliser_N_total(cycle)
88
+ ecoClimateZone = get_ecoClimateZone(cycle)
89
+
90
+ flooded_rice = has_flooded_rice(cycle.get('products', []))
91
+
92
+ logRequirements(cycle, model=MODEL, term=TERM_ID,
93
+ term_type_cropResidue_complete=term_type_complete,
94
+ N_total=N_total,
95
+ has_flooded_rice=flooded_rice,
96
+ ecoClimateZone=ecoClimateZone)
97
+
98
+ should_run = all([N_total is not None, term_type_complete])
99
+ logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
100
+ return should_run, N_total, ecoClimateZone, flooded_rice
101
+
102
+
103
+ def run(cycle: dict):
104
+ should_run, N_total, ecoClimateZone, flooded_rice = _should_run(cycle)
105
+ return _run(cycle, N_total, ecoClimateZone, flooded_rice) if should_run else []