hestia-earth-models 0.64.7__py3-none-any.whl → 0.64.9__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 (58) hide show
  1. hestia_earth/models/cycle/animal/milkYield.py +10 -22
  2. hestia_earth/models/cycle/siteArea.py +2 -1
  3. hestia_earth/models/cycle/unknownPreSeasonWaterRegime.py +0 -1
  4. hestia_earth/models/environmentalFootprintV3/soilQualityIndexLandOccupation.py +73 -82
  5. hestia_earth/models/environmentalFootprintV3/soilQualityIndexLandTransformation.py +168 -0
  6. hestia_earth/models/environmentalFootprintV3/soilQualityIndexTotalLandUseEffects.py +77 -0
  7. hestia_earth/models/environmentalFootprintV3/utils.py +1 -1
  8. hestia_earth/models/faostat2018/landTransformationFromCropland100YearAverage.py +3 -2
  9. hestia_earth/models/faostat2018/landTransformationFromCropland20YearAverage.py +3 -2
  10. hestia_earth/models/frischknechtEtAl2000/ionisingRadiationKbqU235Eq.py +69 -37
  11. hestia_earth/models/hyde32/utils.py +4 -0
  12. hestia_earth/models/ipcc2019/animal/fatContent.py +38 -0
  13. hestia_earth/models/ipcc2019/animal/liveweightGain.py +3 -54
  14. hestia_earth/models/ipcc2019/animal/liveweightPerHead.py +3 -54
  15. hestia_earth/models/ipcc2019/animal/pastureGrass.py +3 -1
  16. hestia_earth/models/ipcc2019/animal/pregnancyRateTotal.py +38 -0
  17. hestia_earth/models/ipcc2019/animal/trueProteinContent.py +38 -0
  18. hestia_earth/models/ipcc2019/animal/utils.py +87 -3
  19. hestia_earth/models/ipcc2019/animal/weightAtMaturity.py +4 -10
  20. hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChangeLandUseChange.py +191 -0
  21. hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChangeLandUseChange.py +204 -0
  22. hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +255 -35
  23. hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChangeManagementChange.py +63 -149
  24. hestia_earth/models/ipcc2019/pastureGrass.py +3 -1
  25. hestia_earth/models/mocking/search-results.json +2026 -26
  26. hestia_earth/models/pooreNemecek2018/landOccupationDuringCycle.py +1 -1
  27. hestia_earth/models/poschEtAl2008/terrestrialAcidificationPotentialAccumulatedExceedance.py +4 -1
  28. hestia_earth/models/poschEtAl2008/terrestrialEutrophicationPotentialAccumulatedExceedance.py +4 -1
  29. hestia_earth/models/site/management.py +3 -5
  30. hestia_earth/models/utils/__init__.py +5 -4
  31. hestia_earth/models/utils/impact_assessment.py +13 -4
  32. hestia_earth/models/utils/input.py +5 -2
  33. hestia_earth/models/utils/site.py +4 -2
  34. hestia_earth/models/version.py +1 -1
  35. {hestia_earth_models-0.64.7.dist-info → hestia_earth_models-0.64.9.dist-info}/METADATA +2 -2
  36. {hestia_earth_models-0.64.7.dist-info → hestia_earth_models-0.64.9.dist-info}/RECORD +58 -44
  37. tests/models/cycle/animal/test_milkYield.py +1 -14
  38. tests/models/environmentalFootprintV3/test_soilQualityIndexLandOccupation.py +97 -66
  39. tests/models/environmentalFootprintV3/test_soilQualityIndexLandTransformation.py +176 -0
  40. tests/models/environmentalFootprintV3/test_soilQualityIndexTotalLandUseEffects.py +55 -0
  41. tests/models/frischknechtEtAl2000/test_ionisingRadiationKbqU235Eq.py +67 -44
  42. tests/models/ipcc2019/animal/test_fatContent.py +22 -0
  43. tests/models/ipcc2019/animal/test_liveweightGain.py +4 -2
  44. tests/models/ipcc2019/animal/test_liveweightPerHead.py +4 -2
  45. tests/models/ipcc2019/animal/test_pregnancyRateTotal.py +22 -0
  46. tests/models/ipcc2019/animal/test_trueProteinContent.py +22 -0
  47. tests/models/ipcc2019/animal/test_weightAtMaturity.py +2 -1
  48. tests/models/ipcc2019/test_co2ToAirAboveGroundBiomassStockChangeLandUseChange.py +83 -0
  49. tests/models/ipcc2019/test_co2ToAirBelowGroundBiomassStockChangeLandUseChange.py +83 -0
  50. tests/models/ipcc2019/test_co2ToAirCarbonStockChange_utils.py +6 -6
  51. tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChangeManagementChange.py +5 -4
  52. tests/models/poschEtAl2008/test_terrestrialAcidificationPotentialAccumulatedExceedance.py +30 -17
  53. tests/models/poschEtAl2008/test_terrestrialEutrophicationPotentialAccumulatedExceedance.py +28 -14
  54. tests/models/site/test_management.py +4 -1
  55. tests/models/utils/test_input.py +65 -1
  56. {hestia_earth_models-0.64.7.dist-info → hestia_earth_models-0.64.9.dist-info}/LICENSE +0 -0
  57. {hestia_earth_models-0.64.7.dist-info → hestia_earth_models-0.64.9.dist-info}/WHEEL +0 -0
  58. {hestia_earth_models-0.64.7.dist-info → hestia_earth_models-0.64.9.dist-info}/top_level.txt +0 -0
