hestia-earth-models 0.66.0__py3-none-any.whl → 0.67.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.
Files changed (65) hide show
  1. hestia_earth/models/cml2001Baseline/abioticResourceDepletionFossilFuels.py +23 -54
  2. hestia_earth/models/cml2001Baseline/resourceUseEnergyDepletionDuringCycle.py +147 -0
  3. hestia_earth/models/cml2001Baseline/resourceUseEnergyDepletionInputsProduction.py +40 -0
  4. hestia_earth/models/cml2001Baseline/resourceUseMineralsAndMetalsDuringCycle.py +80 -0
  5. hestia_earth/models/cml2001Baseline/resourceUseMineralsAndMetalsInputsProduction.py +40 -0
  6. hestia_earth/models/config/ImpactAssessment.json +1869 -1846
  7. hestia_earth/models/cycle/completeness/freshForage.py +7 -3
  8. hestia_earth/models/cycle/inorganicFertiliser.py +67 -17
  9. hestia_earth/models/cycle/input/hestiaAggregatedData.py +13 -10
  10. hestia_earth/models/{environmentalFootprintV3 → environmentalFootprintV3_1}/__init__.py +4 -3
  11. hestia_earth/models/{environmentalFootprintV3 → environmentalFootprintV3_1}/environmentalFootprintSingleOverallScore.py +42 -37
  12. hestia_earth/models/environmentalFootprintV3_1/marineEutrophicationPotential.py +36 -0
  13. hestia_earth/models/environmentalFootprintV3_1/scarcityWeightedWaterUse.py +40 -0
  14. hestia_earth/models/{environmentalFootprintV3 → environmentalFootprintV3_1}/soilQualityIndexLandTransformation.py +22 -14
  15. hestia_earth/models/{environmentalFootprintV3 → environmentalFootprintV3_1}/soilQualityIndexTotalLandUseEffects.py +17 -15
  16. hestia_earth/models/hestia/landTransformation100YearAverageDuringCycle.py +1 -1
  17. hestia_earth/models/hestia/landTransformation20YearAverageDuringCycle.py +1 -1
  18. hestia_earth/models/impact_assessment/product/value.py +1 -1
  19. hestia_earth/models/ipcc2019/aboveGroundBiomass.py +2 -2
  20. hestia_earth/models/ipcc2019/belowGroundBiomass.py +2 -2
  21. hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +2 -1
  22. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +6 -5
  23. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +3 -2
  24. hestia_earth/models/mocking/search-results.json +1200 -1068
  25. hestia_earth/models/site/management.py +2 -2
  26. hestia_earth/models/utils/__init__.py +6 -0
  27. hestia_earth/models/utils/aggregated.py +13 -10
  28. hestia_earth/models/utils/array_builders.py +4 -3
  29. hestia_earth/models/utils/blank_node.py +78 -21
  30. hestia_earth/models/utils/ecoClimateZone.py +2 -2
  31. hestia_earth/models/utils/impact_assessment.py +5 -4
  32. hestia_earth/models/utils/lookup.py +5 -5
  33. hestia_earth/models/utils/property.py +5 -2
  34. hestia_earth/models/version.py +1 -1
  35. hestia_earth/orchestrator/log.py +11 -0
  36. hestia_earth/orchestrator/models/__init__.py +8 -3
  37. {hestia_earth_models-0.66.0.dist-info → hestia_earth_models-0.67.1.dist-info}/METADATA +1 -1
  38. {hestia_earth_models-0.66.0.dist-info → hestia_earth_models-0.67.1.dist-info}/RECORD +64 -52
  39. tests/models/cml2001Baseline/test_abioticResourceDepletionFossilFuels.py +51 -87
  40. tests/models/cml2001Baseline/test_resourceUseEnergyDepletionDuringCycle.py +136 -0
  41. tests/models/cml2001Baseline/test_resourceUseEnergyDepletionInputsProduction.py +23 -0
  42. tests/models/cml2001Baseline/test_resourceUseMineralsAndMetalsDuringCycle.py +58 -0
  43. tests/models/cml2001Baseline/test_resourceUseMineralsAndMetalsInputsProduction.py +23 -0
  44. tests/models/{environmentalFootprintV3 → environmentalFootprintV3_1}/test_environmentalFootprintSingleOverallScore.py +43 -12
  45. tests/models/{environmentalFootprintV3 → environmentalFootprintV3_1}/test_freshwaterEcotoxicityPotentialCtue.py +6 -5
  46. tests/models/environmentalFootprintV3_1/test_marineEutrophicationPotential.py +27 -0
  47. tests/models/environmentalFootprintV3_1/test_scarcityWeightedWaterUse.py +32 -0
  48. tests/models/{environmentalFootprintV3 → environmentalFootprintV3_1}/test_soilQualityIndexLandOccupation.py +4 -3
  49. tests/models/environmentalFootprintV3_1/test_soilQualityIndexLandTransformation.py +194 -0
  50. tests/models/{environmentalFootprintV3 → environmentalFootprintV3_1}/test_soilQualityIndexTotalLandUseEffects.py +4 -4
  51. tests/models/impact_assessment/test_emissions.py +0 -1
  52. tests/models/site/test_management.py +1 -4
  53. tests/models/test_config.py +3 -3
  54. tests/models/test_ecoinventV3.py +0 -1
  55. tests/models/utils/test_array_builders.py +2 -2
  56. tests/models/utils/test_blank_node.py +13 -165
  57. tests/orchestrator/models/test_transformations.py +4 -1
  58. tests/models/environmentalFootprintV3/test_soilQualityIndexLandTransformation.py +0 -164
  59. /hestia_earth/models/{environmentalFootprintV3 → environmentalFootprintV3_1}/freshwaterEcotoxicityPotentialCtue.py +0 -0
  60. /hestia_earth/models/{environmentalFootprintV3 → environmentalFootprintV3_1}/soilQualityIndexLandOccupation.py +0 -0
  61. /hestia_earth/models/{environmentalFootprintV3 → environmentalFootprintV3_1}/utils.py +0 -0
  62. {hestia_earth_models-0.66.0.dist-info → hestia_earth_models-0.67.1.dist-info}/LICENSE +0 -0
  63. {hestia_earth_models-0.66.0.dist-info → hestia_earth_models-0.67.1.dist-info}/WHEEL +0 -0
  64. {hestia_earth_models-0.66.0.dist-info → hestia_earth_models-0.67.1.dist-info}/top_level.txt +0 -0
  65. /tests/models/{environmentalFootprintV3 → environmentalFootprintV3_1}/__init__.py +0 -0
