hestia-earth-models 0.64.13__py3-none-any.whl → 0.64.14__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.

@@ -0,0 +1,158 @@
1
+ """
2
+ Material and Substrate
3
+
4
+ This model gap-fills depreciated amount per Cycle from Site Infrastructure node.
5
+ """
6
+ from typing import Union
7
+ from hestia_earth.schema import TermTermType
8
+ from hestia_earth.utils.lookup import download_lookup
9
+ from hestia_earth.utils.tools import to_precision, flatten, list_sum
10
+ from hestia_earth.utils.model import filter_list_term_type
11
+
12
+ from hestia_earth.models.log import logShouldRun, logRequirements
13
+ from hestia_earth.models.utils.constant import DAYS_IN_YEAR
14
+ from hestia_earth.models.utils.input import _new_input
15
+ from hestia_earth.models.utils.completeness import _is_term_type_incomplete
16
+ from .import MODEL
17
+
18
+ REQUIREMENTS = {
19
+ "Cycle": {
20
+ "completeness.material": "False",
21
+ "cycleDuration": "",
22
+ "site": {
23
+ "@type": "Site",
24
+ "infrastructure": [
25
+ {
26
+ "@type": "Infrastructure",
27
+ "defaultLifespan": "",
28
+ "inputs": [
29
+ {
30
+ "@type": "Input",
31
+ "term.termType": ["material", "substrate"],
32
+ "value": "",
33
+ "lifespan": ""
34
+ }
35
+ ]
36
+ }
37
+ ]
38
+ }
39
+ }
40
+ }
41
+ RETURNS = {
42
+ "Input": [{
43
+ "value": "",
44
+ "min": "",
45
+ "max": "",
46
+ "sd": "",
47
+ "statsDefinition": ""
48
+ }]
49
+ }
50
+ LOOKUPS = {
51
+ "material": ""
52
+ }
53
+ MODEL_KEY = 'materialAndSubstrate'
54
+
55
+ _ID_SUFFIX = "DepreciatedAmountPerCycle"
56
+ _OPTIONAL_VALUES = ["min", "max", "sd"]
57
+ _SIGNIFICANT_DIGITS = 5
58
+
59
+
60
+ def _input(term_id: str, value: float, stats: dict) -> dict:
61
+ node = _new_input(term_id + _ID_SUFFIX)
62
+ node['value'] = [value]
63
+ return node | stats
64
+
65
+
66
+ def _get_value(node: dict, field_name: str) -> float:
67
+ value = node.get(field_name)
68
+ return list_sum(value) if isinstance(value, list) else value
69
+
70
+
71
+ def calculate_value(input_node: dict, field_name: str, cycle_duration: float) -> Union[float, None]:
72
+ lifespan = input_node.get("lifespan")
73
+ value = _get_value(node=input_node, field_name=field_name)
74
+ return (
75
+ to_precision(number=(value / (lifespan * DAYS_IN_YEAR)) * cycle_duration, digits=_SIGNIFICANT_DIGITS)
76
+ if value else None
77
+ )
78
+
79
+
80
+ def _run_input(cycle: dict, input_node: dict) -> dict:
81
+ cycle_duration = cycle.get("cycleDuration")
82
+
83
+ value = calculate_value(
84
+ input_node=input_node,
85
+ field_name="value",
86
+ cycle_duration=cycle_duration
87
+ )
88
+
89
+ optional_gap_filled_values = {
90
+ field_name: [
91
+ calculate_value(input_node=input_node, field_name=field_name, cycle_duration=cycle_duration)
92
+ ]
93
+ for field_name in _OPTIONAL_VALUES if field_name in input_node
94
+ }
95
+ if "statsDefinition" in input_node:
96
+ optional_gap_filled_values["statsDefinition"] = input_node["statsDefinition"]
97
+
98
+ return _input(input_node.get('term', {}).get('@id'), value, optional_gap_filled_values)
99
+
100
+
101
+ def _has_depreciated_term(term: dict):
102
+ lookup = download_lookup(f"{term.get('termType')}.csv")
103
+ return term.get('@id') + _ID_SUFFIX in list(lookup.termid)
104
+
105
+
106
+ def _should_run_input(cycle: dict, input_node: dict) -> bool:
107
+ term = input_node.get('term', {})
108
+ term_id = term.get('@id')
109
+ has_lifespan = input_node.get('lifespan', 0) > 0
110
+ has_valid_value = _get_value(input_node, 'value') > 0
111
+ has_depreciated_term = _has_depreciated_term(term)
112
+
113
+ should_run = all([
114
+ has_depreciated_term,
115
+ has_valid_value,
116
+ has_lifespan
117
+ ])
118
+
119
+ logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
120
+ has_valid_value=has_valid_value,
121
+ has_lifespan=has_lifespan,
122
+ has_depreciated_term=has_depreciated_term)
123
+ logShouldRun(input_node, MODEL, term_id, should_run, model_key=MODEL_KEY)
124
+ return should_run
125
+
126
+
127
+ def _should_run_infrastructure(cycle: dict, infra_node: dict) -> tuple[bool, list]:
128
+ inputs = filter_list_term_type(infra_node.get('inputs', []), [TermTermType.MATERIAL, TermTermType.SUBSTRATE])
129
+ inputs = [
130
+ i | {
131
+ 'lifespan': i.get('lifespan') or infra_node.get('defaultLifespan')
132
+ }
133
+ for i in inputs
134
+ ]
135
+ return [i for i in inputs if _should_run_input(cycle, i)]
136
+
137
+
138
+ def _should_run(cycle: dict) -> tuple[bool, list]:
139
+ inputs = flatten([
140
+ _should_run_infrastructure(cycle, i)
141
+ for i in cycle.get('site', {}).get('infrastructure', [])
142
+ ])
143
+ has_material_inputs = len(inputs) > 0
144
+ cycle_duration = cycle.get('cycleDuration')
145
+ is_incomplete = _is_term_type_incomplete(cycle, TermTermType.MATERIAL)
146
+
147
+ logRequirements(cycle, model=MODEL, term=None, model_key=MODEL_KEY,
148
+ term_type_material_incomplete=is_incomplete,
149
+ has_material_inputs=has_material_inputs)
150
+
151
+ should_run = all([is_incomplete, has_material_inputs, cycle_duration])
152
+ logShouldRun(cycle, MODEL, None, should_run, model_key=MODEL_KEY)
153
+ return should_run, inputs
154
+
155
+
156
+ def run(cycle: dict):
157
+ should_run, inputs = _should_run(cycle)
158
+ return [_run_input(cycle, i) for i in inputs] if should_run else []
@@ -141,10 +141,10 @@ DE_MAPPING = {
141
141
  def _get_grouped_data_key(keys: list, DE: float, NDF: float, ionophore: bool, milk_yield: float):
142
142
  # test conditions one by one and return the key associated for the first one that passes
143
143
  return (
144
- next(
144
+ (next(
145
145
  (key for key in keys if key in DE_NDF_MAPPING and DE_NDF_MAPPING[key](DE, NDF)),
146
146
  None
147
- ) or next(
147
+ ) if all([DE is not None, NDF is not None]) else None) or next(
148
148
  (key for key in keys if key in DE_MAPPING and DE_MAPPING[key](DE, ionophore)),
149
149
  None
150
150
  ) if DE else None
@@ -6,7 +6,7 @@ from hestia_earth.utils.tools import safe_parse_float, list_sum
6
6
 
7
7
  from hestia_earth.models.log import debugValues, logRequirements, debugMissingLookup, logShouldRun, log_as_table
8
8
  from hestia_earth.models.utils import _filter_list_term_unit
9
- from hestia_earth.models.utils.constant import Units
9
+ from hestia_earth.models.utils.constant import Units, DAYS_PER_MONTH
10
10
  from hestia_earth.models.utils.completeness import _is_term_type_complete
11
11
  from hestia_earth.models.utils.productivity import PRODUCTIVITY, get_productivity
12
12
  from hestia_earth.models.utils.emission import _new_emission
@@ -40,7 +40,6 @@ RETURNS = {
40
40
  }
41
41
  TERM_ID = 'ch4ToAirExcreta'
42
42
  TIER = EmissionMethodTier.TIER_2.value
43
- DAYS_PER_MONTH = 365.25/12
44
43
 
45
44
 
46
45
  class DURATION(Enum):
@@ -1,6 +1,6 @@
1
1
  import os
2
- from hestia_earth.models.utils import measurement, site
3
2
 
3
+ from hestia_earth.models.utils import measurement, site
4
4
  from .mock_search import mock as mock_search
5
5
 
6
6
  CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))