hestia-earth-models 0.67.0__py3-none-any.whl → 0.68.0__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.
- hestia_earth/models/aware/scarcityWeightedWaterUse.py +5 -6
- hestia_earth/models/blonkConsultants2016/ch4ToAirNaturalVegetationBurning.py +1 -1
- hestia_earth/models/blonkConsultants2016/co2ToAirAboveGroundBiomassStockChangeLandUseChange.py +1 -1
- hestia_earth/models/blonkConsultants2016/n2OToAirNaturalVegetationBurningDirect.py +1 -1
- hestia_earth/models/blonkConsultants2016/utils.py +9 -9
- hestia_earth/models/cache_sites.py +26 -14
- hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandOccupation.py +2 -2
- hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandTransformation.py +2 -2
- hestia_earth/models/chaudharyBrooks2018/utils.py +13 -8
- hestia_earth/models/cml2001Baseline/abioticResourceDepletionFossilFuels.py +2 -3
- hestia_earth/models/cml2001Baseline/abioticResourceDepletionMineralsAndMetals.py +1 -1
- hestia_earth/models/cml2001Baseline/resourceUseEnergyDepletionDuringCycle.py +5 -10
- hestia_earth/models/config/Cycle.json +15 -0
- hestia_earth/models/config/ImpactAssessment.json +14 -1
- hestia_earth/models/config/Site.json +8 -0
- hestia_earth/models/cycle/completeness/freshForage.py +7 -3
- hestia_earth/models/cycle/excretaKgMass.py +2 -2
- hestia_earth/models/cycle/inorganicFertiliser.py +67 -17
- hestia_earth/models/cycle/materialAndSubstrate.py +3 -2
- hestia_earth/models/cycle/pastureGrass.py +3 -3
- hestia_earth/models/dammgen2009/noxToAirExcreta.py +1 -1
- hestia_earth/models/ecoinventV3AndEmberClimate/__init__.py +1 -1
- hestia_earth/models/ecoinventV3AndEmberClimate/utils.py +2 -6
- hestia_earth/models/emissionNotRelevant/__init__.py +4 -4
- hestia_earth/models/environmentalFootprintV3_1/environmentalFootprintSingleOverallScore.py +60 -46
- hestia_earth/models/environmentalFootprintV3_1/photochemicalOzoneCreationPotentialHumanHealthNmvocEq.py +36 -0
- hestia_earth/models/environmentalFootprintV3_1/scarcityWeightedWaterUse.py +2 -2
- hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexLandOccupation.py +9 -8
- hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexLandTransformation.py +45 -34
- hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexTotalLandUseEffects.py +24 -21
- hestia_earth/models/faostat2018/coldCarcassWeightPerHead.py +2 -2
- hestia_earth/models/faostat2018/coldDressedCarcassWeightPerHead.py +2 -2
- hestia_earth/models/faostat2018/liveweightPerHead.py +7 -8
- hestia_earth/models/faostat2018/product/price.py +34 -28
- hestia_earth/models/faostat2018/readyToCookWeightPerHead.py +2 -2
- hestia_earth/models/faostat2018/utils.py +15 -27
- hestia_earth/models/frischknechtEtAl2000/ionisingRadiationKbqU235Eq.py +16 -9
- hestia_earth/models/geospatialDatabase/altitude.py +60 -0
- hestia_earth/models/geospatialDatabase/croppingIntensity.py +1 -1
- hestia_earth/models/geospatialDatabase/ecoClimateZone.py +2 -2
- hestia_earth/models/geospatialDatabase/longFallowRatio.py +1 -1
- hestia_earth/models/geospatialDatabase/utils.py +4 -1
- hestia_earth/models/globalCropWaterModel2008/rootingDepth.py +2 -3
- hestia_earth/models/haversineFormula/transport/distance.py +3 -3
- hestia_earth/models/hestia/landCover.py +72 -45
- hestia_earth/models/hestia/landTransformation100YearAverageDuringCycle.py +1 -1
- hestia_earth/models/hestia/landTransformation20YearAverageDuringCycle.py +1 -1
- hestia_earth/models/hestia/seed_emissions.py +11 -7
- hestia_earth/models/impact_assessment/__init__.py +3 -3
- hestia_earth/models/ipcc2019/aboveGroundBiomass.py +1 -1
- hestia_earth/models/ipcc2019/animal/fatContent.py +1 -1
- hestia_earth/models/ipcc2019/animal/hoursWorkedPerDay.py +1 -1
- hestia_earth/models/ipcc2019/animal/liveweightGain.py +1 -1
- hestia_earth/models/ipcc2019/animal/liveweightPerHead.py +1 -1
- hestia_earth/models/ipcc2019/animal/milkYieldPerAnimal.py +1 -1
- hestia_earth/models/ipcc2019/animal/pastureGrass.py +1 -1
- hestia_earth/models/ipcc2019/animal/pregnancyRateTotal.py +1 -1
- hestia_earth/models/ipcc2019/animal/trueProteinContent.py +1 -1
- hestia_earth/models/ipcc2019/animal/utils.py +5 -7
- hestia_earth/models/ipcc2019/animal/weightAtMaturity.py +1 -1
- hestia_earth/models/ipcc2019/belowGroundBiomass.py +1 -1
- hestia_earth/models/ipcc2019/ch4ToAirEntericFermentation.py +2 -2
- hestia_earth/models/ipcc2019/ch4ToAirExcreta.py +6 -7
- hestia_earth/models/ipcc2019/ch4ToAirFloodedRice.py +5 -3
- hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +1 -1
- hestia_earth/models/ipcc2019/croppingDuration.py +3 -6
- hestia_earth/models/ipcc2019/nonCo2EmissionsToAirNaturalVegetationBurning.py +947 -0
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +4 -4
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +1 -1
- hestia_earth/models/ipcc2019/pastureGrass.py +1 -1
- hestia_earth/models/koble2014/residueBurnt.py +5 -7
- hestia_earth/models/koble2014/residueRemoved.py +5 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthWaterStress.py +2 -2
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthWaterStress.py +2 -2
- hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthWaterStress.py +2 -2
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthWaterStress.py +2 -2
- hestia_earth/models/log.py +1 -1
- hestia_earth/models/mocking/search-results.json +3477 -1045
- hestia_earth/models/site/management.py +1 -1
- hestia_earth/models/site/post_checks/__init__.py +3 -2
- hestia_earth/models/site/post_checks/country.py +9 -0
- hestia_earth/models/site/pre_checks/__init__.py +3 -2
- hestia_earth/models/site/pre_checks/country.py +9 -0
- hestia_earth/models/utils/__init__.py +1 -16
- hestia_earth/models/utils/blank_node.py +89 -36
- hestia_earth/models/utils/completeness.py +3 -2
- hestia_earth/models/utils/cycle.py +5 -4
- hestia_earth/models/utils/ecoClimateZone.py +2 -2
- hestia_earth/models/utils/emission.py +5 -5
- hestia_earth/models/utils/feedipedia.py +6 -6
- hestia_earth/models/utils/impact_assessment.py +6 -6
- hestia_earth/models/utils/indicator.py +9 -7
- hestia_earth/models/utils/inorganicFertiliser.py +4 -6
- hestia_earth/models/utils/input.py +6 -5
- hestia_earth/models/utils/lookup.py +35 -105
- hestia_earth/models/utils/management.py +4 -4
- hestia_earth/models/utils/measurement.py +6 -7
- hestia_earth/models/utils/method.py +20 -0
- hestia_earth/models/utils/practice.py +4 -5
- hestia_earth/models/utils/product.py +4 -5
- hestia_earth/models/utils/property.py +12 -22
- hestia_earth/models/utils/site.py +14 -8
- hestia_earth/models/utils/term.py +27 -1
- hestia_earth/models/version.py +1 -1
- hestia_earth/orchestrator/log.py +0 -11
- hestia_earth/orchestrator/models/__init__.py +17 -4
- hestia_earth/orchestrator/strategies/run/add_blank_node_if_missing.py +2 -20
- {hestia_earth_models-0.67.0.dist-info → hestia_earth_models-0.68.0.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.67.0.dist-info → hestia_earth_models-0.68.0.dist-info}/RECORD +159 -151
- tests/models/cml2001Baseline/test_abioticResourceDepletionFossilFuels.py +3 -3
- tests/models/cml2001Baseline/test_resourceUseEnergyDepletionDuringCycle.py +68 -35
- tests/models/cycle/test_coldCarcassWeightPerHead.py +1 -1
- tests/models/cycle/test_coldDressedCarcassWeightPerHead.py +1 -1
- tests/models/cycle/test_concentrateFeed.py +1 -1
- tests/models/cycle/test_energyContentLowerHeatingValue.py +1 -1
- tests/models/cycle/test_excretaKgMass.py +1 -1
- tests/models/cycle/test_feedConversionRatio.py +3 -3
- tests/models/cycle/test_pastureGrass.py +1 -1
- tests/models/cycle/test_readyToCookWeightPerHead.py +1 -1
- tests/models/environmentalFootprintV3_1/test_environmentalFootprintSingleOverallScore.py +38 -8
- tests/models/environmentalFootprintV3_1/test_photochemicalOzoneCreationPotentialHumanHealthNmvocEq.py +30 -0
- tests/models/environmentalFootprintV3_1/test_soilQualityIndexLandTransformation.py +65 -36
- tests/models/environmentalFootprintV3_1/test_soilQualityIndexTotalLandUseEffects.py +30 -7
- tests/models/faostat2018/product/test_price.py +27 -14
- tests/models/faostat2018/test_faostat_utils.py +4 -24
- tests/models/faostat2018/test_liveweightPerHead.py +9 -9
- tests/models/globalCropWaterModel2008/test_rootingDepth.py +7 -3
- tests/models/haversineFormula/transport/test_distance.py +1 -1
- tests/models/hestia/test_landCover.py +53 -5
- tests/models/ipcc2019/animal/test_pastureGrass.py +5 -3
- tests/models/ipcc2019/test_aboveGroundCropResidueTotal.py +4 -4
- tests/models/ipcc2019/test_belowGroundCropResidue.py +4 -4
- tests/models/ipcc2019/test_ch4ToAirEntericFermentation.py +10 -10
- tests/models/ipcc2019/test_croppingDuration.py +1 -1
- tests/models/ipcc2019/test_nonCo2EmissionsToAirNaturalVegetationBurning.py +83 -0
- tests/models/ipcc2019/test_organicCarbonPerHa.py +12 -12
- tests/models/ipcc2019/test_pastureGrass.py +5 -3
- tests/models/pooreNemecek2018/test_excretaKgN.py +5 -5
- tests/models/pooreNemecek2018/test_excretaKgVs.py +2 -2
- tests/models/site/post_checks/test_country.py +6 -0
- tests/models/site/pre_checks/test_cache_geospatialDatabase.py +1 -1
- tests/models/site/pre_checks/test_country.py +12 -0
- tests/models/site/test_management.py +1 -4
- tests/models/test_ecoinventV3.py +7 -3
- tests/models/utils/test_blank_node.py +17 -177
- tests/models/utils/test_dataCompleteness.py +5 -5
- tests/models/utils/test_emission.py +2 -2
- tests/models/utils/test_indicator.py +2 -2
- tests/models/utils/test_input.py +2 -2
- tests/models/utils/test_measurement.py +2 -4
- tests/models/utils/test_practice.py +4 -2
- tests/models/utils/test_product.py +2 -2
- tests/models/utils/test_property.py +4 -2
- tests/models/utils/test_site.py +7 -0
- tests/orchestrator/models/test_transformations.py +4 -1
- tests/orchestrator/strategies/run/test_add_blank_node_if_missing.py +4 -9
- hestia_earth/models/environmentalFootprintV3_1/utils.py +0 -17
- tests/models/utils/test_lookup.py +0 -10
- {hestia_earth_models-0.67.0.dist-info → hestia_earth_models-0.68.0.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.67.0.dist-info → hestia_earth_models-0.68.0.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.67.0.dist-info → hestia_earth_models-0.68.0.dist-info}/top_level.txt +0 -0
@@ -1,10 +1,13 @@
|
|
1
1
|
from typing import Optional, List
|
2
|
-
from numpy import recarray
|
3
|
-
from hestia_earth.schema import SchemaType
|
4
2
|
from hestia_earth.utils.lookup import (
|
5
|
-
download_lookup,
|
3
|
+
download_lookup,
|
4
|
+
get_table_value,
|
5
|
+
column_name,
|
6
|
+
extract_grouped_data,
|
7
|
+
_get_single_table_value,
|
8
|
+
lookup_term_ids
|
6
9
|
)
|
7
|
-
from hestia_earth.utils.tools import list_sum, safe_parse_float
|
10
|
+
from hestia_earth.utils.tools import list_sum, safe_parse_float
|
8
11
|
|
9
12
|
from ..log import debugValues, log_as_table, debugMissingLookup
|
10
13
|
|
@@ -15,13 +18,11 @@ def _node_value(node):
|
|
15
18
|
|
16
19
|
|
17
20
|
def _factor_value(model: str, term_id: str, lookup_name: str, lookup_col: str, grouped_key: Optional[str] = None):
|
18
|
-
lookup = download_lookup(lookup_name)
|
19
|
-
|
20
21
|
def get_value(data: dict):
|
21
22
|
node_term_id = data.get('term', {}).get('@id')
|
22
23
|
grouped_data_key = grouped_key or data.get('methodModel', {}).get('@id')
|
23
24
|
value = _node_value(data)
|
24
|
-
coefficient =
|
25
|
+
coefficient = get_region_lookup_value(lookup_name, node_term_id, lookup_col, model=model, term=term_id)
|
25
26
|
# value is either a number or matching between a model and a value (restrict value to specific model only)
|
26
27
|
coefficient = safe_parse_float(
|
27
28
|
extract_grouped_data(coefficient, grouped_data_key), None
|
@@ -50,8 +51,8 @@ def all_factor_value(
|
|
50
51
|
values = list(map(_factor_value(model, term_id, lookup_name, lookup_col, grouped_key), blank_nodes))
|
51
52
|
|
52
53
|
has_values = len(values) > 0
|
53
|
-
missing_values = set([v.get('id') for v in values if v.get('value')
|
54
|
-
all_with_factors =
|
54
|
+
missing_values = set([v.get('id') for v in values if v.get('value') and v.get('coefficient') is None])
|
55
|
+
all_with_factors = not missing_values
|
55
56
|
|
56
57
|
for missing_value in missing_values:
|
57
58
|
debugMissingLookup(lookup_name, 'termid', missing_value, lookup_col, None, model=model, term=term_id)
|
@@ -65,18 +66,14 @@ def all_factor_value(
|
|
65
66
|
values = [float((v.get('value') or 0) * (v.get('coefficient') or 0)) for v in values]
|
66
67
|
|
67
68
|
# fail if some factors are missing
|
68
|
-
return None if not all_with_factors else (
|
69
|
-
list_sum(values) if has_values else default_no_values
|
70
|
-
)
|
69
|
+
return None if not all_with_factors else (list_sum(values) if has_values else default_no_values)
|
71
70
|
|
72
71
|
|
73
72
|
def _term_factor_value(model: str, term_id: str, lookup_name: str, lookup_term_id: str, group_key: str = None):
|
74
|
-
lookup = download_lookup(lookup_name, False) # avoid saving in memory as there could be many different files used
|
75
|
-
|
76
73
|
def get_value(data: dict):
|
77
74
|
node_term_id = data.get('term', {}).get('@id')
|
78
75
|
value = _node_value(data)
|
79
|
-
coefficient =
|
76
|
+
coefficient = get_region_lookup_value(lookup_name, lookup_term_id, node_term_id, model=model, term=term_id)
|
80
77
|
coefficient = safe_parse_float(extract_grouped_data(coefficient, group_key) if group_key else coefficient)
|
81
78
|
if value is not None and coefficient is not None:
|
82
79
|
debugValues(data, model=model, term=term_id,
|
@@ -109,98 +106,31 @@ def _aware_factor_value(model: str, term_id: str, lookup_name: str, aware_id: st
|
|
109
106
|
return get_value
|
110
107
|
|
111
108
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
def _get_sites(node: dict):
|
120
|
-
site = node.get('site', node.get('cycle', {}).get('site'))
|
121
|
-
other_sites = node.get('otherSites', node.get('cycle', {}).get('otherSites', []))
|
122
|
-
return non_empty_list([site] + other_sites)
|
123
|
-
|
124
|
-
|
125
|
-
def _get_site_types(node: dict):
|
126
|
-
sites = [node] if _is_site(node) else _get_sites(node)
|
127
|
-
return non_empty_list([site.get('siteType') for site in sites])
|
128
|
-
|
129
|
-
|
130
|
-
def _model_lookup_values(model: str, term: dict, restriction: str):
|
131
|
-
lookup = download_lookup(f"{term.get('termType')}-model-{restriction}.csv")
|
132
|
-
values = get_table_value(lookup, 'termid', term.get('@id'), column_name(model))
|
133
|
-
return (values or _ALLOW_ALL).split(';') if isinstance(values, str) else _ALLOW_ALL
|
134
|
-
|
135
|
-
|
136
|
-
def is_model_siteType_allowed(model: str, term: dict, data: dict):
|
137
|
-
site_types = _get_site_types(data)
|
138
|
-
allowed_values = _model_lookup_values(model, term, 'siteTypesAllowed')
|
139
|
-
return True if _ALLOW_ALL in allowed_values or not site_types else any([
|
140
|
-
(site_type in allowed_values) for site_type in site_types
|
141
|
-
])
|
142
|
-
|
143
|
-
|
144
|
-
def _lookup_values(term: dict, column: str):
|
145
|
-
lookup = download_lookup(f"{term.get('termType')}.csv")
|
146
|
-
values = get_table_value(lookup, 'termid', term.get('@id'), column_name(column))
|
147
|
-
return (values or _ALLOW_ALL).split(';') if isinstance(values, str) else _ALLOW_ALL
|
148
|
-
|
149
|
-
|
150
|
-
def is_siteType_allowed(data: dict, term: dict):
|
151
|
-
site_types = _get_site_types(data)
|
152
|
-
allowed_values = _lookup_values(term, 'siteTypesAllowed')
|
153
|
-
return True if _ALLOW_ALL in allowed_values or not site_types else any([
|
154
|
-
(site_type in allowed_values) for site_type in site_types
|
155
|
-
])
|
156
|
-
|
157
|
-
|
158
|
-
def is_product_termType_allowed(data: dict, term: dict):
|
159
|
-
products = data.get('products', [])
|
160
|
-
values = non_empty_list([p.get('term', {}).get('termType') for p in products])
|
161
|
-
allowed_values = _lookup_values(term, 'productTermTypesAllowed')
|
162
|
-
return True if any([
|
163
|
-
_ALLOW_ALL in allowed_values,
|
164
|
-
len(values) == 0
|
165
|
-
]) else any([value in allowed_values for value in values])
|
166
|
-
|
167
|
-
|
168
|
-
def is_product_id_allowed(data: dict, term: dict):
|
169
|
-
products = data.get('products', [])
|
170
|
-
values = non_empty_list([p.get('term', {}).get('@id') for p in products])
|
171
|
-
allowed_values = _lookup_values(term, 'productTermIdsAllowed')
|
172
|
-
return True if any([
|
173
|
-
_ALLOW_ALL in allowed_values,
|
174
|
-
len(values) == 0
|
175
|
-
]) else any([value in allowed_values for value in values])
|
176
|
-
|
177
|
-
|
178
|
-
def is_input_termType_allowed(data: dict, term: dict):
|
179
|
-
inputs = data.get('inputs', [])
|
180
|
-
values = non_empty_list([p.get('term', {}).get('termType') for p in inputs])
|
181
|
-
allowed_values = _lookup_values(term, 'inputTermTypesAllowed')
|
182
|
-
return True if any([
|
183
|
-
_ALLOW_ALL in allowed_values,
|
184
|
-
len(values) == 0
|
185
|
-
]) else any([value in allowed_values for value in values])
|
186
|
-
|
187
|
-
|
188
|
-
def is_input_id_allowed(data: dict, term: dict):
|
189
|
-
inputs = data.get('inputs', [])
|
190
|
-
values = non_empty_list([p.get('term', {}).get('@id') for p in inputs])
|
191
|
-
allowed_values = _lookup_values(term, 'inputTermIdsAllowed')
|
192
|
-
return True if any([
|
193
|
-
_ALLOW_ALL in allowed_values,
|
194
|
-
len(values) == 0
|
195
|
-
]) else any([value in allowed_values for value in values])
|
109
|
+
def _country_in_lookup(country_id: str):
|
110
|
+
def in_lookup(lookup_name: str):
|
111
|
+
return (
|
112
|
+
download_lookup(lookup_name.replace('region', country_id)) is not None or
|
113
|
+
country_id in lookup_term_ids(download_lookup(lookup_name))
|
114
|
+
)
|
115
|
+
return in_lookup
|
196
116
|
|
197
117
|
|
198
|
-
def fallback_country(country_id: str,
|
118
|
+
def fallback_country(country_id: str, lookups: List[str]) -> str:
|
199
119
|
"""
|
200
|
-
Given a
|
201
|
-
|
202
|
-
else fallback to the default "region-world"
|
120
|
+
Given a country `@id`, and lookup tables, checks if a location can be used in lookup file
|
121
|
+
else fallback to the default "region-world".
|
203
122
|
"""
|
204
|
-
is_in_lookup = lambda v: all(v
|
123
|
+
is_in_lookup = lambda v: all(map(_country_in_lookup(v), lookups)) # noqa: E731
|
205
124
|
fallback_id = 'region-world'
|
206
|
-
return country_id if is_in_lookup(country_id) else fallback_id if is_in_lookup(fallback_id) else None
|
125
|
+
return country_id if country_id and is_in_lookup(country_id) else fallback_id if is_in_lookup(fallback_id) else None
|
126
|
+
|
127
|
+
|
128
|
+
def get_region_lookup_value(lookup_name: str, term_id: str, column: str, **log_args):
|
129
|
+
# for performance, try to load the region specific lookup if exists
|
130
|
+
lookup = (
|
131
|
+
download_lookup(lookup_name.replace('region-', f"{term_id}-"))
|
132
|
+
if lookup_name and lookup_name.startswith('region-') else None
|
133
|
+
) or download_lookup(lookup_name)
|
134
|
+
value = get_table_value(lookup, 'termid', term_id, column_name(column))
|
135
|
+
debugMissingLookup(lookup_name, 'termid', term_id, column, value, **log_args)
|
136
|
+
return value
|
@@ -1,11 +1,11 @@
|
|
1
1
|
from hestia_earth.schema import SchemaType
|
2
2
|
from hestia_earth.utils.model import linked_node
|
3
|
-
from hestia_earth.utils.api import download_hestia
|
4
3
|
|
5
|
-
from . import
|
4
|
+
from .term import download_term
|
5
|
+
from .method import include_model
|
6
6
|
|
7
7
|
|
8
8
|
def _new_management(term, model=None):
|
9
9
|
node = {'@type': SchemaType.MANAGEMENT.value}
|
10
|
-
node['term'] = linked_node(term if isinstance(term, dict) else
|
11
|
-
return
|
10
|
+
node['term'] = linked_node(term if isinstance(term, dict) else download_term(term))
|
11
|
+
return include_model(node, model)
|
@@ -3,16 +3,15 @@ from collections.abc import Iterable
|
|
3
3
|
from functools import reduce
|
4
4
|
from statistics import mode, mean
|
5
5
|
from typing import Any, Optional, Union
|
6
|
-
|
7
6
|
from hestia_earth.schema import MeasurementMethodClassification, SchemaType
|
8
|
-
from hestia_earth.utils.api import download_hestia
|
9
7
|
from hestia_earth.utils.model import linked_node
|
10
8
|
from hestia_earth.utils.tools import non_empty_list, flatten, safe_parse_float
|
11
9
|
from hestia_earth.utils.date import diff_in_days
|
12
10
|
|
13
|
-
from
|
14
|
-
from . import
|
15
|
-
from .
|
11
|
+
from . import flatten_args
|
12
|
+
from .blank_node import most_relevant_blank_node_by_id
|
13
|
+
from .method import include_methodModel
|
14
|
+
from .term import download_term, get_lookup_value
|
16
15
|
|
17
16
|
|
18
17
|
# TODO: verify those values
|
@@ -29,8 +28,8 @@ MEASUREMENT_METHOD_CLASSIFICATIONS = [e.value for e in MeasurementMethodClassifi
|
|
29
28
|
|
30
29
|
def _new_measurement(term, model=None):
|
31
30
|
node = {'@type': SchemaType.MEASUREMENT.value}
|
32
|
-
node['term'] = linked_node(term if isinstance(term, dict) else
|
33
|
-
return
|
31
|
+
node['term'] = linked_node(term if isinstance(term, dict) else download_term(term))
|
32
|
+
return include_methodModel(node, term_id=model)
|
34
33
|
|
35
34
|
|
36
35
|
def measurement_value(measurement: dict, is_larger_unit: bool = False) -> float:
|
@@ -0,0 +1,20 @@
|
|
1
|
+
from typing import Union
|
2
|
+
from hestia_earth.schema import TermTermType
|
3
|
+
from hestia_earth.utils.model import linked_node
|
4
|
+
|
5
|
+
from .term import download_term
|
6
|
+
|
7
|
+
|
8
|
+
def include_method(node: dict, term_id: Union[None, str, dict], key='method'):
|
9
|
+
term = (
|
10
|
+
download_term(term_id, TermTermType.MODEL) or download_term(term_id) or {}
|
11
|
+
) if isinstance(term_id, str) else term_id
|
12
|
+
return node | ({} if term is None or term.get('@id') is None else {key: linked_node(term)})
|
13
|
+
|
14
|
+
|
15
|
+
def include_model(node: dict, term_id: Union[None, str, dict]):
|
16
|
+
return include_method(node, term_id=term_id, key='model')
|
17
|
+
|
18
|
+
|
19
|
+
def include_methodModel(node: dict, term_id: Union[None, str, dict]):
|
20
|
+
return include_method(node, term_id=term_id, key='methodModel')
|
@@ -1,15 +1,14 @@
|
|
1
1
|
from hestia_earth.schema import SchemaType
|
2
|
-
from hestia_earth.utils.api import download_hestia
|
3
2
|
from hestia_earth.utils.model import linked_node
|
4
3
|
|
5
|
-
from . import
|
6
|
-
from .
|
4
|
+
from .term import get_lookup_value, download_term
|
5
|
+
from .method import include_model
|
7
6
|
|
8
7
|
|
9
8
|
def _new_practice(term, model=None):
|
10
9
|
node = {'@type': SchemaType.PRACTICE.value}
|
11
|
-
node['term'] = linked_node(term if isinstance(term, dict) else
|
12
|
-
return
|
10
|
+
node['term'] = linked_node(term if isinstance(term, dict) else download_term(term))
|
11
|
+
return include_model(node, model)
|
13
12
|
|
14
13
|
|
15
14
|
def is_model_enabled(model: str, term_id: str, practice: dict = None):
|
@@ -1,26 +1,25 @@
|
|
1
1
|
from hestia_earth.schema import SchemaType, TermTermType, UNIQUENESS_FIELDS
|
2
|
-
from hestia_earth.utils.api import download_hestia
|
3
2
|
from hestia_earth.utils.model import filter_list_term_type, find_term_match, linked_node
|
4
3
|
from hestia_earth.utils.tools import flatten, list_sum, non_empty_list, get_dict_key
|
5
4
|
|
6
|
-
from . import _term_id, _include_model
|
7
5
|
from .blank_node import get_total_value, get_total_value_converted
|
8
6
|
from .constant import Units
|
9
7
|
from .currency import DEFAULT_CURRENCY
|
10
8
|
from .property import _get_nitrogen_content, get_node_property_value
|
11
|
-
from .term import get_rice_paddy_terms
|
9
|
+
from .term import get_rice_paddy_terms, download_term
|
10
|
+
from .method import include_model
|
12
11
|
|
13
12
|
|
14
13
|
def _new_product(term, value: float = None, model=None):
|
15
14
|
node = {'@type': SchemaType.PRODUCT.value}
|
16
|
-
node['term'] = linked_node(term if isinstance(term, dict) else
|
15
|
+
node['term'] = linked_node(term if isinstance(term, dict) else download_term(term))
|
17
16
|
if value is not None:
|
18
17
|
node['value'] = [value]
|
19
18
|
elif value == 0:
|
20
19
|
node['economicValueShare'] = 0
|
21
20
|
node['revenue'] = 0
|
22
21
|
node['currency'] = DEFAULT_CURRENCY
|
23
|
-
return
|
22
|
+
return include_model(node, model)
|
24
23
|
|
25
24
|
|
26
25
|
def _match_list_el(source: list, dest: list, key: str):
|
@@ -1,22 +1,17 @@
|
|
1
|
-
from functools import cache
|
2
|
-
|
3
1
|
from hestia_earth.schema import SchemaType, TermTermType
|
4
|
-
from hestia_earth.utils.api import download_hestia
|
5
2
|
from hestia_earth.utils.lookup import download_lookup, extract_grouped_data, get_table_value, column_name
|
6
3
|
from hestia_earth.utils.model import find_term_match, linked_node
|
7
4
|
from hestia_earth.utils.tools import list_sum, safe_parse_float
|
8
5
|
|
9
|
-
from . import
|
10
|
-
from .
|
11
|
-
from
|
12
|
-
|
13
|
-
download_hestia_cached = cache(download_hestia)
|
6
|
+
from hestia_earth.models.log import debugMissingLookup
|
7
|
+
from .method import include_methodModel
|
8
|
+
from .term import download_term, get_lookup_value
|
14
9
|
|
15
10
|
|
16
11
|
def _new_property(term, model=None):
|
17
12
|
node = {'@type': SchemaType.PROPERTY.value}
|
18
|
-
node['term'] = linked_node(term if isinstance(term, dict) else
|
19
|
-
return
|
13
|
+
node['term'] = linked_node(term if isinstance(term, dict) else download_term(term, TermTermType.PROPERTY))
|
14
|
+
return include_methodModel(node, model)
|
20
15
|
|
21
16
|
|
22
17
|
def merge_properties(properties: list, new_properties: list):
|
@@ -30,7 +25,7 @@ def get_property_lookup_value(model: str, term_id: str, column: str):
|
|
30
25
|
return get_lookup_value(term, column, model=model, term=term_id)
|
31
26
|
|
32
27
|
|
33
|
-
def find_term_property(term, property: str, default=None
|
28
|
+
def find_term_property(term, property: str, default=None) -> dict:
|
34
29
|
"""
|
35
30
|
Get the property by `@id` linked to the `Term` in the glossary.
|
36
31
|
|
@@ -42,23 +37,20 @@ def find_term_property(term, property: str, default=None, keep_in_memory=False)
|
|
42
37
|
The `term.@id` of the property. Example: `nitrogenContent`.
|
43
38
|
default : Any
|
44
39
|
The default value if the property is not found. Defaults to `None`.
|
45
|
-
keep_in_memory: bool
|
46
|
-
Should we cache results from download_hestia(). Default False
|
47
40
|
|
48
41
|
Returns
|
49
42
|
-------
|
50
43
|
dict
|
51
44
|
The property if found, `default` otherwise.
|
52
45
|
"""
|
53
|
-
download_func = download_hestia_cached if keep_in_memory else download_hestia
|
54
46
|
props = term.get('defaultProperties', []) if isinstance(term, dict) else []
|
55
|
-
|
56
|
-
|
47
|
+
props = (
|
48
|
+
download_term(term, TermTermType.PROPERTY) or {}
|
49
|
+
).get('defaultProperties', []) if len(props) == 0 and term else props
|
57
50
|
return find_term_match(props, property, default)
|
58
51
|
|
59
52
|
|
60
|
-
def get_node_property(node: dict, property: str, find_default_property: bool = True
|
61
|
-
keep_in_memory: bool = False) -> dict:
|
53
|
+
def get_node_property(node: dict, property: str, find_default_property: bool = True) -> dict:
|
62
54
|
"""
|
63
55
|
Get the property by `@id` linked to the Blank Node in the glossary.
|
64
56
|
|
@@ -74,8 +66,6 @@ def get_node_property(node: dict, property: str, find_default_property: bool = T
|
|
74
66
|
The `term.@id` of the property. Example: `nitrogenContent`.
|
75
67
|
find_default_property : bool
|
76
68
|
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
|
79
69
|
|
80
70
|
Returns
|
81
71
|
-------
|
@@ -83,7 +73,7 @@ def get_node_property(node: dict, property: str, find_default_property: bool = T
|
|
83
73
|
The property if found, `None` otherwise.
|
84
74
|
"""
|
85
75
|
prop = find_term_match(node.get('properties', []), property, None)
|
86
|
-
return find_term_property(node.get('term', {}), property, {}
|
76
|
+
return find_term_property(node.get('term', {}), property, {}) if all([
|
87
77
|
find_default_property,
|
88
78
|
prop is None
|
89
79
|
]) else (prop or {})
|
@@ -115,7 +105,7 @@ def node_property_lookup_value(model: str, node_term: dict, prop_id: str, defaul
|
|
115
105
|
|
116
106
|
def get_node_property_value(model: str, node: dict, prop_id: str, default=None, handle_percents=True, **log_args):
|
117
107
|
prop = get_node_property(node, prop_id)
|
118
|
-
term = (prop or {}).get('term'
|
108
|
+
term = (prop or {}).get('term') or download_term(prop_id, TermTermType.PROPERTY)
|
119
109
|
units = (term or {}).get('units')
|
120
110
|
value = prop.get('value') if prop else node_property_lookup_value(model, node.get('term', {}), prop_id, **log_args)
|
121
111
|
return default if value is None else (value / 100 if units == '%' and handle_percents else value)
|
@@ -1,11 +1,12 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
1
3
|
from hestia_earth.schema import SchemaType, SiteSiteType, TermTermType
|
2
4
|
from hestia_earth.utils.api import find_related
|
3
|
-
from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name
|
4
5
|
from hestia_earth.utils.tools import non_empty_list, flatten, safe_parse_date
|
5
6
|
|
6
|
-
from hestia_earth.models.log import debugMissingLookup
|
7
7
|
from . import cached_value, _load_calculated_node
|
8
8
|
from .term import get_land_cover_siteTypes
|
9
|
+
from .lookup import get_region_lookup_value
|
9
10
|
|
10
11
|
CACHE_YEARS_KEY = 'years'
|
11
12
|
WATER_TYPES = [
|
@@ -67,7 +68,7 @@ def years_from_cycles(cycles: list):
|
|
67
68
|
] for cycle in cycles))))
|
68
69
|
|
69
70
|
|
70
|
-
def related_cycles(site: dict):
|
71
|
+
def related_cycles(site: dict, cycles_mapping: Optional[dict[str, dict]] = None):
|
71
72
|
"""
|
72
73
|
Get the list of `Cycle` related to the `Site`.
|
73
74
|
Gets the `recalculated` data if available, else `original`.
|
@@ -76,15 +77,23 @@ def related_cycles(site: dict):
|
|
76
77
|
----------
|
77
78
|
site_id : str
|
78
79
|
The `@id` of the `Site`.
|
80
|
+
cycles_mapping : dict[str, dict], optional
|
81
|
+
An optional dict of related `Cycle`s for which the data has already been retrieved, with the format
|
82
|
+
`{cycle_id (str): cycle (dict), ...ids}`.
|
79
83
|
|
80
84
|
Returns
|
81
85
|
-------
|
82
86
|
list[dict]
|
83
87
|
The related `Cycle`s as `dict`.
|
84
88
|
"""
|
89
|
+
cycles_mapping = cycles_mapping or {}
|
90
|
+
|
85
91
|
cached_nodes = [n for n in cached_value(site, 'related', []) if n.get('@type') == SchemaType.CYCLE.value]
|
86
92
|
related_nodes = cached_nodes or find_related(SchemaType.SITE, site.get('@id'), SchemaType.CYCLE) or []
|
87
|
-
return non_empty_list(map(
|
93
|
+
return non_empty_list(map(
|
94
|
+
lambda node: cycles_mapping.get(node['@id']) or _load_calculated_node(node, SchemaType.CYCLE),
|
95
|
+
related_nodes
|
96
|
+
))
|
88
97
|
|
89
98
|
|
90
99
|
def related_years(site: dict):
|
@@ -117,10 +126,7 @@ def valid_site_type(site: dict, site_types=[SiteSiteType.CROPLAND.value, SiteSit
|
|
117
126
|
|
118
127
|
|
119
128
|
def region_factor(model: str, region_id: str, term_id: str, termType: TermTermType):
|
120
|
-
|
121
|
-
value = get_table_value(download_lookup(lookup_name), 'termid', region_id, column_name(term_id))
|
122
|
-
debugMissingLookup(lookup_name, 'termid', region_id, term_id, value, model=model, term=term_id)
|
123
|
-
return value
|
129
|
+
return get_region_lookup_value(f"region-{termType.value}.csv", region_id, term_id, model=model, term=term_id)
|
124
130
|
|
125
131
|
|
126
132
|
def get_land_cover_term_id(site_type: str):
|
@@ -1,5 +1,9 @@
|
|
1
|
+
from typing import Union
|
2
|
+
from functools import lru_cache
|
3
|
+
import json
|
1
4
|
from hestia_earth.schema import SchemaType, TermTermType, SiteSiteType
|
2
|
-
from hestia_earth.utils.
|
5
|
+
from hestia_earth.utils.storage import _load_from_storage
|
6
|
+
from hestia_earth.utils.api import find_node, search, download_hestia
|
3
7
|
from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name
|
4
8
|
|
5
9
|
from .constant import Units
|
@@ -20,6 +24,28 @@ def get_lookup_value(lookup_term: dict, column: str, skip_debug: bool = False, *
|
|
20
24
|
return value
|
21
25
|
|
22
26
|
|
27
|
+
@lru_cache()
|
28
|
+
def _load_term_file(term_type: str):
|
29
|
+
try:
|
30
|
+
filepath = f"glossary/{term_type}.json"
|
31
|
+
nodes = json.loads(_load_from_storage(filepath, glossary=True))
|
32
|
+
return {node.get('@id'): node for node in nodes}
|
33
|
+
except Exception as e:
|
34
|
+
print(e)
|
35
|
+
return {}
|
36
|
+
|
37
|
+
|
38
|
+
def download_term(term: Union[str, dict], termType: Union[str, TermTermType] = None):
|
39
|
+
term_id = term.get('@id', term.get('id')) if isinstance(term, dict) else term
|
40
|
+
term_type = (
|
41
|
+
termType if isinstance(termType, str) else termType.value
|
42
|
+
) if termType else (
|
43
|
+
term.get('termType') if isinstance(term, dict) else None
|
44
|
+
)
|
45
|
+
cached_nodes = _load_term_file(term_type) if term_type else {}
|
46
|
+
return cached_nodes.get(term_id) or download_hestia(term_id)
|
47
|
+
|
48
|
+
|
23
49
|
def get_liquid_fuel_terms():
|
24
50
|
"""
|
25
51
|
Find all "liquid" `fuel` terms from the Glossary:
|
hestia_earth/models/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
VERSION = '0.
|
1
|
+
VERSION = '0.68.0'
|
hestia_earth/orchestrator/log.py
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
import os
|
2
2
|
import sys
|
3
|
-
import platform
|
4
|
-
import resource
|
5
3
|
import logging
|
6
4
|
|
7
5
|
LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
|
@@ -44,15 +42,6 @@ if LOG_FILENAME is not None:
|
|
44
42
|
def _join_args(**kwargs): return ', '.join([f"{key}={value}" for key, value in kwargs.items()])
|
45
43
|
|
46
44
|
|
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
|
-
|
56
45
|
def _log_node_suffix(node: dict = {}):
|
57
46
|
node_type = node.get('@type', node.get('type')) if node else None
|
58
47
|
node_id = node.get('@id', node.get('id', node.get('term', {}).get('@id'))) if node else None
|
@@ -1,18 +1,27 @@
|
|
1
1
|
import os
|
2
|
+
import resource
|
3
|
+
import platform
|
2
4
|
from typing import Union, List
|
3
5
|
import importlib
|
4
6
|
from functools import reduce
|
5
7
|
import concurrent.futures
|
6
8
|
from copy import deepcopy
|
7
|
-
from hestia_earth.utils.tools import non_empty_list
|
9
|
+
from hestia_earth.utils.tools import non_empty_list, current_time_ms
|
8
10
|
|
9
11
|
from hestia_earth.models.version import VERSION
|
10
|
-
from ..log import logger
|
12
|
+
from ..log import logger
|
11
13
|
from ..utils import get_required_model_param, _snakecase
|
12
14
|
from ..strategies.run import should_run
|
13
15
|
from ..strategies.merge import merge
|
14
16
|
|
15
17
|
|
18
|
+
def _memory_usage():
|
19
|
+
factor = 1024 * (
|
20
|
+
1024 if platform.system() in ['Darwin', 'Windows'] else 1
|
21
|
+
)
|
22
|
+
return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / factor
|
23
|
+
|
24
|
+
|
16
25
|
def _max_workers(type: str):
|
17
26
|
try:
|
18
27
|
return int(os.getenv(f"MAX_WORKERS_{type.upper()}"))
|
@@ -78,13 +87,17 @@ def _run_post_checks(data: dict):
|
|
78
87
|
def _run_model(data: dict, model: dict, all_models: list):
|
79
88
|
model_id = get_required_model_param(model, 'model')
|
80
89
|
model_value = model.get('value') or _list_except_item(all_models, model)
|
81
|
-
|
90
|
+
|
91
|
+
now = current_time_ms()
|
92
|
+
memory_usage = _memory_usage()
|
82
93
|
|
83
94
|
module = _import_model(model_id.replace('-', '_'))
|
84
95
|
# if no value is provided, use all the models but this one
|
85
96
|
result = module.get('run')(model_value, data)
|
86
97
|
|
87
|
-
|
98
|
+
logger.info('model_model=%s, model_value=%s, time=%s, memory_used=%s',
|
99
|
+
model_id, model.get('value'), current_time_ms() - now, _memory_usage() - memory_usage)
|
100
|
+
|
88
101
|
return {'data': data, 'model': model, 'version': module.get('version'), 'result': result}
|
89
102
|
|
90
103
|
|
@@ -1,29 +1,11 @@
|
|
1
|
-
from hestia_earth.utils.
|
2
|
-
from hestia_earth.utils.api import download_hestia
|
1
|
+
from hestia_earth.utils.lookup_utils import is_node_type_allowed
|
3
2
|
|
4
3
|
from hestia_earth.orchestrator.log import debugValues, logShouldRun
|
5
4
|
from hestia_earth.orchestrator.utils import get_required_model_param, find_term_match
|
6
5
|
|
7
|
-
_ALLOW_ALL = 'all'
|
8
|
-
|
9
|
-
|
10
|
-
def _lookup_values(term: dict, column: str):
|
11
|
-
term_id = term.get('@id')
|
12
|
-
term_type = term.get('termType')
|
13
|
-
lookup = download_lookup(f"{term_type}.csv")
|
14
|
-
values = get_table_value(lookup, 'termid', term_id, column_name(column))
|
15
|
-
return (values or _ALLOW_ALL).split(';')
|
16
|
-
|
17
|
-
|
18
|
-
def _is_node_type_allowed(data: dict, term_id: str):
|
19
|
-
node_type = data.get('@type', data.get('type'))
|
20
|
-
term = download_hestia(term_id)
|
21
|
-
allowed_types = _lookup_values(term, 'typesAllowed') if term else [_ALLOW_ALL]
|
22
|
-
return True if _ALLOW_ALL in allowed_types or not node_type else node_type in allowed_types
|
23
|
-
|
24
6
|
|
25
7
|
def _run_required(data: dict, model: str, term_id: str):
|
26
|
-
node_type_allowed =
|
8
|
+
node_type_allowed = is_node_type_allowed(data, term_id)
|
27
9
|
|
28
10
|
run_required = all([node_type_allowed])
|
29
11
|
debugValues(data, model=model, term=term_id,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: hestia-earth-models
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.68.0
|
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
|
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.6
|
|
12
12
|
Description-Content-Type: text/markdown
|
13
13
|
License-File: LICENSE
|
14
14
|
Requires-Dist: hestia-earth-schema==30.*
|
15
|
-
Requires-Dist: hestia-earth-utils>=0.
|
15
|
+
Requires-Dist: hestia-earth-utils>=0.14.0
|
16
16
|
Requires-Dist: python-dateutil>=2.8.1
|
17
17
|
Requires-Dist: CurrencyConverter==0.16.8
|
18
18
|
Requires-Dist: haversine>=2.7.0
|