@@ -1,18 +1,16 @@
1
1
  """
2
2
  Milk Yield
3
3
 
4
- This model gap-fills the practice "Milk yield per animal X (raw/FPCM)" (e.g. `Milk yield per cow (raw)`) when:
5
- - the practice is added to the Cycle itself, and we can match it with the `term` of an Animal blank node;
6
- - the Animal blank node Term has a lookup value of `kgDayMilkForFeedingOffspring`.
4
+ This model gap-fills the practice "Milk yield per animal X (raw/FPCM)" (e.g. `Milk yield per cow (raw)`) when
5
+ the practice is added to the Cycle itself, and we can match it with the `term` of an Animal blank node.
7
6
  """
8
7
  from hestia_earth.schema import TermTermType
9
8
  from hestia_earth.utils.model import filter_list_term_type
10
- from hestia_earth.utils.tools import non_empty_list, safe_parse_float
9
+ from hestia_earth.utils.tools import non_empty_list
11
10
 
12
11
  from hestia_earth.models.log import logShouldRun, logRequirements, log_blank_nodes_id
13
12
  from hestia_earth.models.utils.blank_node import merge_blank_nodes
14
13
  from hestia_earth.models.utils.term import get_lookup_value
15
- from hestia_earth.models.utils.practice import _new_practice
16
14
  from .. import MODEL
17
15
 
18
16
  REQUIREMENTS = {
@@ -21,12 +19,10 @@ REQUIREMENTS = {
21
19
  "@type": "Animal",
22
20
  "term.termType": "liveAnimal"
23
21
  }],
24
- "optional": {
25
- "practices": [{
26
- "@type": "Practice",
27
- "term.termType": "animalManagement"
28
- }]
29
- }
22
+ "practices": [{
23
+ "@type": "Practice",
24
+ "term.termType": "animalManagement"
25
+ }]
30
26
  }
31
27
  }
32
28
  RETURNS = {
@@ -38,27 +34,19 @@ RETURNS = {
38
34
  }]
39
35
  }
40
36
  LOOKUPS = {
41
- "liveAnimal": ["milkYieldPracticeTermIds", "kgDayMilkForFeedingOffspring"]
37
+ "liveAnimal": "milkYieldPracticeTermIds"
42
38
  }
43
39
 
44
40
  MODEL_KEY = 'milkYield'
45
41
 
46
42
 
47
- def _default_practice(animal: dict, practice_term_id: str):
48
- term = animal.get('term', {})
49
- value = get_lookup_value(term, LOOKUPS['liveAnimal'][1], model=MODEL, model_key=MODEL_KEY)
50
- return (_new_practice(practice_term_id) | {'value': [safe_parse_float(value)]}) if value else None
51
-
52
-
53
43
  def _run(cycle: dict, animal: dict):
54
44
  term = animal.get('term', {})
55
45
  term_id = term.get('@id')
56
- value = get_lookup_value(term, LOOKUPS['liveAnimal'][0], model=MODEL, model_key=MODEL_KEY)
46
+ value = get_lookup_value(term, LOOKUPS['liveAnimal'], model=MODEL, model_key=MODEL_KEY)
57
47
  practice_ids = non_empty_list((value or '').split(';'))
