hestia-earth-models 0.74.15__py3-none-any.whl → 0.74.17__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 (22) hide show
  1. hestia_earth/models/cache_nodes.py +9 -6
  2. hestia_earth/models/config/ImpactAssessment.json +0 -22
  3. hestia_earth/models/cycle/completeness/material.py +2 -3
  4. hestia_earth/models/hestia/resourceUse_utils.py +49 -20
  5. hestia_earth/models/ipcc2019/burning_utils.py +37 -0
  6. hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChange.py +10 -15
  7. hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChange.py +11 -16
  8. hestia_earth/models/ipcc2019/co2ToAirBiocharStockChange.py +7 -17
  9. hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +119 -55
  10. hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChange.py +10 -15
  11. hestia_earth/models/ipcc2019/nonCo2EmissionsToAirNaturalVegetationBurning.py +85 -109
  12. hestia_earth/models/ipcc2019/pastureGrass_utils.py +1 -1
  13. hestia_earth/models/log.py +4 -0
  14. hestia_earth/models/mocking/search-results.json +1 -1
  15. hestia_earth/models/utils/property.py +8 -6
  16. hestia_earth/models/version.py +1 -1
  17. {hestia_earth_models-0.74.15.dist-info → hestia_earth_models-0.74.17.dist-info}/METADATA +14 -6
  18. {hestia_earth_models-0.74.15.dist-info → hestia_earth_models-0.74.17.dist-info}/RECORD +22 -21
  19. {hestia_earth_models-0.74.15.dist-info → hestia_earth_models-0.74.17.dist-info}/WHEEL +1 -1
  20. tests/models/hestia/test_landTransformation20YearAverageDuringCycle.py +4 -8
  21. {hestia_earth_models-0.74.15.dist-info → hestia_earth_models-0.74.17.dist-info/licenses}/LICENSE +0 -0
  22. {hestia_earth_models-0.74.15.dist-info → hestia_earth_models-0.74.17.dist-info}/top_level.txt +0 -0
@@ -144,14 +144,17 @@ def _cache_sites(nodes: list, batch_size: int = _CACHE_BATCH_SIZE):
144
144
  return list(nodes_mapping.values())
145
145
 
146
146
 
147
- def run(nodes: list):
148
- init_gee()
149
-
147
+ def cache_nodes(nodes: list):
150
148
  # cache sites data
151
- cached_nodes = _cache_sites(nodes)
149
+ nodes = _cache_sites(nodes)
152
150
 
153
151
  # cache related nodes
154
- cached_nodes = _cache_related_nodes(cached_nodes) if _ENABLE_CACHE_RELATED_NODES else cached_nodes
152
+ nodes = _cache_related_nodes(nodes) if _ENABLE_CACHE_RELATED_NODES else nodes
155
153
 
156
154
  # cache sources
157
- return _cache_sources(cached_nodes)
155
+ return _cache_sources(nodes)
156
+
157
+
158
+ def run(nodes: list):
159
+ init_gee()
160
+ return cache_nodes(nodes)
@@ -133,17 +133,6 @@
133
133
  "replaceThreshold": ["value", 0.01]
134
134
  },
135
135
  "stage": 1
136
- },
137
- {
138
- "key": "emissionsResourceUse",
139
- "model": "linkedImpactAssessment",
140
- "value": "landTransformation100YearAverageInputsProduction",
141
- "runStrategy": "add_blank_node_if_missing",
142
- "mergeStrategy": "list",
143
- "mergeArgs": {
144
- "replaceThreshold": ["value", 0.01]
145
- },
146
- "stage": 1
147
136
  }
148
137
  ],
149
138
  [
@@ -158,17 +147,6 @@
158
147
  },
159
148
  "stage": 1
160
149
  },
161
- {
162
- "key": "emissionsResourceUse",
163
- "model": "hestia",
164
- "value": "landTransformation100YearAverageDuringCycle",
165
- "runStrategy": "always",
166
- "mergeStrategy": "list",
167
- "mergeArgs": {
168
- "replaceThreshold": ["value", 0.01]
169
- },
170
- "stage": 1
171
- },
172
150
  {
173
151
  "key": "emissionsResourceUse",
174
152
  "model": "cml2001Baseline",
@@ -9,7 +9,7 @@ REQUIREMENTS = {
9
9
  "inputs": [{"@type": "Input", "value": "", "term.@id": "machineryInfrastructureDepreciatedAmountPerCycle"}],
10
10
  "site": {
11
11
  "@type": "Site",
12
- "siteType": ["cropland", "glass or high accessible cover"]
12
+ "siteType": ["cropland"]
13
13
  }
14
14
  }
15
15
  }
