hestia-earth-models 0.65.5__py3-none-any.whl → 0.65.7__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 (57) hide show
  1. hestia_earth/models/cml2001Baseline/abioticResourceDepletionFossilFuels.py +9 -5
  2. hestia_earth/models/config/Cycle.json +2193 -0
  3. hestia_earth/models/config/ImpactAssessment.json +2041 -0
  4. hestia_earth/models/config/Site.json +471 -0
  5. hestia_earth/models/config/__init__.py +71 -0
  6. hestia_earth/models/config/run-calculations.json +42 -0
  7. hestia_earth/models/config/trigger-calculations.json +43 -0
  8. hestia_earth/models/hestia/landCover.py +70 -22
  9. hestia_earth/models/ipcc2019/animal/hoursWorkedPerDay.py +38 -0
  10. hestia_earth/models/mocking/search-results.json +833 -829
  11. hestia_earth/models/site/management.py +82 -22
  12. hestia_earth/models/utils/crop.py +5 -1
  13. hestia_earth/models/version.py +1 -1
  14. hestia_earth/orchestrator/__init__.py +40 -0
  15. hestia_earth/orchestrator/log.py +62 -0
  16. hestia_earth/orchestrator/models/__init__.py +118 -0
  17. hestia_earth/orchestrator/models/emissions/__init__.py +0 -0
  18. hestia_earth/orchestrator/models/emissions/deleted.py +15 -0
  19. hestia_earth/orchestrator/models/transformations.py +103 -0
  20. hestia_earth/orchestrator/strategies/__init__.py +0 -0
  21. hestia_earth/orchestrator/strategies/merge/__init__.py +42 -0
  22. hestia_earth/orchestrator/strategies/merge/merge_append.py +29 -0
  23. hestia_earth/orchestrator/strategies/merge/merge_default.py +1 -0
  24. hestia_earth/orchestrator/strategies/merge/merge_list.py +103 -0
  25. hestia_earth/orchestrator/strategies/merge/merge_node.py +59 -0
  26. hestia_earth/orchestrator/strategies/run/__init__.py +8 -0
  27. hestia_earth/orchestrator/strategies/run/add_blank_node_if_missing.py +85 -0
  28. hestia_earth/orchestrator/strategies/run/add_key_if_missing.py +9 -0
  29. hestia_earth/orchestrator/strategies/run/always.py +6 -0
  30. hestia_earth/orchestrator/utils.py +116 -0
  31. {hestia_earth_models-0.65.5.dist-info → hestia_earth_models-0.65.7.dist-info}/METADATA +27 -5
  32. {hestia_earth_models-0.65.5.dist-info → hestia_earth_models-0.65.7.dist-info}/RECORD +57 -13
  33. tests/models/cml2001Baseline/test_abioticResourceDepletionFossilFuels.py +1 -1
  34. tests/models/hestia/test_landCover.py +2 -1
  35. tests/models/ipcc2019/animal/test_hoursWorkedPerDay.py +22 -0
  36. tests/models/test_config.py +115 -0
  37. tests/orchestrator/__init__.py +0 -0
  38. tests/orchestrator/models/__init__.py +0 -0
  39. tests/orchestrator/models/emissions/__init__.py +0 -0
  40. tests/orchestrator/models/emissions/test_deleted.py +21 -0
  41. tests/orchestrator/models/test_transformations.py +29 -0
  42. tests/orchestrator/strategies/__init__.py +0 -0
  43. tests/orchestrator/strategies/merge/__init__.py +0 -0
  44. tests/orchestrator/strategies/merge/test_merge_append.py +33 -0
  45. tests/orchestrator/strategies/merge/test_merge_default.py +7 -0
  46. tests/orchestrator/strategies/merge/test_merge_list.py +327 -0
  47. tests/orchestrator/strategies/merge/test_merge_node.py +95 -0
  48. tests/orchestrator/strategies/run/__init__.py +0 -0
  49. tests/orchestrator/strategies/run/test_add_blank_node_if_missing.py +114 -0
  50. tests/orchestrator/strategies/run/test_add_key_if_missing.py +14 -0
  51. tests/orchestrator/strategies/run/test_always.py +5 -0
  52. tests/orchestrator/test_models.py +69 -0
  53. tests/orchestrator/test_orchestrator.py +27 -0
  54. tests/orchestrator/test_utils.py +109 -0
  55. {hestia_earth_models-0.65.5.dist-info → hestia_earth_models-0.65.7.dist-info}/LICENSE +0 -0
  56. {hestia_earth_models-0.65.5.dist-info → hestia_earth_models-0.65.7.dist-info}/WHEEL +0 -0
  57. {hestia_earth_models-0.65.5.dist-info → hestia_earth_models-0.65.7.dist-info}/top_level.txt +0 -0