@@ -110,7 +110,7 @@ _PRACTICES_TERM_TYPES = [
110
110
  TermTermType.LANDUSEMANAGEMENT,
111
111
  TermTermType.SYSTEM
112
112
  ]
113
- _PRACTICES_COMPLETENESS_MAPPING = COMPLETENESS_MAPPING.get(SchemaType.PRACTICE.value)
113
+ _PRACTICES_COMPLETENESS_MAPPING = COMPLETENESS_MAPPING.get(SchemaType.PRACTICE.value, {})
114
114
  _ANIMAL_MANURE_USED_TERM_ID = "animalManureUsed"
115
115
  _INORGANIC_NITROGEN_FERTILISER_USED_TERM_ID = "inorganicNitrogenFertiliserUsed"
116
116
  _ORGANIC_FERTILISER_USED_TERM_ID = "organicFertiliserUsed"
@@ -242,7 +242,7 @@ def _get_relevant_items(cycle: dict, item_name: str, term_types: List[TermTermTy
242
242
  term_type for term_type in term_types
243
243
  if any([
244
244
  not completeness_mapping.get(term_type.value),
245
- cycle.get('completeness').get(completeness_mapping.get(term_type.value), False)
245
+ cycle.get('completeness', {}).get(completeness_mapping.get(term_type.value), False)
246
246
  ])
247
247
  ]
248
248
  blank_nodes = filter_list_term_type(cycle.get(item_name, []), complete_term_types)
@@ -165,6 +165,12 @@ def last_day_of_month(year: int, month: int):
165
165
  )
166
166
 
167
167
 
168
+ def current_date(): return datetime.datetime.now().date().strftime('%Y-%m-%d')
169
+
170
+
171
+ def current_year(): return int(current_date()[:4])
172
+
173
+
168
174
  def flatten_args(args) -> list:
169
175
  """
170
176
  Flatten the input args into a single list.
@@ -6,7 +6,8 @@ from hestia_earth.utils.model import find_term_match, linked_node
6
6
  from hestia_earth.utils.tools import safe_parse_date, non_empty_list
7
7
 
8
8
  from hestia_earth.models.log import debugValues, logShouldRun
9
- from hestia_earth.models.utils.cycle import is_organic
9
+ from . import current_year
10
+ from .cycle import is_organic
10
11
 
11
12
  MODEL_KEY = 'impactAssessment'
12
13
  MATCH_WORLD_QUERY = {'match': {'country.name.keyword': {'query': 'World', 'boost': 1}}}
@@ -14,7 +15,7 @@ MATCH_WORLD_QUERY = {'match': {'country.name.keyword': {'query': 'World', 'boost
14
15
 
15
16
  def aggregated_end_date(end_date: str):
16
17
  year = safe_parse_date(end_date).year
17
- return round(math.floor(year / 10) * 10) + 9
18
+ return min([round(math.floor(year / 10) * 10) + 9, current_year()])
18
19
 
19
20
 
20
21
  def _match_region_country(region: dict, country: dict):
@@ -36,8 +37,7 @@ def _match_region_country(region: dict, country: dict):
36
37
  }
37
38
 
38
39
 
39
- def find_closest_impact(cycle: dict, end_date: str, input: dict, region: dict, country: dict, must_queries=[]):
40
- term = input.get('term', {})
40
+ def find_closest_impact(cycle: dict, end_date: str, term: dict, region: dict, country: dict, must_queries=[]):
41
41
  query = {
42
42
  'bool': {
43
43
  'must': non_empty_list([
@@ -74,21 +74,24 @@ def find_closest_impact(cycle: dict, end_date: str, input: dict, region: dict, c
74
74
 
75
75
  def _link_input_to_impact(model: str, cycle: dict, date: int):
76
76
  def run(input: dict):
77
- term_id = input.get('term', {}).get('@id')
77
+ term = input.get('term', {})
78
+ term_id = term.get('@id')
78
79
  region = input.get('region')
79
80
  country = input.get('country')
80
- impact = find_closest_impact(cycle, date, input, region, country)
81
+ impact = find_closest_impact(cycle, date, term, region, country)
81
82
 
83
+ search_by_region_id = (region or country or {}).get('@id') or 'region-world'
82
84
  debugValues(cycle, model=model, term=term_id, key=MODEL_KEY,
83
- input_region=(region or {}).get('@id'),
84
- input_country=(country or {}).get('@id'),
85
- impact=(impact or {}).get('@id'))
85
+ search_by_input_term_id=term_id,
86
+ search_by_region_id=search_by_region_id,
87
+ search_by_end_date=str(date),
88
+ impact_assessment_id_found=(impact or {}).get('@id'))
86
89
 
87
90
  should_run = all([impact is not None])
88
91
  logShouldRun(cycle, model, term_id, should_run)
89
92
  logShouldRun(cycle, model, term_id, should_run, key=MODEL_KEY) # show specifically under Input
90
93
 
91
- return {**input, MODEL_KEY: linked_node(impact), 'impactAssessmentIsProxy': True} if impact else None
94
+ return input | {MODEL_KEY: linked_node(impact), 'impactAssessmentIsProxy': True} if impact else None
92
95
  return run
93
96
 
94
97
 
@@ -527,12 +527,13 @@ def avg_run_in_rowwise(arr: NDArray, n: int):
527
527
  return avg_run_in_columnwise(arr.transpose(), n).transpose()
528
528
 
529
529
 
530
- def gen_seed(node: dict) -> int:
530
+ def gen_seed(node: dict, *args: tuple[str]) -> int:
531
531
  """