@@ -20,8 +20,7 @@ RETURNS = {
20
20
  }
21
21
  MODEL_KEY = 'material'
22
22
  ALLOWED_SITE_TYPES = [
23
- SiteSiteType.CROPLAND.value,
24
- SiteSiteType.GLASS_OR_HIGH_ACCESSIBLE_COVER.value
23
+ SiteSiteType.CROPLAND.value
25
24
  ]
26
25
 
27
26
 
@@ -1,10 +1,12 @@
1
1
  from datetime import datetime
2
2
  from dateutil.relativedelta import relativedelta
3
3
  from hestia_earth.schema import TermTermType
4
- from hestia_earth.utils.tools import list_sum
4
+ from hestia_earth.utils.tools import list_sum, flatten
5
5
 
6
6
  from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
7
- from hestia_earth.models.utils.blank_node import _gapfill_datestr, DatestrGapfillMode, DatestrFormat, _str_dates_match
7
+ from hestia_earth.models.utils.blank_node import (
8
+ _gapfill_datestr, DatestrGapfillMode, DatestrFormat, _str_dates_match
9
+ )
8
10
  from hestia_earth.models.utils.impact_assessment import get_site
9
11
  from hestia_earth.models.utils.indicator import _new_indicator
10
12
  from .utils import LAND_USE_TERMS_FOR_TRANSFORMATION, crop_ipcc_land_use_category
@@ -42,7 +44,17 @@ def _find_closest_node_date(
42
44
  return filtered_dates[min(filtered_dates.keys())] if filtered_dates else ""
43
45
 
44
46
 
45
- def should_run(impact_assessment: dict, term_id: str, historic_date_offset: int) -> tuple[bool, dict, str, str]:
47
+ def _get_current_nodes(management_nodes: list, ia_date_str: str) -> list:
48
+ return [
49
+ node for node in management_nodes
50
+ if (
51
+ node.get("startDate") and node.get("endDate")
52
+ and node.get("startDate") <= ia_date_str <= node.get("endDate")
53
+ )
54
+ ]
55
+
56
+
57
+ def should_run(impact_assessment: dict, term_id: str, historic_date_offset: int) -> tuple[bool, list]:
46
58
  cycle = impact_assessment.get('cycle', {})
47
59
  has_otherSites = len(cycle.get('otherSites') or []) != 0
48
60
 
@@ -64,26 +76,45 @@ def should_run(impact_assessment: dict, term_id: str, historic_date_offset: int)
64
76
  node_date_field=match_date
65
77
  )
66
78
  closest_start_date, closest_end_date = (closest_date, None) if match_date == "startDate" else (None, closest_date)
67
- current_node_index = next(
68
- (i for i, node in enumerate(filtered_management_nodes)
69
- if _str_dates_match(
70
- date_str_one=node.get(match_date, ""),
71
- date_str_two=impact_assessment.get(match_date, ""),
72
- mode=match_mode
73
- )),
74
- None
75
- )
76
- current_node = filtered_management_nodes.pop(current_node_index) if current_node_index is not None else None
77
- landCover_term_id = (current_node or {}).get('term', {}).get('@id')
78
-
79
79
  prior_management_nodes = [
80
80
  node for node in filtered_management_nodes
81
81
  if _str_dates_match(node.get("endDate", ""), closest_end_date) or
82
82
  _str_dates_match(node.get("startDate", ""), closest_start_date)
83
83
  ]
84
84
 
85
- ipcc_land_use_category = crop_ipcc_land_use_category(landCover_term_id)
85
+ current_nodes = _get_current_nodes(
86
+ management_nodes=filtered_management_nodes,
87
+ ia_date_str=_gapfill_datestr(impact_assessment.get(match_date, ""), mode=match_mode)[:10],
88
+ )
86
89
 