@@ -6,6 +6,8 @@ functionality of the Blonk model.
6
6
  """
7
7
  import math
8
8
  from collections import defaultdict
9
+ from datetime import datetime, timedelta
10
+
9
11
  from hestia_earth.schema import SiteSiteType, TermTermType
10
12
  from hestia_earth.utils.lookup import (
11
13
  download_lookup, get_table_value, column_name,
@@ -32,6 +34,8 @@ from .utils import (
32
34
  crop_ipcc_land_use_category,
33
35
  )
34
36
  from . import MODEL
37
+ from ..utils.blank_node import _node_date, DatestrFormat
38
+ from ..utils.constant import DAYS_IN_YEAR
35
39
 
36
40
  REQUIREMENTS = {
37
41
  "Site": {
@@ -90,6 +94,7 @@ SITE_TYPES = {
90
94
  SiteSiteType.PERMANENT_PASTURE.value
91
95
  }
92
96
  DEFAULT_WINDOW_IN_YEARS = 20
97
+ DATE_TOLERANCE_IN_YEARS = 2
93
98
  OUTPUT_SIGNIFICANT_DIGITS = 3
94
99
 
95
100
 
@@ -115,16 +120,6 @@ def site_area_sum_to_100(dict_of_percentages: dict):
115
120
  math.isclose(sum(dict_of_percentages.values()), 0.0, rel_tol=0.01))
116
121
 
117
122
 
118
- def _lookup_land_use_type(nodes: list) -> str:
119
- """Look up the land use type from a management node."""
120
- return "" if nodes == [] else get_lookup_value(
121
- lookup_term=nodes[0].get("term", {}),
122
- column=LOOKUPS.get("landCover")[1],
123
- model=MODEL,
124
- term=nodes[0].get("term", {})
125
- )
126
-
127
-
128
123
  def get_changes(country_id: str, end_year: int) -> dict:
129
124
  """
130
125
  For each entry in ALL_LAND_USE_TERMS, creates a key: value in output dictionary, also TOTAL
