hestia-earth-models 0.65.4__py3-none-any.whl → 0.65.6__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.
- hestia_earth/models/agribalyse2016/fuelElectricity.py +40 -24
- hestia_earth/models/aware/scarcityWeightedWaterUse.py +1 -1
- hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandOccupation.py +1 -1
- hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandTransformation.py +1 -1
- hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsTotalLandUseEffects.py +1 -1
- hestia_earth/models/cml2001Baseline/abioticResourceDepletionFossilFuels.py +9 -5
- hestia_earth/models/cycle/completeness/electricityFuel.py +4 -2
- hestia_earth/models/geospatialDatabase/precipitationAnnual.py +2 -2
- hestia_earth/models/geospatialDatabase/precipitationLongTermAnnualMean.py +2 -2
- hestia_earth/models/geospatialDatabase/precipitationMonthly.py +2 -2
- hestia_earth/models/geospatialDatabase/temperatureAnnual.py +2 -2
- hestia_earth/models/geospatialDatabase/temperatureLongTermAnnualMean.py +2 -2
- hestia_earth/models/geospatialDatabase/temperatureMonthly.py +2 -2
- hestia_earth/models/hestia/landCover.py +101 -68
- hestia_earth/models/hestia/landTransformation100YearAverageDuringCycle.py +49 -0
- hestia_earth/models/hestia/landTransformation20YearAverageDuringCycle.py +49 -0
- hestia_earth/models/hestia/resourceUse_utils.py +200 -0
- hestia_earth/models/hestia/seed_emissions.py +35 -21
- hestia_earth/models/hestia/utils.py +48 -0
- hestia_earth/models/impact_assessment/emissions.py +20 -5
- hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +66 -28
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +26 -142
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +3 -3
- hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +8 -5
- hestia_earth/models/linkedImpactAssessment/utils.py +3 -1
- hestia_earth/models/mocking/search-results.json +27 -4504
- hestia_earth/models/pooreNemecek2018/freshwaterWithdrawalsDuringCycle.py +4 -1
- hestia_earth/models/schererPfister2015/nErosionSoilFlux.py +23 -14
- hestia_earth/models/schererPfister2015/pErosionSoilFlux.py +23 -15
- hestia_earth/models/schererPfister2015/utils.py +3 -5
- hestia_earth/models/site/management.py +82 -22
- hestia_earth/models/utils/blank_node.py +28 -0
- hestia_earth/models/utils/crop.py +5 -1
- hestia_earth/models/utils/fuel.py +4 -1
- hestia_earth/models/utils/impact_assessment.py +7 -5
- hestia_earth/models/utils/pesticideAI.py +1 -0
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.65.4.dist-info → hestia_earth_models-0.65.6.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.65.4.dist-info → hestia_earth_models-0.65.6.dist-info}/RECORD +52 -46
- tests/models/cml2001Baseline/test_abioticResourceDepletionFossilFuels.py +1 -1
- tests/models/hestia/test_landCover.py +2 -1
- tests/models/hestia/test_landTransformation100YearAverageDuringCycle.py +30 -0
- tests/models/hestia/test_landTransformation20YearAverageDuringCycle.py +31 -0
- tests/models/ipcc2019/test_co2ToAirAboveGroundBiomassStockChange.py +3 -1
- tests/models/ipcc2019/test_co2ToAirBelowGroundBiomassStockChange.py +3 -1
- tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChange.py +3 -1
- tests/models/ipcc2019/test_organicCarbonPerHa.py +3 -2
- tests/models/ipcc2019/test_organicCarbonPerHa_tier_1_utils.py +15 -11
- tests/models/utils/test_blank_node.py +22 -7
- {hestia_earth_models-0.65.4.dist-info → hestia_earth_models-0.65.6.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.65.4.dist-info → hestia_earth_models-0.65.6.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.65.4.dist-info → hestia_earth_models-0.65.6.dist-info}/top_level.txt +0 -0
|
@@ -66,7 +66,10 @@ def _run(impact_assessment: dict, product: dict, irrigation: float):
|
|
|
66
66
|
conveyancing = _get_conveyancing_efficiency(impact_assessment, product)
|
|
67
67
|
# convert from m3 to litre
|
|
68
68
|
value = convert_value_from_cycle(
|
|
69
|
-
|
|
69
|
+
impact_assessment,
|
|
70
|
+
product,
|
|
71
|
+
irrigation / conveyancing * 1000 if irrigation > 0 else 0,
|
|
72
|
+
model=MODEL, term_id=TERM_ID
|
|
70
73
|
)
|
|
71
74
|
debugValues(impact_assessment, model=MODEL, term=TERM_ID,
|
|
72
75
|
value=value)
|
|
@@ -1,28 +1,36 @@
|
|
|
1
1
|
from hestia_earth.schema import EmissionMethodTier
|
|
2
|
+
from hestia_earth.utils.tools import list_sum
|
|
2
3
|
|
|
3
4
|
from hestia_earth.models.log import debugValues, logRequirements, logShouldRun
|
|
4
5
|
from hestia_earth.models.utils.emission import _new_emission
|
|
5
6
|
from hestia_earth.models.utils.measurement import most_relevant_measurement_value
|
|
6
|
-
from .utils import get_pcorr, get_p_ef_c1, get_ef_p_c2, get_practice_factor,
|
|
7
|
+
from .utils import get_pcorr, get_p_ef_c1, get_ef_p_c2, get_practice_factor, get_water_input, calculate_R, calculate_A
|
|
7
8
|
from . import MODEL
|
|
8
9
|
|
|
9
10
|
REQUIREMENTS = {
|
|
10
11
|
"Cycle": {
|
|
11
12
|
"endDate": "",
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
"or": {
|
|
14
|
+
"inputs": [
|
|
15
|
+
{"@type": "Input", "value": "> 0", "term.termType": "water"}
|
|
16
|
+
],
|
|
17
|
+
"site": {
|
|
18
|
+
"@type": "Site",
|
|
19
|
+
"measurements": [
|
|
20
|
+
{"@type": "Measurement", "value": "> 0", "term.@id": "precipitationAnnual"}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
},
|
|
15
24
|
"site": {
|
|
16
25
|
"@type": "Site",
|
|
17
26
|
"country": {"@type": "Term", "termType": "region"},
|
|
18
27
|
"measurements": [
|
|
19
|
-
{"@type": "Measurement", "value": "", "term.@id": "nutrientLossToAquaticEnvironment"},
|
|
20
|
-
{"@type": "Measurement", "value": "", "term.@id": "heavyWinterPrecipitation"},
|
|
21
|
-
{"@type": "Measurement", "value": "", "term.@id": "totalNitrogenPerKgSoil"},
|
|
22
|
-
{"@type": "Measurement", "value": "", "term.@id": "
|
|
23
|
-
{"@type": "Measurement", "value": "", "term.@id": "
|
|
24
|
-
{"@type": "Measurement", "value": "", "term.@id": "
|
|
25
|
-
{"@type": "Measurement", "value": "", "term.@id": "slope"}
|
|
28
|
+
{"@type": "Measurement", "value": "> 0", "term.@id": "nutrientLossToAquaticEnvironment"},
|
|
29
|
+
{"@type": "Measurement", "value": "> 0", "term.@id": "heavyWinterPrecipitation"},
|
|
30
|
+
{"@type": "Measurement", "value": "> 0", "term.@id": "totalNitrogenPerKgSoil"},
|
|
31
|
+
{"@type": "Measurement", "value": "> 0", "term.@id": "erodibility"},
|
|
32
|
+
{"@type": "Measurement", "value": "> 0", "term.@id": "slopeLength"},
|
|
33
|
+
{"@type": "Measurement", "value": "> 0", "term.@id": "slope"}
|
|
26
34
|
]
|
|
27
35
|
}
|
|
28
36
|
}
|
|
@@ -82,7 +90,7 @@ def _should_run(cycle: dict):
|
|
|
82
90
|
heavy_winter_precipitation = _get_measurement_content('heavyWinterPrecipitation')
|
|
83
91
|
|
|
84
92
|
precipitation = _get_measurement_content('precipitationAnnual')
|
|
85
|
-
|
|
93
|
+
inputs_water = get_water_input(cycle)
|
|
86
94
|
|
|
87
95
|
practice_factor = get_practice_factor(TERM_ID, site)
|
|
88
96
|
pcorr = get_pcorr(slope / 100) if slope is not None else None
|
|
@@ -92,7 +100,7 @@ def _should_run(cycle: dict):
|
|
|
92
100
|
list_of_contents_for_A = [
|
|
93
101
|
practice_factor, erodibility, slope_length,
|
|
94
102
|
pcorr, p_ef_c1, ef_p_c2]
|
|
95
|
-
list_of_contents_for_R = [heavy_winter_precipitation,
|
|
103
|
+
list_of_contents_for_R = [heavy_winter_precipitation, list_sum([(inputs_water or 0)/10, precipitation or 0])]
|
|
96
104
|
list_of_contents_for_value = [nla_environment, soil_nitrogen_content]
|
|
97
105
|
|
|
98
106
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
@@ -104,7 +112,8 @@ def _should_run(cycle: dict):
|
|
|
104
112
|
p_ef_c1=p_ef_c1,
|
|
105
113
|
ef_p_c2=ef_p_c2,
|
|
106
114
|
heavy_winter_precipitation=heavy_winter_precipitation,
|
|
107
|
-
|
|
115
|
+
inputs_water=inputs_water,
|
|
116
|
+
precipitationAnnual=precipitation,
|
|
108
117
|
nla_environment=nla_environment,
|
|
109
118
|
soil_nitrogen_content=soil_nitrogen_content)
|
|
110
119
|
|
|
@@ -1,29 +1,36 @@
|
|
|
1
1
|
from hestia_earth.schema import EmissionMethodTier
|
|
2
|
+
from hestia_earth.utils.tools import list_sum
|
|
2
3
|
|
|
3
4
|
from hestia_earth.models.log import debugValues, logRequirements, logShouldRun
|
|
4
5
|
from hestia_earth.models.utils.emission import _new_emission
|
|
5
6
|
from hestia_earth.models.utils.measurement import most_relevant_measurement_value
|
|
6
|
-
from .utils import get_pcorr, get_p_ef_c1, get_ef_p_c2, get_practice_factor,
|
|
7
|
+
from .utils import get_pcorr, get_p_ef_c1, get_ef_p_c2, get_practice_factor, get_water_input, calculate_R, calculate_A
|
|
7
8
|
from . import MODEL
|
|
8
9
|
|
|
9
10
|
REQUIREMENTS = {
|
|
10
11
|
"Cycle": {
|
|
11
12
|
"endDate": "",
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
"or": {
|
|
14
|
+
"inputs": [
|
|
15
|
+
{"@type": "Input", "value": "> 0", "term.termType": "water"}
|
|
16
|
+
],
|
|
17
|
+
"site": {
|
|
18
|
+
"@type": "Site",
|
|
19
|
+
"measurements": [
|
|
20
|
+
{"@type": "Measurement", "value": "> 0", "term.@id": "precipitationAnnual"}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
},
|
|
15
24
|
"site": {
|
|
16
25
|
"@type": "Site",
|
|
17
26
|
"country": {"@type": "Term", "termType": "region"},
|
|
18
27
|
"measurements": [
|
|
19
|
-
{"@type": "Measurement", "value": "", "term.@id": "nutrientLossToAquaticEnvironment"},
|
|
20
|
-
{"@type": "Measurement", "value": "", "term.@id": "heavyWinterPrecipitation"},
|
|
21
|
-
{"@type": "Measurement", "value": "", "term.@id": "totalPhosphorusPerKgSoil"},
|
|
22
|
-
{"@type": "Measurement", "value": "", "term.@id": "
|
|
23
|
-
{"@type": "Measurement", "value": "", "term.@id": "
|
|
24
|
-
{"@type": "Measurement", "value": "", "term.@id": "
|
|
25
|
-
{"@type": "Measurement", "value": "", "term.@id": "slopeLength"},
|
|
26
|
-
{"@type": "Measurement", "value": "", "term.@id": "slope"}
|
|
28
|
+
{"@type": "Measurement", "value": "> 0", "term.@id": "nutrientLossToAquaticEnvironment"},
|
|
29
|
+
{"@type": "Measurement", "value": "> 0", "term.@id": "heavyWinterPrecipitation"},
|
|
30
|
+
{"@type": "Measurement", "value": "> 0", "term.@id": "totalPhosphorusPerKgSoil"},
|
|
31
|
+
{"@type": "Measurement", "value": "> 0", "term.@id": "erodibility"},
|
|
32
|
+
{"@type": "Measurement", "value": "> 0", "term.@id": "slopeLength"},
|
|
33
|
+
{"@type": "Measurement", "value": "> 0", "term.@id": "slope"}
|
|
27
34
|
]
|
|
28
35
|
}
|
|
29
36
|
}
|
|
@@ -83,7 +90,7 @@ def _should_run(cycle: dict):
|
|
|
83
90
|
heavy_winter_precipitation = _get_measurement_content('heavyWinterPrecipitation')
|
|
84
91
|
|
|
85
92
|
precipitation = _get_measurement_content('precipitationAnnual')
|
|
86
|
-
|
|
93
|
+
inputs_water = get_water_input(cycle)
|
|
87
94
|
|
|
88
95
|
practice_factor = get_practice_factor(TERM_ID, site)
|
|
89
96
|
pcorr = get_pcorr(slope / 100) if slope is not None else None
|
|
@@ -93,7 +100,7 @@ def _should_run(cycle: dict):
|
|
|
93
100
|
list_of_contents_for_A = [
|
|
94
101
|
practice_factor, erodibility, slope_length,
|
|
95
102
|
pcorr, p_ef_c1, ef_p_c2]
|
|
96
|
-
list_of_contents_for_R = [heavy_winter_precipitation,
|
|
103
|
+
list_of_contents_for_R = [heavy_winter_precipitation, list_sum([(inputs_water or 0)/10, precipitation or 0])]
|
|
97
104
|
list_of_contents_for_value = [nla_environment, soil_phosphorus_content]
|
|
98
105
|
|
|
99
106
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
@@ -105,7 +112,8 @@ def _should_run(cycle: dict):
|
|
|
105
112
|
p_ef_c1=p_ef_c1,
|
|
106
113
|
ef_p_c2=ef_p_c2,
|
|
107
114
|
heavy_winter_precipitation=heavy_winter_precipitation,
|
|
108
|
-
|
|
115
|
+
inputs_water=inputs_water,
|
|
116
|
+
precipitationAnnual=precipitation,
|
|
109
117
|
nla_environment=nla_environment,
|
|
110
118
|
soil_phosphorus_content=soil_phosphorus_content)
|
|
111
119
|
|
|
@@ -64,11 +64,9 @@ def get_ef_p_c2(term_id: str, cycle: dict):
|
|
|
64
64
|
get_lookup_value(country, 'EF_P_C2', model=MODEL, term=term_id), None)
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
irrigation = list_sum(get_total_value(filter_irrigation))
|
|
71
|
-
return list_sum([irrigation/10, precipitation or 0])
|
|
67
|
+
def get_water_input(cycle: dict):
|
|
68
|
+
inputs_water = filter_list_term_type(cycle.get('inputs', []), TermTermType.WATER)
|
|
69
|
+
return list_sum(get_total_value(inputs_water), default=None)
|
|
72
70
|
|
|
73
71
|
|
|
74
72
|
def calculate_R(heavy_winter_precipitation: bool, water: float):
|
|
@@ -10,11 +10,18 @@ tillage, cropResidueManagement and landUseManagement.
|
|
|
10
10
|
All values are copied from the source node, except for crop and forage terms in which case the dates are copied from the
|
|
11
11
|
cycle.
|
|
12
12
|
|
|
13
|
+
Where `startDate` is missing from landCover products, gap-filling is attempted using `endDate` - `maximumCycleDuration`.
|
|
14
|
+
This is the `endDate` of the `landCover` product.
|
|
15
|
+
This ensures no overlapping date ranges.
|
|
16
|
+
If both `endDate` and `startDate` are missing from the product, these will be gap-filled from the `Cycle`.
|
|
17
|
+
|
|
13
18
|
When nodes are chronologically consecutive with "% area" or "boolean" units and the same term and value, they are
|
|
14
19
|
condensed into a single node to aid readability.
|
|
15
20
|
"""
|
|
21
|
+
from datetime import timedelta, datetime
|
|
16
22
|
from functools import reduce
|
|
17
23
|
from hestia_earth.schema import TermTermType, SiteSiteType
|
|
24
|
+
from hestia_earth.utils.lookup import column_name, get_table_value, download_lookup
|
|
18
25
|
from hestia_earth.utils.model import filter_list_term_type
|
|
19
26
|
from hestia_earth.utils.tools import safe_parse_float, flatten
|
|
20
27
|
from hestia_earth.utils.blank_node import get_node_value
|
|
@@ -23,12 +30,12 @@ from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
|
|
|
23
30
|
from hestia_earth.models.utils import _include, _omit, group_by
|
|
24
31
|
from hestia_earth.models.utils.management import _new_management
|
|
25
32
|
from hestia_earth.models.utils.term import get_lookup_value
|
|
26
|
-
from hestia_earth.models.utils.blank_node import condense_nodes
|
|
27
|
-
from hestia_earth.models.utils.crop import get_landCover_term_id
|
|
33
|
+
from hestia_earth.models.utils.blank_node import condense_nodes, DatestrFormat, _gapfill_datestr, DatestrGapfillMode
|
|
28
34
|
from hestia_earth.models.utils.site import (
|
|
29
35
|
related_cycles, get_land_cover_term_id as get_landCover_term_id_from_site_type
|
|
30
36
|
)
|
|
31
37
|
from . import MODEL
|
|
38
|
+
from ..utils.crop import get_landCover_term_id
|
|
32
39
|
|
|
33
40
|
REQUIREMENTS = {
|
|
34
41
|
"Site": {
|
|
@@ -81,13 +88,13 @@ RETURNS = {
|
|
|
81
88
|
}]
|
|
82
89
|
}
|
|
83
90
|
LOOKUPS = {
|
|
84
|
-
"crop": ["landCoverTermId"],
|
|
91
|
+
"crop": ["landCoverTermId", "maximumCycleDuration"],
|
|
85
92
|
"forage": ["landCoverTermId"],
|
|
86
93
|
"inorganicFertiliser": "nitrogenContent",
|
|
87
94
|
"organicFertiliser": "ANIMAL_MANURE",
|
|
88
95
|
"soilAmendment": "PRACTICE_INCREASING_C_INPUT",
|
|
89
96
|
"landUseManagement": "GAP_FILL_TO_MANAGEMENT",
|
|
90
|
-
"property": "GAP_FILL_TO_MANAGEMENT"
|
|
97
|
+
"property": ["GAP_FILL_TO_MANAGEMENT", "CALCULATE_TOTAL_LAND_COVER_SHARE_SEPARATELY"]
|
|
91
98
|
}
|
|
92
99
|
MODEL_KEY = 'management'
|
|
93
100
|
|
|
@@ -126,19 +133,49 @@ _INPUT_RULES = {
|
|
|
126
133
|
_SKIP_LAND_COVER_SITE_TYPES = [
|
|
127
134
|
SiteSiteType.CROPLAND.value
|
|
128
135
|
]
|
|
136
|
+
_CYCLE_DATE_TERM_TYPES = {TermTermType.CROP.value, TermTermType.FORAGE.value}
|
|
129
137
|
|
|
130
138
|
|
|
131
139
|
def management(data: dict):
|
|
132
140
|
node = _new_management(data.get('id'))
|
|
133
141
|
node['value'] = data['value']
|
|
134
|
-
node['endDate'] = data['endDate']
|
|
142
|
+
node['endDate'] = _gap_filled_date_only_str(data['endDate'])
|
|
135
143
|
if data.get('startDate'):
|
|
136
|
-
node['startDate'] = data['startDate']
|
|
144
|
+
node['startDate'] = _gap_filled_date_only_str(date_str=data['startDate'], mode=DatestrGapfillMode.START)
|
|
137
145
|
if data.get('properties'):
|
|
138
146
|
node['properties'] = data['properties']
|
|
139
147
|
return node
|
|
140
148
|
|
|
141
149
|
|
|
150
|
+
def _get_maximum_cycle_duration(land_cover_id: str):
|
|
151
|
+
lookup = download_lookup("crop.csv")
|
|
152
|
+
return safe_parse_float(
|
|
153
|
+
get_table_value(lookup, column_name('landCoverTermId'), land_cover_id, column_name('maximumCycleDuration'))
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _gap_filled_date_only_str(date_str: str, mode: str = DatestrGapfillMode.END) -> str:
|
|
158
|
+
return _gapfill_datestr(datestr=date_str, mode=mode)[:10]
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _gap_filled_date_obj(date_str: str, mode: str = DatestrGapfillMode.END) -> datetime:
|
|
162
|
+
return datetime.strptime(
|
|
163
|
+
_gap_filled_date_only_str(date_str=date_str, mode=mode),
|
|
164
|
+
DatestrFormat.YEAR_MONTH_DAY.value
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _gap_filled_start_date(land_cover_id: str, end_date: str, cycle: dict) -> str:
|
|
169
|
+
"""If possible, gap-fill the startDate based on the endDate - maximumCycleDuration"""
|
|
170
|
+
maximum_cycle_duration = _get_maximum_cycle_duration(land_cover_id)
|
|
171
|
+
return max(
|
|
172
|
+
_gap_filled_date_obj(end_date) - timedelta(days=maximum_cycle_duration)
|
|
173
|
+
if maximum_cycle_duration else datetime.fromtimestamp(0),
|
|
174
|
+
_gap_filled_date_obj(cycle.get("startDate"), mode=DatestrGapfillMode.START)
|
|
175
|
+
if cycle.get("startDate") else datetime.fromtimestamp(0)
|
|
176
|
+
) if any([maximum_cycle_duration, cycle.get("startDate")]) else None
|
|
177
|
+
|
|
178
|
+
|
|
142
179
|
def _should_gap_fill(term: dict):
|
|
143
180
|
value = get_lookup_value(lookup_term=term, column='GAP_FILL_TO_MANAGEMENT')
|
|
144
181
|
return bool(value)
|
|
@@ -167,20 +204,27 @@ def _copy_item_if_exists(source: dict, keys: list[str] = None, dest: dict = None
|
|
|
167
204
|
return reduce(lambda p, c: p | ({c: source[c]} if source.get(c) else {}), keys or [], dest or {})
|
|
168
205
|
|
|
169
206
|
|
|
170
|
-
def _get_landCover_term_id(product: dict) -> str:
|
|
171
|
-
term = product.get('term', {})
|
|
172
|
-
return get_landCover_term_id(term, model=MODEL, term=term.get('@id'), model_key=MODEL_KEY)
|
|
173
|
-
|
|
174
|
-
|
|
175
207
|
def _get_relevant_items(cycle: dict, item_name: str, relevant_terms: list):
|
|
176
208
|
"""
|
|
177
209
|
Get items from the list of cycles with any of the relevant terms.
|
|
178
210
|
Also adds dates from Cycle.
|
|
179
211
|
"""
|
|
180
|
-
|
|
181
|
-
_include(cycle, ["startDate", "endDate"]) |
|
|
212
|
+
items = [
|
|
213
|
+
_include(cycle, ["startDate", "endDate"]) |
|
|
214
|
+
_include(
|
|
215
|
+
{
|
|
216
|
+
"startDate": _gap_filled_start_date(
|
|
217
|
+
land_cover_id=get_landCover_term_id(item.get('term', {})),
|
|
218
|
+
end_date=item.get("endDate") if "endDate" in item else cycle.get("endDate", ""),
|
|
219
|
+
cycle=cycle
|
|
220
|
+
)
|
|
221
|
+
} if "startDate" not in item else {},
|
|
222
|
+
"startDate"
|
|
223
|
+
) |
|
|
224
|
+
item
|
|
182
225
|
for item in filter_list_term_type(cycle.get(item_name, []), relevant_terms)
|
|
183
226
|
]
|
|
227
|
+
return items
|
|
184
228
|
|
|
185
229
|
|
|
186
230
|
def _process_rule(node: dict, term: dict) -> list:
|
|
@@ -228,11 +272,12 @@ def _run_products(cycle: dict, products: list, total_products: int = None, use_c
|
|
|
228
272
|
source=product,
|
|
229
273
|
keys=['properties', 'startDate', 'endDate'],
|
|
230
274
|
dest={
|
|
231
|
-
"term": {'@id':
|
|
275
|
+
"term": {'@id': get_landCover_term_id(product.get('term', {}))},
|
|
232
276
|
"value": round(100 / (total_products or len(products)), 2)
|
|
233
277
|
}
|
|
234
278
|
) | (
|
|
235
|
-
default_dates if use_cycle_dates
|
|
279
|
+
default_dates if use_cycle_dates or product.get("term", {}).get("termType") in _CYCLE_DATE_TERM_TYPES
|
|
280
|
+
else {}
|
|
236
281
|
))
|
|
237
282
|
for product in products
|
|
238
283
|
]
|
|
@@ -242,7 +287,7 @@ def _run_from_landCover(cycle: dict, crop_forage_products: list):
|
|
|
242
287
|
"""
|
|
243
288
|
Copy landCover items, and include crop/forage landCover items with properties to count in ratio.
|
|
244
289
|
"""
|
|
245
|
-
|
|
290
|
+
land_cover_products = [
|
|
246
291
|
_map_to_value(_extract_node_value(
|
|
247
292
|
_include(
|
|
248
293
|
value=product,
|
|
@@ -254,14 +299,29 @@ def _run_from_landCover(cycle: dict, crop_forage_products: list):
|
|
|
254
299
|
relevant_terms=[TermTermType.LANDCOVER]
|
|
255
300
|
)
|
|
256
301
|
]
|
|
257
|
-
return
|
|
302
|
+
return land_cover_products + _run_products(
|
|
258
303
|
cycle,
|
|
259
304
|
crop_forage_products,
|
|
260
|
-
total_products=len(crop_forage_products) + len(
|
|
305
|
+
total_products=len(crop_forage_products) + len(land_cover_products),
|
|
261
306
|
use_cycle_dates=True
|
|
262
307
|
)
|
|
263
308
|
|
|
264
309
|
|
|
310
|
+
def _should_group_landCover(term: dict):
|
|
311
|
+
value = get_lookup_value(lookup_term=term, column='CALCULATE_TOTAL_LAND_COVER_SHARE_SEPARATELY')
|
|
312
|
+
return bool(value)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def _has_prop_grouped_with_landCover(product: dict):
|
|
316
|
+
return bool(
|
|
317
|
+
next((
|
|
318
|
+
p
|
|
319
|
+
for p in product.get('properties', [])
|
|
320
|
+
if _should_group_landCover(p.get('term', {}))
|
|
321
|
+
), None)
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
|
|
265
325
|
def _run_from_crop_forage(cycle: dict):
|
|
266
326
|
products = _get_relevant_items(
|
|
267
327
|
cycle=cycle,
|
|
@@ -269,13 +329,13 @@ def _run_from_crop_forage(cycle: dict):
|
|
|
269
329
|
relevant_terms=[TermTermType.CROP, TermTermType.FORAGE]
|
|
270
330
|
)
|
|
271
331
|
# only take products with a matching landCover term
|
|
272
|
-
products =
|
|
332
|
+
products = [p for p in products if get_landCover_term_id(p.get('term', {}))]
|
|
273
333
|
# remove any properties that should not get gap-filled
|
|
274
334
|
products = list(map(_filter_properties, products))
|
|
275
335
|
|
|
276
|
-
# split products with properties
|
|
277
|
-
products_with_gap_filled_props = [p for p in products if p
|
|
278
|
-
products_without_gap_filled_props = [p for p in products if not p
|
|
336
|
+
# split products with properties that group with landCover
|
|
337
|
+
products_with_gap_filled_props = [p for p in products if _has_prop_grouped_with_landCover(p)]
|
|
338
|
+
products_without_gap_filled_props = [p for p in products if not _has_prop_grouped_with_landCover(p)]
|
|
279
339
|
|
|
280
340
|
return _run_from_landCover(
|
|
281
341
|
cycle=cycle,
|
|
@@ -663,6 +663,7 @@ DATESTR_FORMAT_TO_EXPECTED_LENGTH = {
|
|
|
663
663
|
|
|
664
664
|
DatestrGapfillMode = Enum("DatestrGapfillMode", [
|
|
665
665
|
"START",
|
|
666
|
+
"MIDDLE",
|
|
666
667
|
"END"
|
|
667
668
|
])
|
|
668
669
|
"""
|
|
@@ -738,8 +739,25 @@ def _gapfill_datestr_end(datestr: str, format: DatestrFormat) -> str:
|
|
|
738
739
|
return datestr + completion_str[len(datestr):]
|
|
739
740
|
|
|
740
741
|
|
|
742
|
+
def _gapfill_datestr_middle(datestr: str, format: DatestrFormat) -> str:
|
|
743
|
+
"""
|
|
744
|
+
Gap-fill an incomplete datestr with the middle value, halfway between the latest and earliest values.
|
|
745
|
+
"""
|
|
746
|
+
start_date_obj = datetime.strptime(
|
|
747
|
+
_gapfill_datestr_start(datestr),
|
|
748
|
+
DatestrFormat.YEAR_MONTH_DAY_HOUR_MINUTE_SECOND.value
|
|
749
|
+
)
|
|
750
|
+
end_date_obj = datetime.strptime(
|
|
751
|
+
_gapfill_datestr_end(datestr, format=format),
|
|
752
|
+
DatestrFormat.YEAR_MONTH_DAY_HOUR_MINUTE_SECOND.value
|
|
753
|
+
)
|
|
754
|
+
middle_date = start_date_obj + (end_date_obj - start_date_obj) / 2
|
|
755
|
+
return datetime.strftime(middle_date, DatestrFormat.YEAR_MONTH_DAY_HOUR_MINUTE_SECOND.value)
|
|
756
|
+
|
|
757
|
+
|
|
741
758
|
DATESTR_GAPFILL_MODE_TO_GAPFILL_FUNCTION = {
|
|
742
759
|
DatestrGapfillMode.START: _gapfill_datestr_start,
|
|
760
|
+
DatestrGapfillMode.MIDDLE: _gapfill_datestr_middle,
|
|
743
761
|
DatestrGapfillMode.END: _gapfill_datestr_end
|
|
744
762
|
}
|
|
745
763
|
|
|
@@ -757,6 +775,16 @@ def _gapfill_datestr(datestr: str, mode: DatestrGapfillMode = DatestrGapfillMode
|
|
|
757
775
|
return DATESTR_GAPFILL_MODE_TO_GAPFILL_FUNCTION[mode](_datestr, format) if should_run else _datestr
|
|
758
776
|
|
|
759
777
|
|
|
778
|
+
def _str_dates_match(date_str_one: str, date_str_two: str, mode=DatestrGapfillMode.END) -> bool:
|
|
779
|
+
"""
|
|
780
|
+
Comparison of non-gap-filled string dates.
|
|
781
|
+
example: For end dates, '2010' would match '2010-12-31', but not '2010-01-01'
|
|
782
|
+
"""
|
|
783
|
+
return (
|
|
784
|
+
_gapfill_datestr(datestr=date_str_one, mode=mode) == _gapfill_datestr(datestr=date_str_two, mode=mode)
|
|
785
|
+
)
|
|
786
|
+
|
|
787
|
+
|
|
760
788
|
def _datetime_within_range(datetime: datetime, range: DatetimeRange) -> bool:
|
|
761
789
|
"""
|
|
762
790
|
Determine whether or not a `datetime` falls within a `DatetimeRange`.
|
|
@@ -65,4 +65,8 @@ def valid_site_type(cycle: dict, include_permanent_pasture=False):
|
|
|
65
65
|
|
|
66
66
|
def get_landCover_term_id(lookup_term: dict, **log_args) -> str:
|
|
67
67
|
value = get_lookup_value(lookup_term, 'landCoverTermId', **log_args)
|
|
68
|
-
return
|
|
68
|
+
return (
|
|
69
|
+
lookup_term.get("@id") if lookup_term.get("termType") == TermTermType.LANDCOVER.value else
|
|
70
|
+
value.split(';')[0] if value else
|
|
71
|
+
None
|
|
72
|
+
)
|
|
@@ -13,7 +13,10 @@ def impact_lookup_value(model: str, term_id: str, impact_assessment: dict, looku
|
|
|
13
13
|
fuels = filter_list_term_type(cycle.get('inputs', []), TermTermType.FUEL)
|
|
14
14
|
has_fuels_inputs = len(fuels) > 0
|
|
15
15
|
fuels_total_value = convert_value_from_cycle(
|
|
16
|
-
|
|
16
|
+
impact_assessment,
|
|
17
|
+
product,
|
|
18
|
+
cycle_lookup_value(model, term_id, cycle, fuels, lookup_col),
|
|
19
|
+
model=model, term_id=term_id
|
|
17
20
|
) if has_fuels_inputs else None
|
|
18
21
|
logRequirements(impact_assessment, model=model, term=term_id,
|
|
19
22
|
term_type_electricityFuel_complete=fuel_complete,
|
|
@@ -4,7 +4,7 @@ from hestia_earth.utils.lookup import download_lookup
|
|
|
4
4
|
from hestia_earth.utils.model import find_term_match, filter_list_term_type
|
|
5
5
|
from hestia_earth.utils.tools import list_sum, safe_parse_date
|
|
6
6
|
|
|
7
|
-
from hestia_earth.models.log import
|
|
7
|
+
from hestia_earth.models.log import debugValues, log_as_table
|
|
8
8
|
from .lookup import all_factor_value, _term_factor_value, _aware_factor_value, fallback_country
|
|
9
9
|
from .product import find_by_product
|
|
10
10
|
from .site import region_level_1_id
|
|
@@ -244,13 +244,15 @@ def emission_value(impact_assessment: dict, term_id: str):
|
|
|
244
244
|
return find_term_match(impact_assessment.get('emissionsResourceUse', []), term_id).get('value')
|
|
245
245
|
|
|
246
246
|
|
|
247
|
-
def convert_value_from_cycle(
|
|
247
|
+
def convert_value_from_cycle(
|
|
248
|
+
log_node: dict, product: dict, value: float, default=None, model: str = None, term_id: str = None
|
|
249
|
+
):
|
|
248
250
|
pyield = list_sum(product.get('value', [])) if product else 0
|
|
249
251
|
economic_value = product.get('economicValueShare') if product else 0
|
|
250
252
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
253
|
+
debugValues(log_node, model=model, term=term_id,
|
|
254
|
+
product_yield=pyield,
|
|
255
|
+
economicValueShare=economic_value)
|
|
254
256
|
|
|
255
257
|
return (value / pyield) * economic_value / 100 if all([
|
|
256
258
|
value is not None, pyield > 0, economic_value
|
|
@@ -22,6 +22,7 @@ def impact_lookup_value(model: str, term_id: str, impact_assessment: dict, looku
|
|
|
22
22
|
get_pesticides_from_inputs(cycle)
|
|
23
23
|
has_pesticides_inputs = len(pesticides) > 0
|
|
24
24
|
pesticides_total_value = convert_value_from_cycle(
|
|
25
|
+
log_node=impact_assessment,
|
|
25
26
|
product=product,
|
|
26
27
|
value=cycle_lookup_value(model, term_id, cycle, pesticides, lookup_col),
|
|
27
28
|
default=None,
|
hestia_earth/models/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
VERSION = '0.65.
|
|
1
|
+
VERSION = '0.65.6'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: hestia-earth-models
|
|
3
|
-
Version: 0.65.
|
|
3
|
+
Version: 0.65.6
|
|
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.13.
|
|
15
|
+
Requires-Dist: hestia-earth-utils>=0.13.14
|
|
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
|