58
48
  practices = non_empty_list(
59
- [p for p in cycle.get('practices', []) if p.get('term', {}).get('@id') in practice_ids] or (
60
- [_default_practice(animal, practice_ids[0])] if practice_ids else []
61
- )
49
+ [p for p in cycle.get('practices', []) if p.get('term', {}).get('@id') in practice_ids]
62
50
  )
63
51
 
64
52
  logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
@@ -9,6 +9,7 @@ from hestia_earth.utils.model import filter_list_term_type, find_term_match
9
9
  from hestia_earth.utils.tools import list_sum
10
10
 
11
11
  from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
12
+ from hestia_earth.models.utils import square_meter_to_hectare
12
13
  from . import MODEL
13
14
 
14
15
  REQUIREMENTS = {
@@ -48,7 +49,7 @@ def _run(cycle: dict):
48
49
  animals = filter_list_term_type(cycle.get('animals', []), TermTermType.LIVEANIMAL)
49
50
  stocking_density = find_term_match(
50
51
  cycle.get('practices', []), 'stockingDensityAnimalHousingAverage', {}).get('value', [])
51
- return round(list_sum([a.get('value') for a in animals]) / list_sum(stocking_density), 7)
52
+ return square_meter_to_hectare(round(list_sum([a.get('value') for a in animals]) / list_sum(stocking_density), 7))
52
53
 
53
54
 
54
55
  def _should_run(cycle: dict, site: dict, key=MODEL_KEY):
@@ -36,7 +36,6 @@ def _practice():
36
36
  def _should_run(cycle: dict):
37
37
  practices = cycle.get('practices', [])
38
38
  flooded_terms = get_flooded_pre_season_terms()
39
- print(flooded_terms)
40
39
  existing_practice = next((p for p in practices if p.get('term', {}).get('@id') in flooded_terms), None)
41
40
 
42
41
  logRequirements(cycle, model=MODEL, term=TERM_ID,
@@ -1,41 +1,37 @@
1
+ """
2
+ Characterises [soilQualityIndexLandOccupation](https://hestia.earth/term/soilQualityIndexLandOccupation)
3
+ based on an updated [LANCA model (De Laurentiis et al. 2019)](
4
+ http://publications.jrc.ec.europa.eu/repository/handle/JRC113865) and on the LANCA (Regionalised) Characterisation
5
+ Factors version 2.5 (Horn and Meier, 2018).
6
+ """
7
+ from typing import Tuple
8
+
1
9
  from hestia_earth.schema import TermTermType
2
10
  from hestia_earth.utils.lookup import download_lookup
3
- from hestia_earth.utils.tools import list_sum, non_empty_list
11
+ from hestia_earth.utils.model import filter_list_term_type
12
+ from hestia_earth.utils.tools import list_sum
4
13
 
5
14
  from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
6
- from hestia_earth.models.utils import hectar_to_square_meter, days_to_years
7
- from hestia_earth.models.utils.blank_node import most_relevant_blank_node_by_type
8
15
  from hestia_earth.models.utils.indicator import _new_indicator
9
16
  from hestia_earth.models.utils.landCover import get_pef_grouping
10
- from hestia_earth.models.utils.lookup import fallback_country
17
+ from hestia_earth.models.utils.lookup import fallback_country, _node_value
11
18
  from . import MODEL
12
19
  from .utils import get_coefficient_factor
20
+ from ..utils.impact_assessment import get_country_id
13
21
 
14
22
  REQUIREMENTS = {
15
23
  "ImpactAssessment": {
16
- "cycle": {
17
- "@type": "Cycle",
18
- "siteDuration": "> 0",
19
- "siteArea": "> 0",
20
- "site": {
21
- "siteType": "",
22
- "@type": "Site",
23
- "management": [{"@type": "Management", "value": "", "term.termType": "landCover"}],
24
- "country": {"@type": "Term", "termType": "region"}
25
- },
26
- "optional": {
27
- "otherSitesDuration": "> 0",
28
- "otherSitesArea": "> 0",
29
- "otherSites": [{
30
- "@type": "Site",
31
- "siteType": "",
32
- "management": [{"@type": "Management", "value": "", "term.termType": "landCover"}],
33
- "country": {"@type": "Term", "termType": "region"}
34
- }]
35
- }
36
- }
24
+ "optional": {"country": {"@type": "Term", "termType": "region"}},
25
+ "emissionsResourceUse": [{
26
+ "@type": "Indicator",
27
+ "value": ">0",
28
+ "term.@id": ["landOccupationInputsProduction", "landOccupationDuringCycle"],
29
+ "term.units": "m2*year",
30
+ "landCover": {"@type": "Term", "term.termType": "landCover"}
31
+ }]
37
32
  }
38
33
  }
34
+
39
35
  LOOKUPS = {
40
36
  "@doc": "Performs lookup on landCover.csv for column headers and region-pefTermGrouping-landOccupation.csv for CFs",
41
37
  "region-pefTermGrouping-landOccupation": "",
@@ -50,6 +46,8 @@ RETURNS = {
50
46
  TERM_ID = 'soilQualityIndexLandOccupation'
51
47
  LOOKUP = f"{list(LOOKUPS.keys())[1]}.csv"
52
48
 
49
+ authorised_indicators = ["landOccupationInputsProduction", "landOccupationDuringCycle"]
50
+
53
51
 
54
52
  def _indicator(value: float):
55
53
  indicator = _new_indicator(TERM_ID, MODEL)
@@ -57,73 +55,66 @@ def _indicator(value: float):
57
55
  return indicator
58
56
 
59
57
 
60
- def _run(sites: list):
61
- values = [site['coeff'] * hectar_to_square_meter(site['area']) * days_to_years(site['duration']) for site in sites]
62
- return _indicator(list_sum(values)) if values else None
63
-
64
-
65
- def _should_run(impact_assessment: dict):
66
- cycle = impact_assessment.get('cycle', {})
67
- end_date = cycle.get('endDate')
68
-
69
- has_site = bool(cycle.get('site', False))
70
- has_other_sites = bool(cycle.get('otherSites', []))
58
+ def _run(land_occupation_indicators: list):
59
+ values = [indicator['coefficient'] * indicator['area-by-year'] for indicator in land_occupation_indicators]
60
+ return _indicator(list_sum(values))
71
61
 
72
- all_sites = non_empty_list([cycle.get('site')] + cycle.get('otherSites', []))
73
- site_areas = [cycle.get('siteArea')] + cycle.get('otherSitesArea', [])
74
- site_durations = [cycle.get('siteDuration')] + cycle.get('otherSitesDuration', [])
75
62
 
76
- sites = [
77
- {
78
- 'site-type': site.get('siteType'),
79
- 'country-id': site.get('country', {}).get('@id'),
80
- 'area': site_areas[index],
81
- 'duration': site_durations[index],
82
- 'landCover-id': (most_relevant_blank_node_by_type(
83
- site.get("management", []),
84
- term_type=TermTermType.LANDCOVER.value,
85
- date=end_date
86
- ) or {}).get('term', {}).get('@id'),
87
- }
88
- for index, site in enumerate(all_sites)
63
+ def _should_run(impact_assessment: dict) -> Tuple[bool, list]:
64
+ land_occupation_indicators = [
65
+ i for i in filter_list_term_type(impact_assessment.get('emissionsResourceUse', []), TermTermType.RESOURCEUSE) if
66
+ i.get('landCover', {}).get('termType') == TermTermType.LANDCOVER.value and
67
+ i.get('term', {}).get('@id', '') in authorised_indicators
89
68
  ]
90
69
 
91
- sites = [
92
- site for site in sites
93
- if all([
94
- (site.get('area') or 0) > 0,
95
- (site.get('duration') or 0) > 0,
96
- site.get('landCover-id')
97
- ])
98
- ]
99
-
100
- sites = [
101
- site |
102
- {
103
- 'coeff': get_coefficient_factor(
104
- LOOKUP,
105
- fallback_country(site.get('country-id'), [download_lookup(LOOKUP)]),
106
- get_pef_grouping(site.get('landCover-id')),
70
+ found_land_occupation_indicators = [{
71
+ 'area-by-year': _node_value(land_occupation_indicator),
72
+ 'area-unit': land_occupation_indicator.get('term', {}).get("units"),
73
+ 'land-cover-id': land_occupation_indicator.get('landCover', {}).get("@id"),
74
+ 'country-id': get_country_id(impact_assessment),
75
+ 'area-by-year-is-valid': _node_value(land_occupation_indicator) is not None and _node_value(
76
+ land_occupation_indicator) > 0,
77
+ 'area-unit-is-valid': land_occupation_indicator.get('term', {}).get("units") == "m2*year",
78
+ 'used-country': fallback_country(get_country_id(impact_assessment), [download_lookup(LOOKUP)]),
79
+
80
+ } for land_occupation_indicator in land_occupation_indicators]
81
+
82
+ found_indicators_with_coefficient = [
83
+ indicator | {
84
+ 'coefficient': get_coefficient_factor(
85
+ LOOKUP, indicator['used-country'],
86
+ get_pef_grouping(indicator['land-cover-id']),
107
87
  term_id=TERM_ID
108
- )
109
- } for site in sites
88
+ ),
89
+ "using-fallback-country-region-world-CFs": indicator['used-country'] != indicator['country-id']
90
+ } for indicator in found_land_occupation_indicators
110
91
  ]
111
- valid_sites = [site for site in sites if site.get('coeff') is not None]
112
92
 
113
- has_valid_sites = bool(valid_sites)
93
+ has_valid_land_occupations = all(
94
+ [
95
+ indicator['area-by-year-is-valid'] and indicator['area-unit-is-valid']
96
+ for indicator in found_land_occupation_indicators
97
+ ]) if found_land_occupation_indicators else False
98
+
99
+ valid_indicator_with_coef = [indicator for indicator in found_indicators_with_coefficient if
100
+ indicator['coefficient'] is not None and
101
+ indicator['area-by-year-is-valid'] and
102
+ indicator['area-unit-is-valid']]
114
103
 
115
- logRequirements(cycle, model=MODEL, term=TERM_ID,
116
- has_valid_sites=has_valid_sites,
117
- has_site=has_site,
118
- has_other_sites=has_other_sites,
119
- valid_sites=log_as_table(valid_sites),
104
+ has_land_occupation_indicators = bool(land_occupation_indicators)
105
+
106
+ logRequirements(impact_assessment, model=MODEL, term=TERM_ID,
107
+ has_land_occupation_indicators=has_land_occupation_indicators,
108
+ has_valid_land_occupations=has_valid_land_occupations,
109
+ land_occupation_indicators=log_as_table(found_indicators_with_coefficient)
120
110
  )
121
111
 
122
- should_run = all([has_valid_sites])
123
- logShouldRun(cycle, MODEL, TERM_ID, should_run)
124
- return should_run, valid_sites
112
+ should_run = has_land_occupation_indicators and has_valid_land_occupations
113
+
114
+ logShouldRun(impact_assessment, MODEL, TERM_ID, should_run)
115
+ return should_run, valid_indicator_with_coef
125
116
 
126
117
 
127
118
  def run(impact_assessment: dict):
128
- should_run, sites = _should_run(impact_assessment)
129
- return _run(sites) if should_run else None
119
+ should_run, land_occupation_indicators = _should_run(impact_assessment)
120
+ return _run(land_occupation_indicators) if should_run else None
@@ -0,0 +1,168 @@
1
+ """
2
+ Characterises [soilQualityIndexLandTransformation](https://hestia.earth/term/soilQualityIndexLandTransformation)
3
+ based on an updated [LANCA model (De Laurentiis et al. 2019)](
4
+ http://publications.jrc.ec.europa.eu/repository/handle/JRC113865) and on the LANCA (Regionalised) Characterisation
5
+ Factors version 2.5 (Horn and Meier, 2018).
6
+ """
7
+ from typing import List, Tuple, Optional
8
+
9
+ from hestia_earth.schema import TermTermType
10
+ from hestia_earth.utils.lookup import download_lookup
11
+ from hestia_earth.utils.model import filter_list_term_type
12
+ from hestia_earth.utils.tools import list_sum
13
+
14
+ from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
15
+ from . import MODEL
16
+ from .utils import get_coefficient_factor
17
+ from ..utils.impact_assessment import get_country_id
18
+ from ..utils.indicator import _new_indicator
19
+ from ..utils.landCover import get_pef_grouping
20
+ from ..utils.lookup import fallback_country, _node_value
21
+ from ..utils.term import download_all_land_cover_terms
22
+
23
+ REQUIREMENTS = {
24
+ "ImpactAssessment": {
25
+ "emissionsResourceUse": [
26
+ {
27
+ "@type": "Indicator",
28
+ "term.units": "m2 / year",
29
+ "term.termType": "resourceUse",
30
+ "term.name": "Land transformation from",
31
+ "value": "> 0",
32
+ "landCover": {"@type": "Term", "term.termType": "landCover"}
33
+ }
34
+ ],
35
+ "optional": {"country": {"@type": "Term", "termType": "region"}}
36
+ }
37
+ }
38
+
39
+ # Note: CFs in `region-pefTermGrouping-landTransformation-from.csv` appear to be the opposite values as those in
40
+ # `region-pefTermGrouping-landTransformation-to.csv` but can be different in some cases.
41
+ LOOKUPS = {
42
+ "region-pefTermGrouping-landTransformation-from": "using country and `pefTermGrouping` from `landCover`",
43
+ "region-pefTermGrouping-landTransformation-to": "using country and `pefTermGrouping` from `landCover`",
44
+ "landCover": "pefTermGrouping"
45
+ }
46
+
47
+ from_lookup_file = f"{list(LOOKUPS.keys())[0]}.csv"
48
+ to_lookup_file = f"{list(LOOKUPS.keys())[1]}.csv"
49
+
50
+ LOOKUP = {
51
+ "from": from_lookup_file,
52
+ "to": to_lookup_file
53
+ }
54
+
55
+ RETURNS = {
56
+ "Indicator": {
57
+ "value": ""
58
+ }
59
+ }
60
+
61
+ TERM_ID = 'soilQualityIndexLandTransformation'
62
+
63
+
64
+ def _indicator(value: float):
65
+ indicator = _new_indicator(TERM_ID, MODEL)
66
+ indicator['value'] = value
67
+ return indicator
68
+
69
+
70
+ def _run(transformations: List[dict]):
71
+ values = [(transformation['factor-from'] + transformation['factor-to']) * transformation['area'] for transformation
72
+ in transformations]
73
+ return _indicator(list_sum(values))
74
+
75
+
76
+ def _extract_land_cover_from_indicator_id(indicator: dict) -> Optional[str]:
77
+ """
78
+ Given a indicator with term type `resourceUse` return the equivalent `landCover` term
79
+ """
80
+ term_in_id = indicator.get('term', {}).get('@id', '') \
81
+ .removeprefix("landTransformationFrom") \
82
+ .removesuffix("20YearAverageInputsProduction") \
83
+ .removesuffix("20YearAverageDuringCycle")
84
+ term_in_id = term_in_id[0].lower() + term_in_id[1:] if term_in_id else None
85
+ return term_in_id
86
+
87
+
88
+ def _is_valid_indicator(indicator: dict, land_cover_term_ids: list[str]) -> bool:
89
+ term_id = _extract_land_cover_from_indicator_id(indicator)
90
+ return term_id in land_cover_term_ids
91
+
92
+
93
+ def _should_run(impact_assessment: dict) -> Tuple[bool, list]:
94
+ resource_uses = filter_list_term_type(impact_assessment.get('emissionsResourceUse', []), TermTermType.RESOURCEUSE)
95
+ land_cover_term_ids = [term.get("@id") for term in download_all_land_cover_terms()] if resource_uses else []
96
+
97
+ land_transformation_indicators = [i for i in resource_uses if _is_valid_indicator(i, land_cover_term_ids)]
98
+
99
+ found_transformations = [
100
+ {
101
+ 'area': _node_value(transformation_indicator) * 20,
102
+ 'area-unit': transformation_indicator.get('term', {}).get("units"),
103
+ 'land-cover-id-from': _extract_land_cover_from_indicator_id(transformation_indicator),
104
+ 'land-cover-id-to': transformation_indicator.get('landCover', {}).get("@id"),
105
+ 'indicator-id': transformation_indicator.get('term', {}).get('@id', ''),
106
+ 'good-land-cover-term': transformation_indicator.get('landCover', {}).get('termType') == 'landCover',
107
+ 'country-id': get_country_id(impact_assessment),
108
+ 'area-is-valid': _node_value(transformation_indicator) is not None and _node_value(
109
+ transformation_indicator) > 0,
110
+ 'area-unit-is-valid': transformation_indicator.get('term', {}).get("units") == "m2 / year",
111
+ 'lookup-country': fallback_country(get_country_id(impact_assessment),
112
+ [download_lookup(from_lookup_file), download_lookup(to_lookup_file)]),
113
+ } for transformation_indicator in land_transformation_indicators
114
+ ]
115
+
116
+ found_transformations_with_coefficient = [
117
+ transformation | {
118
+ "using-fallback-country-region-world-CFs": transformation['lookup-country'] != transformation['country-id'],
119
+ 'factor-from': get_coefficient_factor(
120
+ lookup_name=from_lookup_file,
121
+ country_id=transformation['lookup-country'],
122
+ term_id=TERM_ID,
123
+ occupation_type=get_pef_grouping(transformation['land-cover-id-from'])) if
124
+ transformation['land-cover-id-from'] else None,
125
+ 'factor-to': get_coefficient_factor(
126
+ lookup_name=to_lookup_file,
127
+ country_id=transformation['lookup-country'],
128
+ term_id=TERM_ID,
129
+ occupation_type=get_pef_grouping(transformation['land-cover-id-to'])) if
130
+ transformation['land-cover-id-to'] else None
131
+ } for transformation in found_transformations
132
+ ]
133
+
134
+ valid_transformations_with_coef = [
135
+ t for t in found_transformations_with_coefficient if all([
136
+ t['area-is-valid'],
137
+ t['area-unit-is-valid'],
138
+ t['factor-from'] is not None,
139
+ t['factor-to'] is not None
140
+ ])
141
+ ]
142
+
143
+ has_land_transformation_indicators = bool(land_transformation_indicators)
144
+
145
+ all_transformations_are_valid = all(
146
+ [
147
+ all([t['area-is-valid'], t['area-unit-is-valid'], t['good-land-cover-term']])
148
+ for t in found_transformations_with_coefficient
149
+ ]
150
+ ) if found_transformations_with_coefficient else False
151
+
152
+ logRequirements(impact_assessment, model=MODEL, term=TERM_ID,
153
+ has_land_occupation_indicators=has_land_transformation_indicators,
154
+ all_transformations_are_valid=all_transformations_are_valid,
155
+ has_valid_transformations_with_coef=bool(valid_transformations_with_coef),
156
+ found_transformations=log_as_table(found_transformations_with_coefficient)
157
+ )
158
+
159
+ should_run = has_land_transformation_indicators is False or all([has_land_transformation_indicators,
160
+ all_transformations_are_valid])
161
+
162
+ logShouldRun(impact_assessment, MODEL, TERM_ID, should_run)
163
+ return should_run, valid_transformations_with_coef
164
+
165
+
166
+ def run(impact_assessment: dict):
167
+ should_run, transformations = _should_run(impact_assessment)
168
+ return _run(transformations) if should_run else None
@@ -0,0 +1,77 @@
1
+ """
2
+ Characterises [soilQualityIndexTotalLandUseEffects](https://hestia.earth/term/soilQualityIndexTotalLandUseEffects)
3
+ based on an updated [LANCA model (De Laurentiis et al. 2019)](
4
+ http://publications.jrc.ec.europa.eu/repository/handle/JRC113865) and on the LANCA (Regionalised) Characterisation
5
+ Factors version 2.5 (Horn and Meier, 2018).
6
+ """
7
+ from hestia_earth.utils.model import find_term_match
8
+ from hestia_earth.utils.tools import list_sum
9
+
10
+ from hestia_earth.models.log import logRequirements, logShouldRun
11
+ from . import MODEL
12
+ from ..utils.indicator import _new_indicator
13
+
14
+ REQUIREMENTS = {
15
+ "ImpactAssessment": {
16
+ "emissionsResourceUse": [
17
+ {"@type": "Indicator", "value": "", "term.@id": "soilQualityIndexLandOccupation"},
18
+ {"@type": "Indicator", "value": "", "term.@id": "soilQualityIndexLandTransformation"}
19
+ ]
20
+ }
21
+ }
22
+
23
+ RETURNS = {
24
+ "Indicator": {
25
+ "value": "",
26
+ "methodTier": "tier 1",
27
+ "statsDefinition": "modelled"
28
+ }
29
+ }
30
+ TERM_ID = 'soilQualityIndexTotalLandUseEffects'
31
+
32
+
33
+ def _indicator(value: float):
34
+ indicator = _new_indicator(TERM_ID, MODEL)
35
+ indicator['value'] = value
36
+ return indicator
37
+
38
+
39
+ def _run(indicators: list):
40
+ values = [indicator['value'] for indicator in indicators]
41
+ return _indicator(list_sum(values))
42
+
43
+
44
+ def _should_run(impactassessment: dict) -> tuple[bool, list]:
45
+ land_indicators = [
46
+ i for i in impactassessment.get('emissionsResourceUse', []) if
47
+ i.get('term', {}).get('@id', '') in ['soilQualityIndexLandOccupation', 'soilQualityIndexLandTransformation']
48
+ ]
49
+ has_indicators = bool(land_indicators)
50
+
51
+ land_occupation_indicator = find_term_match(land_indicators, "soilQualityIndexLandOccupation",
52
+ default_val=None)
53
+ has_land_occupation_indicator = bool(land_occupation_indicator)
54
+
55
+ land_transformation_indicator = find_term_match(land_indicators, "soilQualityIndexLandTransformation",
56
+ default_val=None)
57
+ has_land_transformation_indicator = bool(land_transformation_indicator)
58
+
59
+ has_valid_values = all([isinstance(indicator.get('value', None), (int, float)) for indicator in land_indicators])
60
+
61
+ logRequirements(impactassessment, model=MODEL, term=TERM_ID,
62
+ has_indicators=has_indicators,
63
+ has_land_occupation_indicator=has_land_occupation_indicator,
64
+ has_land_transformation_indicator=has_land_transformation_indicator,
65
+ has_valid_values=has_valid_values
66
+ )
67
+
68
+ should_run = all([has_indicators, has_valid_values,
69
+ has_land_occupation_indicator, has_land_transformation_indicator])
70
+
71
+ logShouldRun(impactassessment, MODEL, TERM_ID, should_run)
72
+ return should_run, land_indicators
73
+
74
+
75
+ def run(impactassessment: dict):
76
+ should_run, indicators = _should_run(impactassessment)
77
+ return _run(indicators) if should_run else None
@@ -12,6 +12,6 @@ def get_coefficient_factor(lookup_name: str, country_id: str, occupation_type: O
12
12
  """
13
13
  coefficient = get_table_value(download_lookup(lookup_name), 'termid', country_id, column_name(occupation_type))
14
14
  debugMissingLookup(
15
- lookup_name, 'termid', country_id, column_name(occupation_type), coefficient, model=MODEL, term=term_id
15
+ lookup_name, 'termid', country_id, occupation_type, coefficient, model=MODEL, term=term_id
16
16
  )
17
17
  return coefficient
@@ -20,7 +20,8 @@ LOOKUPS = {
20
20
  }
21
21
  RETURNS = {
22
22
  "Indicator": [{
23
- "value": ""
23
+ "value": "",
24
+ "landCover": ""
24
25
  }]
25
26
  }
26
27
  TERM_ID = 'landTransformationFromTemporaryCropland100YearAverageDuringCycle,landTransformationFromPermanentCropland100YearAverageDuringCycle' # noqa: E501
@@ -30,7 +31,7 @@ PERMANENT_TERM_ID = 'landTransformationFromPermanentCropland100YearAverageDuring
30
31
 
31
32
 
32
33
  def _indicator(term_id: str, value: float):
33
- indicator = _new_indicator(term_id, MODEL)
34
+ indicator = _new_indicator(term_id, MODEL, 'cropland')
34
35
  indicator['value'] = value
35
36
  return indicator
36
37
 
@@ -20,7 +20,8 @@ LOOKUPS = {
20
20
  }
21
21
  RETURNS = {
22
22
  "Indicator": [{
23
- "value": ""
23
+ "value": "",
24
+ "landCover": ""
24
25
  }]
25
26
  }
26
27
  TERM_ID = 'landTransformationFromTemporaryCropland20YearAverageDuringCycle,landTransformationFromPermanentCropland20YearAverageDuringCycle' # noqa: E501
@@ -30,7 +31,7 @@ PERMANENT_TERM_ID = 'landTransformationFromPermanentCropland20YearAverageDuringC
30
31
 
31
32
 
32
33
  def _indicator(term_id: str, value: float):
33
- indicator = _new_indicator(term_id, MODEL)
34
+ indicator = _new_indicator(term_id, MODEL, 'cropland')
34
35
  indicator['value'] = value
35
36
  return indicator
36
37