@@ -512,14 +507,13 @@ def _get_net_expansion_cultivated_vs_harvested(annual_crops_net_expansion, chang
512
507
  return net_expansion_cultivated_vs_harvested
513
508
 
514
509
 
515
- def _should_run_historical_land_use_change(site: dict, land_use_type: str) -> tuple[bool, dict]:
516
- management_nodes = filter_list_term_type(site.get("management", []), TermTermType.LANDCOVER)
510
+ def _should_run_historical_land_use_change(site: dict, nodes: list, land_use_type: str) -> tuple[bool, dict]:
517
511
  # Assume a single management node for single-cropping.
518
512
  return _should_run_historical_land_use_change_single_crop(
519
513
  site=site,
520
- term=management_nodes[0].get("term", {}),
514
+ term=nodes[0].get("term", {}),
521
515
  country_id=site.get("country", {}).get("@id"),
522
- end_year=int(management_nodes[0].get("endDate")[:4]),
516
+ end_year=int(nodes[0].get("endDate")[:4]),
523
517
  land_use_type=land_use_type
524
518
  )
525
519
 
@@ -688,18 +682,72 @@ def _should_run_historical_land_use_change_single_crop(
688
682
  return should_run, site_area
689
683
 
690
684
 
685
+ def _get_land_use_term_from_node(node: dict) -> str:
686
+ return get_lookup_value(
687
+ lookup_term=node.get("term", {}),
688
+ column=LOOKUPS.get("landCover")[1],
689
+ model=MODEL,
690
+ term=node.get("term", {})
691
+ )
692
+
693
+
694
+ def _collect_land_use_types(nodes: list) -> list:
695
+ """Look up the land use type from management nodes."""
696
+ return [
697
+ {
698
+ "id": node.get("term", {}).get("@id"),
699
+ "land-use-type": _get_land_use_term_from_node(node),
700
+ "endDate": node.get("endDate")
701
+ } for node in nodes
702
+ ]
703
+
704
+
705
+ def _no_prior_land_cover_data(nodes: list, end_date: str) -> bool:
706
+ target_date = (
707
+ datetime.strptime(end_date, DatestrFormat.YEAR_MONTH_DAY.value)
708
+ - timedelta(days=DEFAULT_WINDOW_IN_YEARS * DAYS_IN_YEAR)
709
+ )
710
+ previous_nodes = [
711
+ node for node in nodes
712
+ if abs(_node_date(node) - target_date) < timedelta(days=DATE_TOLERANCE_IN_YEARS * DAYS_IN_YEAR)
713
+ ]
714
+ return len(previous_nodes) == 0
715
+
716
+
691
717
  def _should_run(site: dict) -> tuple[bool, dict]:
692
718
  management_nodes = filter_list_term_type(site.get("management", []), TermTermType.LANDCOVER)
693
- land_use_type = _lookup_land_use_type(nodes=management_nodes)
694
- should_run_result, site_area = (
695
- (False, {}) if land_use_type not in {ANNUAL_CROPLAND, PERMANENT_CROPLAND, PERMANENT_PASTURE}
696
- else _should_run_historical_land_use_change(
697
- site=site,
698
- land_use_type=land_use_type
699
- )
719
+ summarised_nodes = _collect_land_use_types(management_nodes)
720
+ allowed_land_use_types = [ANNUAL_CROPLAND, PERMANENT_CROPLAND, PERMANENT_PASTURE]
721
+ relevant_nodes = sorted(
722
+ [
723
+ node for node in summarised_nodes
724
+ if node["land-use-type"] in allowed_land_use_types
725
+ ],
726
+ key=lambda n: n.get("endDate")
700
727
  )
728
+ land_use_type = relevant_nodes[0].get("land-use-type") if relevant_nodes else None
701
729
 
702
- return should_run_result, site_area
730
+ has_no_prior_land_cover_data = _no_prior_land_cover_data(
731
+ nodes=management_nodes,
732
+ end_date=relevant_nodes[-1:][0].get("endDate")
733
+ ) if relevant_nodes else None
734
+
735
+ should_run_nodes, site_area = _should_run_historical_land_use_change(
736
+ site=site,
737
+ nodes=management_nodes,
738
+ land_use_type=land_use_type
739
+ ) if all([land_use_type, has_no_prior_land_cover_data]) else (False, {})
740
+
741
+ logRequirements(site, model=MODEL, model_key=MODEL_KEY,
742
+ has_management_nodes=bool(management_nodes),
743
+ land_use_type=land_use_type,
744
+ allowed_land_use_types=';'.join(allowed_land_use_types),
745
+ has_no_prior_land_cover_data=has_no_prior_land_cover_data,
746
+ summarised_nodes=log_as_table(summarised_nodes))
747
+
748
+ should_run = all([land_use_type, has_no_prior_land_cover_data, should_run_nodes])
749
+ logShouldRun(site, MODEL, None, should_run, model_key=MODEL_KEY)
750
+ return should_run_nodes, site_area
703
751
 
704
752
 
705
753
  def run(site: dict) -> list:
@@ -0,0 +1,38 @@
1
+ from .utils import should_run_by_productivity_lookup, run_animal_by_productivity
2
+
3
+ REQUIREMENTS = {
4
+ "Cycle": {
5
+ "site": {
6
+ "@type": "Site",
7
+ "country": {"@type": "Term", "termType": "region"}
8
+ },
9
+ "animals": [{
10
+ "@type": "Animal",
11
+ "term.termType": "liveAnimal",
12
+ "none": {
13
+ "properties": [{
14
+ "@type": "Property",
15
+ "value": "",
16
+ "term.@id": "hoursWorkedPerDay"
17
+ }]
18
+ }
19
+ }]
20
+ }
21
+ }
22
+ LOOKUPS = {
23
+ "region-liveAnimal-hoursWorkedPerDay": "hours worked per day"
24
+ }
25
+ RETURNS = {
26
+ "Animal": [{
27
+ "properties": [{
28
+ "@type": "Property",
29
+ "value": ""
30
+ }]
31
+ }]
32
+ }
33
+ TERM_ID = 'hoursWorkedPerDay'
34
+
35
+
36
+ def run(cycle: dict):
37
+ animals = should_run_by_productivity_lookup(TERM_ID, cycle, list(LOOKUPS.keys())[0])
38
+ return list(map(run_animal_by_productivity(TERM_ID), animals))