532
- Generate a seed based on a node's `@id` so that rng is the same each time the model is re-run.
532
+ Generate a seed based on a node's `@id` and optional args so that rng is the same each time the model is re-run.
533
533
  """
534
534
  node_id = node.get("@id", "")
535
- hashed = hashlib.shake_128(node_id.encode(), usedforsecurity=False).hexdigest(4)
535
+ seed_str = "".join([node_id] + [str(arg) for arg in args])
536
+ hashed = hashlib.shake_128(seed_str.encode(), usedforsecurity=False).hexdigest(4)
536
537
  return abs(int(hashed, 16))
537
538
 
538
539
 
@@ -12,7 +12,6 @@ from typing import (
12
12
  Optional,
13
13
  Union
14
14
  )
15
-
16
15
  from dateutil import parser
17
16
  from dateutil.relativedelta import relativedelta
18
17
  from hestia_earth.schema import TermTermType
@@ -22,6 +21,7 @@ from hestia_earth.utils.model import filter_list_term_type
22
21
  from hestia_earth.utils.tools import (
23
22
  flatten,
24
23
  list_sum,
24
+ list_average,
25
25
  safe_parse_date,
26
26
  safe_parse_float,
27
27
  non_empty_list
@@ -35,7 +35,7 @@ from .lookup import (
35
35
  is_product_id_allowed, is_product_termType_allowed,
36
36
  is_input_id_allowed, is_input_termType_allowed, _node_value
37
37
  )
38
- from .property import get_node_property, get_node_property_value, find_term_property
38
+ from .property import get_node_property, get_node_property_value
39
39
  from .term import get_lookup_value
40
40
  from ..log import debugValues, log_as_table
41
41
 
@@ -44,6 +44,14 @@ MAX_DEPTH = 1000
44
44
  OLDEST_DATE = '1800'
45
45
 
46
46
 
47
+ def group_by_term(values: list):
48
+ def group_by(groups: dict, value: dict):
49
+ key = value.get('term', {}).get('@id')
50
+ groups[key] = groups.get(key, []) + [value]
51
+ return groups
52
+ return reduce(group_by, values, {})
53
+
54
+
47
55
  def merge_blank_nodes(source: list, new_values: list):
48
56
  """
49
57
  Merge a list of blank nodes into an existing list of blank nodes.
@@ -1335,11 +1343,14 @@ def _node_from_group(nodes: list):
1335
1343
  # `nodes` contain list with consecutive dates
1336
1344
  return nodes[0] if len(nodes) == 1 else (
1337
1345
  # if all nodes have the same dates, sum up the values
1338
- nodes[0] | {'value': _sum_nodes_value(nodes)} if _same_dates(nodes)
1339
- else nodes[0] | {
1340
- 'startDate': min(n.get('startDate') for n in nodes),
1341
- 'endDate': max(n.get('endDate') for n in nodes)
1342
- }
1346
+ nodes[0] | (
1347
+ {
1348
+ 'value': _sum_nodes_value(nodes)
1349
+ } if _same_dates(nodes) else {
1350
+ 'startDate': min(n.get('startDate') for n in nodes),
1351
+ 'endDate': max(n.get('endDate') for n in nodes)
1352
+ }
1353
+ )
1343
1354
  )
1344
1355
 
1345
1356
 
@@ -1349,7 +1360,7 @@ def _condense_nodes(nodes: list):
1349
1360
  return flatten(map(_node_from_group, grouped_nodes))
1350
1361
 
1351
1362
 
1352
- def _group_nodes_to_condense(nodes: list) -> dict:
1363
+ def _group_nodes_by_value_and_properties(nodes: list) -> dict:
1353
1364
  def _group_node(group: dict, node: dict):
1354
1365
  value = node.get('value', [])
1355
1366
  value = '-'.join(map(str, value if isinstance(value, list) else [value]))
@@ -1359,7 +1370,6 @@ def _group_nodes_to_condense(nodes: list) -> dict:
1359
1370
  f"{p.get('value')}"
1360
1371
  ])) for p in node.get('properties', [])
1361
1372
  ]))