90
+ should_run_node_results = [
91
+ should_run_node(
92
+ current_node=node,
93
+ closest_end_date=closest_end_date,
94
+ closest_start_date=closest_start_date,
95
+ has_otherSites=has_otherSites,
96
+ impact_assessment=impact_assessment,
97
+ prior_management_nodes=prior_management_nodes,
98
+ term_id=term_id
99
+ )
100
+ for node in current_nodes
101
+ ]
102
+ should_run_result = all([n[0] for n in should_run_node_results])
103
+ logShouldRun(impact_assessment, MODEL, term=term_id, should_run=should_run_result)
104
+ return should_run_result, flatten([n[1] for n in should_run_node_results])
105
+
106
+
107
+ def should_run_node(
108
+ current_node,
109
+ closest_end_date,
110
+ closest_start_date,
111
+ has_otherSites: bool,
112
+ impact_assessment: dict,
113
+ prior_management_nodes: list,
114
+ term_id: str
115
+ ) -> tuple[bool, list]:
116
+ landCover_term_id = (current_node or {}).get('term', {}).get('@id')
117
+ ipcc_land_use_category = crop_ipcc_land_use_category(landCover_term_id)
87
118
  total_landOccupationDuringCycle = list_sum([
88
119
  node.get("value") for node in impact_assessment.get("emissionsResourceUse", [])
89
120
  if node.get("term", {}).get("@id", "") == _RESOURCE_USE_TERM_ID
@@ -112,15 +143,13 @@ def should_run(impact_assessment: dict, term_id: str, historic_date_offset: int)
112
143
  ipcc_land_use_category=ipcc_land_use_category,
113
144
  indicators=log_as_table(indicators))
114
145
 
115
- should_run_result = all([
146
+ should_run_node_result = all([
116
147
  not has_otherSites,
117
148
  ipcc_land_use_category,
118
149
  total_landOccupationDuringCycle is not None,
119
150
  valid_indicators
120
151
  ])
121
- logShouldRun(impact_assessment, MODEL, term=term_id, should_run=should_run_result)
122
-
123
- return should_run_result, valid_indicators
152
+ return should_run_node_result, valid_indicators
124
153
 
125
154
 
126
155
  def run_resource_use(
@@ -0,0 +1,37 @@
1
+ from enum import Enum
2
+
3
+
4
+ class FuelCategory(Enum):
5
+ """
6
+ Natural vegetation fuel categories from IPCC (2019).
7
+ """
8
+ BOREAL_FOREST = "boreal-forest"
9
+ DRAINED_EXTRATROPICAL_ORGANIC_SOILS_WILDFIRE = "drained-extratropical-organic-soils-wildfire"
10
+ DRAINED_TROPICAL_ORGANIC_SOILS_WILDFIRE = "drained-tropical-organic-soils-wildfire"
11
+ EUCALYPT_FOREST = "eucalypt-forest"
12
+ NATURAL_TROPICAL_FOREST = "natural-tropical-forest" # mean of primary and secondary tropical forest
13
+ PEATLAND_VEGETATION = "peatland-vegetation"
14
+ PRIMARY_TROPICAL_FOREST = "primary-tropical-forest"
15
+ SAVANNA_GRASSLAND_EARLY_DRY_SEASON_BURNS = "savanna-grassland-early-dry-season-burns"
16
+ SAVANNA_GRASSLAND_MID_TO_LATE_DRY_SEASON_BURNS = "savanna-grassland-mid-to-late-dry-season-burns"
17
+ SAVANNA_WOODLAND_EARLY_DRY_SEASON_BURNS = "savanna-woodland-early-dry-season-burns"
18
+ SAVANNA_WOODLAND_MID_TO_LATE_DRY_SEASON_BURNS = "savanna-woodland-mid-to-late-dry-season-burns"
19
+ SECONDARY_TROPICAL_FOREST = "secondary-tropical-forest"
20
+ SHRUBLAND = "shrubland"
21
+ TEMPERATE_FOREST = "temperate-forest"
22
+ TERTIARY_TROPICAL_FOREST = "tertiary-tropical-forest"
23
+ TROPICAL_ORGANIC_SOILS_PRESCRIBED_FIRE = "tropical-organic-soils-prescribed-fire"
24
+ TUNDRA = "tundra"
25
+ UNDRAINED_EXTRATROPICAL_ORGANIC_SOILS_WILDFIRE = "undrained-extratropical-organic-soils-wildfire"
26
+ UNKNOWN_TROPICAL_FOREST = "unknown-tropical-forest" # mean of primary, secondary and tertiary tropical forest
27
+
28
+
29
+ class EmissionCategory(Enum):
30
+ """
31
+ Natural vegetation burning emission categories from IPCC (2019).
32
+ """
33
+ AGRICULTURAL_RESIDUES = "agricultural-residues"
34
+ BIOFUEL_BURNING = "biofuel-burning"
35
+ OTHER_FOREST = "other-forest"
36
+ SAVANNA_AND_GRASSLAND = "savanna-and-grassland"
37
+ TROPICAL_FOREST = "tropical-forest"
@@ -1,6 +1,5 @@
1
1
  from hestia_earth.schema import EmissionMethodTier, MeasurementMethodClassification
2
2
 
3
- from hestia_earth.models.log import logRequirements, logShouldRun
4
3
  from hestia_earth.models.utils.emission import _new_emission
5
4
 
6
5
  from .biomass_utils import detect_land_cover_change, get_valid_management_nodes, summarise_land_cover_nodes
@@ -42,8 +41,10 @@ RETURNS = {
42
41
  }
43
42
  TERM_ID = 'co2ToAirAboveGroundBiomassStockChangeLandUseChange,co2ToAirAboveGroundBiomassStockChangeManagementChange'
44
43
 
45
- _LU_EMISSION_TERM_ID = "co2ToAirAboveGroundBiomassStockChangeLandUseChange"
46
- _MG_EMISSION_TERM_ID = "co2ToAirAboveGroundBiomassStockChangeManagementChange"
44
+ _TERM_IDS = TERM_ID.split(",")
45
+
46
+ _LU_EMISSION_TERM_ID = _TERM_IDS[0]
47
+ _MG_EMISSION_TERM_ID = _TERM_IDS[1]
47
48
 
48
49
  _CARBON_STOCK_TERM_ID = 'aboveGroundBiomass'
49
50
 
@@ -122,23 +123,17 @@ def run(cycle: dict) -> list[dict]:
122
123
  """
123
124
  should_run_exec = create_should_run_function(
124
125
  _CARBON_STOCK_TERM_ID,
125
- measurements_mandatory=False, # Model can allocate zero emissions to LUC with enough landCover data
126
+ _LU_EMISSION_TERM_ID,
127
+ _MG_EMISSION_TERM_ID,
128
+ measurements_required=False, # Model can allocate zero emissions to LUC with enough landCover data
126
129
  measurement_method_ranking=_MEASUREMENT_METHOD_RANKING,
127
130
  get_valid_management_nodes_func=get_valid_management_nodes,
128
131
  summarise_land_use_func=summarise_land_cover_nodes,
129
132
  detect_land_use_change_func=detect_land_cover_change,
130
133
  )
131
134
 
132
- run_exec = create_run_function(
133
- new_emission_func=_emission,
134
- land_use_change_emission_term_id=_LU_EMISSION_TERM_ID,
135
- management_change_emission_term_id=_MG_EMISSION_TERM_ID
136
- )
137
-
138
- should_run, cycle_id, inventory, logs = should_run_exec(cycle)
135
+ run_exec = create_run_function(new_emission_func=_emission)
139
136
 
140
- for term_id in [_LU_EMISSION_TERM_ID, _MG_EMISSION_TERM_ID]:
141
- logRequirements(cycle, model=MODEL, term=term_id, **logs)
142
- logShouldRun(cycle, MODEL, term_id, should_run)
137
+ should_run, assigned_emissions = should_run_exec(cycle)
143
138
 
144
- return run_exec(cycle_id, inventory) if should_run else []
139
+ return run_exec(assigned_emissions) if should_run else []
@@ -1,6 +1,5 @@
1
1
  from hestia_earth.schema import EmissionMethodTier
2
2
 
3
- from hestia_earth.models.log import logRequirements, logShouldRun
4
3
  from hestia_earth.models.utils.emission import _new_emission
5
4
 
6
5
  from .biomass_utils import detect_land_cover_change, get_valid_management_nodes, summarise_land_cover_nodes
@@ -43,8 +42,10 @@ RETURNS = {
43
42
  }
44
43
  TERM_ID = 'co2ToAirBelowGroundBiomassStockChangeLandUseChange,co2ToAirBelowGroundBiomassStockChangeManagementChange'
45
44
 
46
- _LU_EMISSION_TERM_ID = "co2ToAirBelowGroundBiomassStockChangeLandUseChange"
47
- _MG_EMISSION_TERM_ID = "co2ToAirBelowGroundBiomassStockChangeManagementChange"
45
+ _TERM_IDS = TERM_ID.split(",")
46
+
47
+ _LU_EMISSION_TERM_ID = _TERM_IDS[0]
48
+ _MG_EMISSION_TERM_ID = _TERM_IDS[1]
48
49
 
49
50
  _DEPTH_UPPER = 0
50
51
  _DEPTH_LOWER = 30
@@ -114,24 +115,18 @@ def run(cycle: dict) -> list[dict]:
114
115
  """
115
116
  should_run_exec = create_should_run_function(
116
117
  _CARBON_STOCK_TERM_ID,
118
+ _LU_EMISSION_TERM_ID,
119
+ _MG_EMISSION_TERM_ID,
117
120
  depth_upper=_DEPTH_UPPER,
118
121
  depth_lower=_DEPTH_LOWER,
119
- measurements_mandatory=False, # Model can allocate zero emissions to LUC with enough landCover data
122
+ measurements_required=False, # Model can allocate zero emissions to LUC with enough landCover data
120
123
  get_valid_management_nodes_func=get_valid_management_nodes,
121
124
  summarise_land_use_func=summarise_land_cover_nodes,
122
- detect_land_use_change_func=detect_land_cover_change
123
- )
124
-
125
- run_exec = create_run_function(
126
- new_emission_func=_emission,
127
- land_use_change_emission_term_id=_LU_EMISSION_TERM_ID,
128
- management_change_emission_term_id=_MG_EMISSION_TERM_ID
125
+ detect_land_use_change_func=detect_land_cover_change,
129
126
  )
130
127
 
131
- should_run, cycle_id, inventory, logs = should_run_exec(cycle)
128
+ run_exec = create_run_function(new_emission_func=_emission)
132
129
 
133
- for term_id in [_LU_EMISSION_TERM_ID, _MG_EMISSION_TERM_ID]:
134
- logRequirements(cycle, model=MODEL, term=term_id, **logs)
135
- logShouldRun(cycle, MODEL, term_id, should_run)
130
+ should_run, assigned_emissions = should_run_exec(cycle)
136
131
 
137
- return run_exec(cycle_id, inventory) if should_run else []
132
+ return run_exec(assigned_emissions) if should_run else []
@@ -1,7 +1,6 @@
1
1
  from hestia_earth.schema import EmissionMethodTier
2
2
  from hestia_earth.utils.date import YEAR
3
3
 
4
- from hestia_earth.models.log import logRequirements, logShouldRun
5
4
  from hestia_earth.models.utils.emission import _new_emission
6
5
 
7
6
  from .co2ToAirCarbonStockChange_utils import create_run_function, create_should_run_function
@@ -116,25 +115,16 @@ def run(cycle: dict) -> list[dict]:
116
115
  """
117
116
  should_run_exec = create_should_run_function(
118
117
  _CARBON_STOCK_TERM_ID,
118
+ land_use_change_emission_term_id=None, # All emissions allocated to management change
119
+ management_change_emission_term_id=TERM_ID,
119
120
  depth_upper=_DEPTH_UPPER,
120
121
  depth_lower=_DEPTH_LOWER,
121
- transition_period=_TRANSITION_PERIOD_DAYS
122
+ transition_period=_TRANSITION_PERIOD_DAYS,
123
+ exclude_from_logs=_EXCLUDE_FROM_LOGS
122
124
  )
123
125
 
124
- run_exec = create_run_function(
125
- new_emission_func=_emission,
126
- land_use_change_emission_term_id=None, # All emissions allocated to management change
127
- management_change_emission_term_id=TERM_ID
128
- )
126
+ run_exec = create_run_function(new_emission_func=_emission)
129
127
 
130
- should_run, cycle_id, inventory, logs = should_run_exec(cycle)
131
-
132
- logRequirements(
133
- cycle,
134
- model=MODEL,
135
- term=TERM_ID,
136
- **{k: v for k, v in logs.items() if k not in _EXCLUDE_FROM_LOGS}
137
- )
138
- logShouldRun(cycle, MODEL, TERM_ID, should_run)
128
+ should_run, assigned_emissions = should_run_exec(cycle)
139
129
 
140
- return run_exec(cycle_id, inventory) if should_run else []
130
+ return run_exec(assigned_emissions) if should_run else []