1362
- # group by term, value, and properties
1363
1373
  group_key = '-'.join(non_empty_list([
1364
1374
  node.get('term', {}).get('@id', ''),
1365
1375
  value,
@@ -1371,8 +1381,51 @@ def _group_nodes_to_condense(nodes: list) -> dict:
1371
1381
  return reduce(_group_node, nodes, {})
1372
1382
 
1373
1383
 
1384
+ def _group_nodes_by_dates(nodes: list) -> dict:
1385
+ def _group_node(group: dict, node: dict):
1386
+ group_key = '-'.join(non_empty_list([
1387
+ node.get('term', {}).get('@id', ''),
1388
+ node.get('startDate'),
1389
+ node.get('endDate'),
1390
+ ]))
1391
+ group[group_key] = group.get(group_key, []) + [node]
1392
+ return group
1393
+
1394
+ return reduce(_group_node, nodes, {})
1395
+
1396
+
1397
+ def _average_properties(properties: list):
1398
+ # group properties by term
1399
+ grouped_properties = group_by_term(properties)
1400
+ return [
1401
+ props[0] | {
1402
+ 'value': list_average(non_empty_list([p.get('value') for p in props]), default=props[0].get('value'))
1403
+ }
1404
+ for props in grouped_properties.values()
1405
+ ]
1406
+
1407
+
1408
+ def _merge_same_dates(nodes: list):
1409
+ # group by term, startDate and endDate
1410
+ grouped_nodes = _group_nodes_by_dates(nodes)
1411
+
1412
+ def merge_nodes(nodes: list):
1413
+ properties = flatten([n.get('properties', []) for n in nodes])
1414
+ return nodes[0] | (
1415
+ {
1416
+ 'value': _sum_nodes_value(nodes)
1417
+ } | ({
1418
+ 'properties': _average_properties(properties)
1419
+ } if properties else {})
1420
+ ) if len(nodes) > 1 else nodes[0]
1421
+
1422
+ return list(map(merge_nodes, grouped_nodes.values()))
1423
+
1424
+
1374
1425
  def condense_nodes(nodes: list) -> list:
1375
- grouped_nodes = _group_nodes_to_condense(nodes)
1426
+ # merge nodes with the same term and dates as they need to be unique
1427
+ values = _merge_same_dates(nodes)
1428
+ grouped_nodes = _group_nodes_by_value_and_properties(values)
1376
1429
  return flatten(map(_condense_nodes, grouped_nodes.values()))
1377
1430
 
1378
1431
 
@@ -1446,13 +1499,13 @@ def most_relevant_blank_node_by_id(nodes: list, term_id: str, date: str):
1446
1499
  PROPERTY_UNITS_CONVERSIONS = {
1447
1500
  Units.KG.value: {
1448
1501
  Units.MJ.value: [
1449
- 'energyContentHigherHeatingValue', # "kg" to "mj"
1502
+ 'energyContentLowerHeatingValue', # "kg" to "mj"
1450
1503
  ]
1451
1504
  },
1452
1505
  Units.M3.value: {
1453
1506
  Units.MJ.value: [
1454
1507
  'density', # "m3" to "kg"
1455
- 'energyContentHigherHeatingValue', # "kg" to "mj"
1508
+ 'energyContentLowerHeatingValue', # "kg" to "mj"
1456
1509
  ]
1457
1510
  }
1458
1511
  }
@@ -1468,16 +1521,20 @@ def _convert_via_property(node: dict, node_value: Union[int, float], property_fi
1468
1521
 
1469
1522
  Parameters
1470
1523
  ----------
1471
- node: a dict containing a term
1472
- node_value: value to be converted as float or int
1473
- property_field: str such as "density"
1524
+ node: dict
1525
+ Blank node containing a term
1526
+ node_value: int | float
1527
+ Value to be converted as float or int
1528
+ property_field: str
1529
+ E.g., "density"
1474
1530
 
1475
- Returns float or None
1531
+ Returns
1476
1532
  -------
1533
+ Float or None
1477
1534
  """
1478
- node_property = find_term_property(node, property_field, default={}, keep_in_memory=True)
1479
- node_property_value = safe_parse_float(node_property.get("value", 0))
1480
-
1535
+ node_property_value = get_node_property_value(
1536
+ model=None, node=node, prop_id=property_field, default=0, handle_percents=False
1537
+ )
1481
1538
  return node_value * node_property_value if node_value is not None and bool(node_property_value) else None
1482
1539
 
1483
1540
 
@@ -1486,7 +1543,7 @@ def convert_unit(node, dest_unit: Units, node_value: Union[int, float] = None) -
1486
1543
  Convert a number `value` inside a node or a optional `node_value` belonging to a term `node`, to unit `dest_unit`
1487
1544
  using the ATOMIC_WEIGHT_CONVERSIONS map or failing that, the PROPERTY_UNITS_CONVERSIONS map and lookups
1488
1545
  """
1489
- src_unit = node.get("units", "")
1546
+ src_unit = node.get("units") or node.get('term', {}).get('units', "")
1490
1547
 
1491
1548
  node_value = _node_value(node) if node_value is None else node_value
1492
1549
 
@@ -1505,7 +1562,7 @@ def convert_unit_properties(node_value: Union[int, float], node: dict, dest_unit
1505
1562
  Uses cached calls to download_hestia() internally for speedup
1506
1563
  Returns None if no conversion possible.
1507
1564
  """
1508
- src_unit = node.get('units', '')
1565
+ src_unit = node.get("units") or node.get('term', {}).get('units', "")
1509
1566
  conversions = PROPERTY_UNITS_CONVERSIONS.get(src_unit, {}).get(dest_unit.value, [])
1510
1567
  return reduce(
1511
1568
  lambda value, conversion_property_field: _convert_via_property(node, value, conversion_property_field),
@@ -36,8 +36,8 @@ def get_eco_climate_zone_value(node: dict, as_enum: bool = False) -> Union[int,
36
36
  Parameters
37
37
  ----------
38
38
  node : dict
39
- A HESTIA [Site](https://www-staging.hestia.earth/schema/Site) or
40
- [Cycle](https://www-staging.hestia.earth/schema/Cycle).
39
+ A HESTIA [Site](https://hestia.earth/schema/Site) or
40
+ [Cycle](https://hestia.earth/schema/Cycle).
41
41
 
42
42
  Returns
43
43
  -------
@@ -131,6 +131,7 @@ def impact_country_value(
131
131
  lookup: str,
132
132
  group_key: str = None,
133
133
  country_fallback: bool = False,
134
+ default_no_values=None
134
135
  ) -> float:
135
136
  """
136
137
  Calculate the value of the impact based on lookup factors and `site.country.@id`.
@@ -166,13 +167,13 @@ def impact_country_value(
166
167
  debugValues(impact, model=model, term=term_id,
167
168
  values_used=log_as_table(values))
168
169
 
169
- all_with_factors = all([v.get('coefficient') is not None for v in values if v.get('value') is not None])
170
+ has_values = len(values) > 0
171
+ missing_values = set([v.get('id') for v in values if v.get('value') and v.get('coefficient') is None])
172
+ all_with_factors = not missing_values
170
173
  values = [float((v.get('value') or 0) * (v.get('coefficient') or 0)) for v in values]
171
174
 
172
175
  # fail if some factors are missing
173
- return None if not all_with_factors else (
174
- list_sum(values) if len(values) > 0 else None
175
- )
176
+ return None if not all_with_factors else (list_sum(values) if has_values else default_no_values)
176
177
 
177
178
 
178
179
  def impact_aware_value(model: str, term_id: str, impact: dict, lookup: str, group_key: str = None) -> float:
@@ -49,8 +49,9 @@ def all_factor_value(
49
49
  ):
50
50
  values = list(map(_factor_value(model, term_id, lookup_name, lookup_col, grouped_key), blank_nodes))
51
51
 
52
- missing_values = set([v.get('id') for v in values if v.get('value') is not None and v.get('coefficient') is None])
53
- all_with_factors = all([v.get('coefficient') is not None for v in values if v.get('value') is not None])
52
+ has_values = len(values) > 0
53
+ missing_values = set([v.get('id') for v in values if v.get('value') and v.get('coefficient') is None])
54
+ all_with_factors = not missing_values
54
55
 
55
56
  for missing_value in missing_values:
56
57
  debugMissingLookup(lookup_name, 'termid', missing_value, lookup_col, None, model=model, term=term_id)
@@ -58,14 +59,13 @@ def all_factor_value(
58
59
  debugValues(node, model=model, term=term_id,
59
60
  all_with_factors=all_with_factors,
60
61
  missing_lookup_factor=';'.join(missing_values),
62
+ has_values=has_values,
61
63
  values_used=log_as_table(values))
62
64
 
63
65
  values = [float((v.get('value') or 0) * (v.get('coefficient') or 0)) for v in values]
64
66
 
65
67
  # fail if some factors are missing
66
- return None if not all_with_factors else (
67
- list_sum(values) if len(values) > 0 else default_no_values
68
- )
68
+ return None if not all_with_factors else (list_sum(values) if has_values else default_no_values)
69
69
 
70
70
 
71
71
  def _term_factor_value(model: str, term_id: str, lookup_name: str, lookup_term_id: str, group_key: str = None):
@@ -57,7 +57,8 @@ def find_term_property(term, property: str, default=None, keep_in_memory=False)
57
57
  return find_term_match(props, property, default)
58
58
 
59
59
 
60
- def get_node_property(node: dict, property: str, find_default_property: bool = True):
60
+ def get_node_property(node: dict, property: str, find_default_property: bool = True,
61
+ keep_in_memory: bool = False) -> dict:
61
62
  """
62
63
  Get the property by `@id` linked to the Blank Node in the glossary.
63
64
 
@@ -73,6 +74,8 @@ def get_node_property(node: dict, property: str, find_default_property: bool = T
73
74
  The `term.@id` of the property. Example: `nitrogenContent`.
74
75
  find_default_property : bool
75
76
  Default to fetching the property from the `defaultProperties` of the `Term`.
77
+ keep_in_memory:
78
+ If True and find_default_property is True, will cache this term_id call to api
76
79
 
77
80
  Returns
78
81
  -------
@@ -80,7 +83,7 @@ def get_node_property(node: dict, property: str, find_default_property: bool = T
80
83
  The property if found, `None` otherwise.
81
84
  """
82
85
  prop = find_term_match(node.get('properties', []), property, None)
83
- return find_term_property(node.get('term', {}), property, {}) if all([
86
+ return find_term_property(node.get('term', {}), property, {}, keep_in_memory) if all([
84
87
  find_default_property,
85
88
  prop is None
86
89
  ]) else (prop or {})
@@ -1 +1 @@
1
- VERSION = '0.66.0'
1
+ VERSION = '0.67.1'
@@ -1,5 +1,7 @@
1
1
  import os
2
2
  import sys
3
+ import platform
4
+ import resource
3
5
  import logging
4
6
 
5
7
  LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
@@ -42,6 +44,15 @@ if LOG_FILENAME is not None:
42
44
  def _join_args(**kwargs): return ', '.join([f"{key}={value}" for key, value in kwargs.items()])
43
45
 
44
46
 
47
+ def log_memory_usage(**kwargs):
48
+ factor = 1024 * (
49
+ 1024 if platform.system() in ['Darwin', 'Windows'] else 1
50
+ )
51
+ value = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / factor
52
+ extra = (', ' + _join_args(**kwargs)) if len(kwargs.keys()) > 0 else ''
53
+ logger.info('memory used=%s, unit=MB' + extra, value)
54
+
55
+
45
56
  def _log_node_suffix(node: dict = {}):
46
57
  node_type = node.get('@type', node.get('type')) if node else None
47
58
  node_id = node.get('@id', node.get('id', node.get('term', {}).get('@id'))) if node else None
@@ -7,7 +7,7 @@ from copy import deepcopy
7
7
  from hestia_earth.utils.tools import non_empty_list
8
8
 
9
9
  from hestia_earth.models.version import VERSION
10
- from ..log import logger
10
+ from ..log import logger, log_memory_usage
11
11
  from ..utils import get_required_model_param, _snakecase
12
12
  from ..strategies.run import should_run
13
13
  from ..strategies.merge import merge
@@ -76,10 +76,15 @@ def _run_post_checks(data: dict):
76
76
 
77
77
 
78
78
  def _run_model(data: dict, model: dict, all_models: list):
79
- module = _import_model(get_required_model_param(model, 'model'))
80
- # if no value is provided, use all the models but this one
79
+ model_id = get_required_model_param(model, 'model')
81
80
  model_value = model.get('value') or _list_except_item(all_models, model)
81
+ log_memory_usage(model_model=model_id, model_value=model_value, step='before')
82
+
83
+ module = _import_model(model_id.replace('-', '_'))
84
+ # if no value is provided, use all the models but this one
82
85
  result = module.get('run')(model_value, data)
86
+
87
+ log_memory_usage(model_model=model_id, model_value=model_value, step='after')
83
88
  return {'data': data, 'model': model, 'version': module.get('version'), 'result': result}
84
89
 
85
90
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hestia-earth-models
3
- Version: 0.66.0
3
+ Version: 0.67.1
4
4
  Summary: HESTIA's set of modules for filling gaps in the activity data using external datasets (e.g. populating soil properties with a geospatial dataset using provided coordinates) and internal lookups (e.g. populating machinery use from fuel use). Includes rules for when gaps should be filled versus not (e.g. never gap fill yield, gap fill crop residue if yield provided etc.).
5
5
  Home-page: https://gitlab.com/hestia-earth/hestia-engine-models
6
6
  Author: HESTIA Team