hestia-earth-models 0.62.4__py3-none-any.whl → 0.62.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.

Files changed (55) hide show
  1. hestia_earth/models/cycle/croppingIntensity.py +57 -0
  2. hestia_earth/models/cycle/longFallowRatio.py +52 -0
  3. hestia_earth/models/cycle/siteDuration.py +23 -3
  4. hestia_earth/models/cycle/siteUnusedDuration.py +64 -0
  5. hestia_earth/models/emepEea2019/co2ToAirFuelCombustion.py +2 -10
  6. hestia_earth/models/emepEea2019/n2OToAirFuelCombustionDirect.py +2 -10
  7. hestia_earth/models/emepEea2019/nh3ToAirExcreta.py +2 -9
  8. hestia_earth/models/emepEea2019/nh3ToAirInorganicFertiliser.py +2 -9
  9. hestia_earth/models/emepEea2019/noxToAirFuelCombustion.py +2 -10
  10. hestia_earth/models/emepEea2019/pm10ToAirAnimalHousing.py +57 -0
  11. hestia_earth/models/emepEea2019/pm25ToAirAnimalHousing.py +57 -0
  12. hestia_earth/models/emepEea2019/so2ToAirFuelCombustion.py +2 -10
  13. hestia_earth/models/emepEea2019/tspToAirAnimalHousing.py +57 -0
  14. hestia_earth/models/emepEea2019/utils.py +60 -1
  15. hestia_earth/models/geospatialDatabase/croppingIntensity.py +32 -22
  16. hestia_earth/models/geospatialDatabase/longFallowRatio.py +34 -23
  17. hestia_earth/models/ipcc2019/animal/pastureGrass.py +2 -2
  18. hestia_earth/models/ipcc2019/pastureGrass.py +2 -2
  19. hestia_earth/models/ipcc2019/pastureGrass_utils.py +1 -10
  20. hestia_earth/models/mocking/mock_search.py +14 -8
  21. hestia_earth/models/mocking/search-results.json +27 -27
  22. hestia_earth/models/pooreNemecek2018/longFallowPeriod.py +1 -2
  23. hestia_earth/models/pooreNemecek2018/nurseryDensity.py +1 -2
  24. hestia_earth/models/pooreNemecek2018/nurseryDuration.py +1 -2
  25. hestia_earth/models/pooreNemecek2018/plantationDensity.py +1 -2
  26. hestia_earth/models/pooreNemecek2018/plantationLifespan.py +1 -2
  27. hestia_earth/models/pooreNemecek2018/plantationProductiveLifespan.py +1 -2
  28. hestia_earth/models/preload_requests.py +14 -7
  29. hestia_earth/models/site/management.py +7 -5
  30. hestia_earth/models/utils/__init__.py +0 -9
  31. hestia_earth/models/utils/cycle.py +20 -3
  32. hestia_earth/models/utils/product.py +2 -2
  33. hestia_earth/models/version.py +1 -1
  34. {hestia_earth_models-0.62.4.dist-info → hestia_earth_models-0.62.6.dist-info}/METADATA +2 -2
  35. {hestia_earth_models-0.62.4.dist-info → hestia_earth_models-0.62.6.dist-info}/RECORD +55 -43
  36. tests/models/cycle/test_croppingIntensity.py +53 -0
  37. tests/models/cycle/test_longFallowRatio.py +49 -0
  38. tests/models/cycle/test_siteDuration.py +41 -14
  39. tests/models/cycle/test_siteUnusedDuration.py +55 -0
  40. tests/models/emepEea2019/test_co2ToAirFuelCombustion.py +3 -2
  41. tests/models/emepEea2019/test_n2OToAirFuelCombustionDirect.py +3 -2
  42. tests/models/emepEea2019/test_nh3ToAirExcreta.py +2 -1
  43. tests/models/emepEea2019/test_nh3ToAirInorganicFertiliser.py +4 -3
  44. tests/models/emepEea2019/test_noxToAirFuelCombustion.py +3 -2
  45. tests/models/emepEea2019/test_pm10ToAirAnimalHousing.py +21 -0
  46. tests/models/emepEea2019/test_pm25ToAirAnimalHousing.py +21 -0
  47. tests/models/emepEea2019/test_so2ToAirFuelCombustion.py +3 -2
  48. tests/models/emepEea2019/test_tspToAirAnimalHousing.py +21 -0
  49. tests/models/emepEea2019/test_utils.py +198 -1
  50. tests/models/geospatialDatabase/test_croppingIntensity.py +5 -3
  51. tests/models/geospatialDatabase/test_longFallowRatio.py +5 -3
  52. tests/models/site/test_management.py +1 -0
  53. {hestia_earth_models-0.62.4.dist-info → hestia_earth_models-0.62.6.dist-info}/LICENSE +0 -0
  54. {hestia_earth_models-0.62.4.dist-info → hestia_earth_models-0.62.6.dist-info}/WHEEL +0 -0
  55. {hestia_earth_models-0.62.4.dist-info → hestia_earth_models-0.62.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,57 @@
1
+ from hestia_earth.schema import CycleStartDateDefinition
2
+ from hestia_earth.utils.model import find_primary_product
3
+
4
+ from hestia_earth.models.log import logRequirements, logShouldRun
5
+ from hestia_earth.models.utils.practice import _new_practice
6
+ from hestia_earth.models.utils.crop import is_plantation
7
+ from . import MODEL
8
+
9
+ REQUIREMENTS = {
10
+ "Cycle": {
11
+ "cycleDuration": "> 0",
12
+ "startDateDefinition": "harvest of previous crop"
13
+ }
14
+ }
15
+ RETURNS = {
16
+ "Practice": [{
17
+ "value": ""
18
+ }]
19
+ }
20
+ LOOKUPS = {
21
+ "crop": "isPlantation"
22
+ }
23
+ TERM_ID = 'croppingIntensity'
24
+
25
+
26
+ def _practice(value: float):
27
+ practice = _new_practice(TERM_ID)
28
+ practice['value'] = [round(value, 7)]
29
+ return practice
30
+
31
+
32
+ def _run(cycle: dict):
33
+ cycleDuration = cycle.get('cycleDuration')
34
+ return [_practice(cycleDuration / 365)]
35
+
36
+
37
+ def _should_run(cycle: dict):
38
+ product = find_primary_product(cycle) or {}
39
+ not_plantation = not is_plantation(MODEL, TERM_ID, product.get('term', {}).get('@id'))
40
+
41
+ cycleDuration = cycle.get('cycleDuration', 0)
42
+ has_cycleDuration = cycleDuration > 0
43
+ startDateDefinition = cycle.get('startDateDefinition')
44
+ harvest_previous_crop = CycleStartDateDefinition.HARVEST_OF_PREVIOUS_CROP.value
45
+ is_harvest_previous_crop = startDateDefinition == harvest_previous_crop
46
+
47
+ logRequirements(cycle, model=MODEL, term=TERM_ID,
48
+ not_plantation=not_plantation,
49
+ cycleDuration=cycleDuration,
50
+ is_harvest_previous_crop=is_harvest_previous_crop)
51
+
52
+ should_run = all([not_plantation, has_cycleDuration, is_harvest_previous_crop])
53
+ logShouldRun(cycle, MODEL, TERM_ID, should_run)
54
+ return should_run
55
+
56
+
57
+ def run(cycle: dict): return _run(cycle) if _should_run(cycle) else []
@@ -0,0 +1,52 @@
1
+ from hestia_earth.utils.model import find_term_match
2
+ from hestia_earth.utils.tools import list_sum
3
+
4
+ from hestia_earth.models.log import logRequirements, logShouldRun
5
+ from hestia_earth.models.utils.practice import _new_practice
6
+ from . import MODEL
7
+
8
+ REQUIREMENTS = {
9
+ "Cycle": {
10
+ "practices": [
11
+ {"@type": "Practice", "value": "> 0", "term.@id": "longFallowPeriod"},
12
+ {"@type": "Practice", "value": "> 0", "term.@id": "rotationDuration"}
13
+ ]
14
+ }
15
+ }
16
+ RETURNS = {
17
+ "Practice": [{
18
+ "value": ""
19
+ }]
20
+ }
21
+ TERM_ID = 'longFallowRatio'
22
+
23
+
24
+ def _practice(value: float):
25
+ practice = _new_practice(TERM_ID)
26
+ practice['value'] = [round(value, 7)]
27
+ return practice
28
+
29
+
30
+ def _run(longFallowPeriod: float, rotationDuration: float):
31
+ value = rotationDuration / (rotationDuration - longFallowPeriod)
32
+ return [_practice(value)]
33
+
34
+
35
+ def _should_run(cycle: dict):
36
+ practices = cycle.get('practices', [])
37
+
38
+ longFallowPeriod = list_sum(find_term_match(practices, 'longFallowPeriod', {}).get('value', 0), 0)
39
+ rotationDuration = list_sum(find_term_match(practices, 'rotationDuration', {}).get('value', 0), 0)
40
+
41
+ logRequirements(cycle, model=MODEL, term=TERM_ID,
42
+ longFallowPeriod=longFallowPeriod,
43
+ rotationDuration=rotationDuration)
44
+
45
+ should_run = all([longFallowPeriod > 0, rotationDuration > 0])
46
+ logShouldRun(cycle, MODEL, TERM_ID, should_run)
47
+ return should_run, longFallowPeriod, rotationDuration
48
+
49
+
50
+ def run(cycle: dict):
51
+ should_run, longFallowPeriod, rotationDuration = _should_run(cycle)
52
+ return _run(longFallowPeriod, rotationDuration) if should_run else []
@@ -3,7 +3,12 @@ Site duration
3
3
 
4
4
  This model calculates the `siteDuration` on the `Cycle` to the same value as `cycleDuration`
5
5
  when only a single `Site` is present.
6
+
7
+ Note: on `crop` production cycles, the model will only run if `startDateDefinition` = `harvest of previous crop`.
6
8
  """
9
+ from hestia_earth.schema import TermTermType, CycleStartDateDefinition
10
+ from hestia_earth.utils.model import find_primary_product
11
+
7
12
  from hestia_earth.models.log import logRequirements, logShouldRun
8
13
  from . import MODEL
9
14
 
@@ -12,11 +17,19 @@ REQUIREMENTS = {
12
17
  "cycleDuration": "> 0",
13
18
  "none": {
14
19
  "otherSites": ""
20
+ },
21
+ "optional": {
22
+ "products": {
23
+ "@type": "Product",
24
+ "primary": "True",
25
+ "term.termType": "crop"
26
+ },
27
+ "startDateDefinition": ""
15
28
  }
16
29
  }
17
30
  }
18
31
  RETURNS = {
19
- "a `number` or `None` if the `Cycle` has multiple `Sites`": ""
32
+ "the duration as a `number`": ""
20
33
  }
21
34
  MODEL_KEY = 'siteDuration'
22
35
 
@@ -28,11 +41,18 @@ def _should_run(cycle: dict):
28
41
  cycleDuration = cycle.get('cycleDuration', 0)
29
42
  has_other_sites = len(cycle.get('otherSites', [])) == 0
30
43
 
44
+ product = find_primary_product(cycle)
45
+ is_primary_crop_product = (product or {}).get('term', {}).get('termType') == TermTermType.CROP.value
46
+ harvest_previous_crop = CycleStartDateDefinition.HARVEST_OF_PREVIOUS_CROP.value
47
+ is_harvest_previous_crop = cycle.get('startDateDefinition') == harvest_previous_crop
48
+
31
49
  logRequirements(cycle, model=MODEL, key=MODEL_KEY,
32
50
  cycleDuration=cycleDuration,
33
- has_other_sites=has_other_sites)
51
+ has_other_sites=has_other_sites,
52
+ is_primary_crop_product=is_primary_crop_product,
53
+ is_harvest_previous_crop=is_harvest_previous_crop)
34
54
 
35
- should_run = all([cycleDuration > 0, has_other_sites])
55
+ should_run = all([cycleDuration > 0, has_other_sites, not is_primary_crop_product or is_harvest_previous_crop])
36
56
  logShouldRun(cycle, MODEL, None, should_run, key=MODEL_KEY)
37
57
  return should_run
38
58
 
@@ -0,0 +1,64 @@
1
+ """
2
+ Site Unused Duration
3
+
4
+ This model sets the [Cycle siteUnusedDuration](https://hestia.earth/schema/Cycle#siteUnusedDuration) based on
5
+ the `siteDuration` and the `longFallowRatio` practice.
6
+ """
7
+ from hestia_earth.schema import SiteSiteType
8
+ from hestia_earth.utils.tools import list_sum
9
+
10
+ from hestia_earth.models.log import logRequirements, logShouldRun
11
+ from hestia_earth.models.utils.site import valid_site_type
12
+ from . import MODEL
13
+
14
+ REQUIREMENTS = {
15
+ "Cycle": {
16
+ "site": {
17
+ "@type": "Site",
18
+ "siteType": ["cropland", "glass and high accessible cover"]
19
+ },
20
+ "siteDuration": "> 0",
21
+ "practices": [{"@type": "Practice", "value": "> 0", "term.@id": "longFallowRatio"}]
22
+ }
23
+ }
24
+ RETURNS = {
25
+ "The siteUnusedDuration as a number": ""
26
+ }
27
+ MODEL_KEY = 'siteUnusedDuration'
28
+ VALID_SITE_TYPES = [
29
+ SiteSiteType.CROPLAND.value,
30
+ SiteSiteType.GLASS_OR_HIGH_ACCESSIBLE_COVER.value
31
+ ]
32
+
33
+
34
+ def _run(cycle: dict, longFallowRatio: float):
35
+ siteDuration = cycle.get('siteDuration')
36
+ return siteDuration * (longFallowRatio - 1)
37
+
38
+
39
+ def _should_run(cycle: dict):
40
+ site_type_valid = valid_site_type(cycle.get('site'), site_types=VALID_SITE_TYPES)
41
+
42
+ siteDuration = cycle.get('siteDuration', 0)
43
+
44
+ practices = cycle.get('practices', [])
45
+ longFallowRatio = list_sum(next((
46
+ p for p in practices if all([
47
+ p.get('term', {}).get('@id') == 'longFallowRatio',
48
+ p.get('site') is None or p.get('site', {}).get('@id') == cycle.get('site', {}).get('@id')
49
+ ])
50
+ ), {}).get('value'), None)
51
+
52
+ logRequirements(cycle, model=MODEL, key=MODEL_KEY,
53
+ site_type_valid=site_type_valid,
54
+ siteDuration=siteDuration,
55
+ longFallowRatio=longFallowRatio)
56
+
57
+ should_run = all([site_type_valid, siteDuration > 0, longFallowRatio is not None])
58
+ logShouldRun(cycle, MODEL, None, should_run, key=MODEL_KEY)
59
+ return should_run, longFallowRatio
60
+
61
+
62
+ def run(cycle: dict):
63
+ should_run, longFallowRatio = _should_run(cycle)
64
+ return _run(cycle, longFallowRatio) if should_run else None
@@ -2,8 +2,7 @@ from hestia_earth.schema import EmissionMethodTier
2
2
  from hestia_earth.utils.tools import list_sum
3
3
 
4
4
  from hestia_earth.models.log import logRequirements, logShouldRun
5
- from hestia_earth.models.utils.emission import _new_emission
6
- from .utils import get_fuel_values
5
+ from .utils import get_fuel_values, _emission
7
6
  from . import MODEL
8
7
 
9
8
  REQUIREMENTS = {
@@ -32,16 +31,9 @@ TERM_ID = 'co2ToAirFuelCombustion'
32
31
  TIER = EmissionMethodTier.TIER_1.value
33
32
 
34
33
 
35
- def _emission(value: float):
36
- emission = _new_emission(TERM_ID, MODEL)
37
- emission['value'] = [value]
38
- emission['methodTier'] = TIER
39
- return emission
40
-
41
-
42
34
  def _run(fuel_values: list):
43
35
  value = list_sum(fuel_values)
44
- return [_emission(value)]
36
+ return [_emission(value=value, tier=TIER, term_id=TERM_ID)]
45
37
 
46
38
 
47
39
  def _should_run(cycle: dict):
@@ -2,8 +2,7 @@ from hestia_earth.schema import EmissionMethodTier
2
2
  from hestia_earth.utils.tools import list_sum
3
3
 
4
4
  from hestia_earth.models.log import logRequirements, logShouldRun
5
- from hestia_earth.models.utils.emission import _new_emission
6
- from .utils import get_fuel_values
5
+ from .utils import get_fuel_values, _emission
7
6
  from . import MODEL
8
7
 
9
8
  REQUIREMENTS = {
@@ -32,16 +31,9 @@ TERM_ID = 'n2OToAirFuelCombustionDirect'
32
31
  TIER = EmissionMethodTier.TIER_1.value
33
32
 
34
33
 
35
- def _emission(value: float):
36
- emission = _new_emission(TERM_ID, MODEL)
37
- emission['value'] = [value]
38
- emission['methodTier'] = TIER
39
- return emission
40
-
41
-
42
34
  def _run(fuel_values: list):
43
35
  value = list_sum(fuel_values)
44
- return [_emission(value)]
36
+ return [_emission(value=value, tier=TIER, term_id=TERM_ID)]
45
37
 
46
38
 
47
39
  def _should_run(cycle: dict):
@@ -7,9 +7,9 @@ from hestia_earth.models.log import logRequirements, debugMissingLookup, logShou
7
7
  from hestia_earth.models.utils import _filter_list_term_unit
8
8
  from hestia_earth.models.utils.constant import Units, get_atomic_conversion
9
9
  from hestia_earth.models.utils.completeness import _is_term_type_complete
10
- from hestia_earth.models.utils.emission import _new_emission
11
10
  from hestia_earth.models.utils.input import total_excreta_tan
12
11
  from . import MODEL
12
+ from .utils import _emission
13
13
 
14
14
  REQUIREMENTS = {
15
15
  "Cycle": {
@@ -40,13 +40,6 @@ TERM_ID = 'nh3ToAirExcreta'
40
40
  TIER = EmissionMethodTier.TIER_2.value
41
41
 
42
42
 
43
- def _emission(value: float):
44
- emission = _new_emission(TERM_ID, MODEL)
45
- emission['value'] = [value]
46
- emission['methodTier'] = TIER
47
- return emission
48
-
49
-
50
43
  def _get_nh3_factor(term_id: str, input: dict):
51
44
  input_term_id = input.get('term', {}).get('@id')
52
45
  lookup_name = f"{list(LOOKUPS.keys())[0]}.csv"
@@ -57,7 +50,7 @@ def _get_nh3_factor(term_id: str, input: dict):
57
50
 
58
51
  def _run(excreta_EF_product: float):
59
52
  value = excreta_EF_product * get_atomic_conversion(Units.KG_NH3, Units.TO_N)
60
- return [_emission(value)]
53
+ return [_emission(value=value, tier=TIER, term_id=TERM_ID)]
61
54
 
62
55
 
63
56
  def _should_run(cycle: dict):
@@ -10,7 +10,7 @@ from hestia_earth.models.utils.inorganicFertiliser import (
10
10
  get_NH3_emission_factor, get_terms, get_term_lookup, get_country_breakdown, get_cycle_inputs
11
11
  )
12
12
  from hestia_earth.models.utils.constant import Units
13
- from hestia_earth.models.utils.emission import _new_emission
13
+ from .utils import _emission
14
14
  from hestia_earth.models.utils.measurement import most_relevant_measurement_value
15
15
  from . import MODEL
16
16
 
@@ -58,13 +58,6 @@ TIER = EmissionMethodTier.TIER_2.value
58
58
  UNSPECIFIED_TERM_ID = 'inorganicNitrogenFertiliserUnspecifiedKgN'
59
59
 
60
60
 
61
- def _emission(value: float):
62
- emission = _new_emission(TERM_ID, MODEL)
63
- emission['value'] = [value]
64
- emission['methodTier'] = TIER
65
- return emission
66
-
67
-
68
61
  def _input_with_factor(soilPh: float, temperature: float):
69
62
  def get_value(input: dict):
70
63
  term_id = input.get('term', {}).get('@id')
@@ -151,4 +144,4 @@ def _should_run(cycle: dict):
151
144
  def run(cycle: dict):
152
145
  should_run, N_inputs_with_factor = _should_run(cycle)
153
146
  value = list_sum([i.get('value') * i.get('factor') for i in N_inputs_with_factor]) if should_run else None
154
- return [_emission(value)] if value is not None else []
147
+ return [_emission(value=value, tier=TIER, term_id=TERM_ID)] if value is not None else []
@@ -2,8 +2,7 @@ from hestia_earth.schema import EmissionMethodTier
2
2
  from hestia_earth.utils.tools import list_sum
3
3
 
4
4
  from hestia_earth.models.log import logRequirements, logShouldRun
5
- from hestia_earth.models.utils.emission import _new_emission
6
- from .utils import get_fuel_values
5
+ from .utils import get_fuel_values, _emission
7
6
  from . import MODEL
8
7
 
9
8
  REQUIREMENTS = {
@@ -32,16 +31,9 @@ TERM_ID = 'noxToAirFuelCombustion'
32
31
  TIER = EmissionMethodTier.TIER_1.value
33
32
 
34
33
 
35
- def _emission(value: float):
36
- emission = _new_emission(TERM_ID, MODEL)
37
- emission['value'] = [value]
38
- emission['methodTier'] = TIER
39
- return emission
40
-
41
-
42
34
  def _run(fuel_values: list):
43
35
  value = list_sum(fuel_values)
44
- return [_emission(value)]
36
+ return [_emission(value=value, tier=TIER, term_id=TERM_ID)]
45
37
 
46
38
 
47
39
  def _should_run(cycle: dict):
@@ -0,0 +1,57 @@
1
+ """
2
+ When `otherSites` are provided, `otherSitesDuration` must be set as well so we know the length of time the animals
3
+ spend on each Site.
4
+ """
5
+ from hestia_earth.schema import EmissionMethodTier
6
+
7
+ from .utils import _emission, get_live_animal_emission_value, should_run_animal
8
+ from . import MODEL
9
+
10
+ REQUIREMENTS = {
11
+ "Cycle": {
12
+ "completeness.animalPopulation": "True",
13
+ "site": {"@type": "Site"},
14
+ "siteDuration": "",
15
+ "animals": [
16
+ {
17
+ "@type": "Animal",
18
+ "term.termType": "liveAnimal",
19
+ "value": "",
20
+ "referencePeriod": "average"
21
+ }
22
+ ],
23
+ "optional": {
24
+ "otherSites": [{"@type": "Site"}],
25
+ "otherSitesDuration": ""
26
+ }
27
+ }
28
+ }
29
+ RETURNS = {
30
+ "Emission": [{
31
+ "value": "",
32
+ "methodTier": "tier 1"
33
+ }]
34
+ }
35
+ LOOKUPS = {
36
+ "liveAnimal": "pm10ToAirEea2019",
37
+ "operation": "pm10ToAirAnimalHousingEmepEea2019"
38
+ }
39
+ TERM_ID = 'pm10ToAirAnimalHousing'
40
+ TIER = EmissionMethodTier.TIER_1.value
41
+
42
+
43
+ def _run(animals: list[dict], total_duration: float):
44
+ return [
45
+ _emission(
46
+ value=get_live_animal_emission_value(animals, total_duration, lookup_col=LOOKUPS["liveAnimal"]),
47
+ tier=TIER,
48
+ term_id=TERM_ID
49
+ )
50
+ ]
51
+
52
+
53
+ def run(cycle: dict):
54
+ should_run, animals, total_duration = should_run_animal(
55
+ cycle=cycle, model=MODEL, term=TERM_ID, tier=TIER
56
+ )
57
+ return _run(animals, total_duration) if should_run else []
@@ -0,0 +1,57 @@
1
+ """
2
+ When `otherSites` are provided, `otherSitesDuration` must be set as well so we know the length of time the animals
3
+ spend on each Site.
4
+ """
5
+ from hestia_earth.schema import EmissionMethodTier
6
+
7
+ from .utils import _emission, get_live_animal_emission_value, should_run_animal
8
+ from . import MODEL
9
+
10
+ REQUIREMENTS = {
11
+ "Cycle": {
12
+ "completeness.animalPopulation": "True",
13
+ "site": {"@type": "Site"},
14
+ "siteDuration": "",
15
+ "animals": [
16
+ {
17
+ "@type": "Animal",
18
+ "term.termType": "liveAnimal",
19
+ "value": "",
20
+ "referencePeriod": "average"
21
+ }
22
+ ],
23
+ "optional": {
24
+ "otherSites": [{"@type": "Site"}],
25
+ "otherSitesDuration": ""
26
+ }
27
+ }
28
+ }
29
+ RETURNS = {
30
+ "Emission": [{
31
+ "value": "",
32
+ "methodTier": "tier 1"
33
+ }]
34
+ }
35
+ LOOKUPS = {
36
+ "liveAnimal": "pm25ToAirEea2019",
37
+ "operation": "pm25ToAirAnimalHousingEmepEea2019"
38
+ }
39
+ TERM_ID = 'pm25ToAirAnimalHousing'
40
+ TIER = EmissionMethodTier.TIER_1.value
41
+
42
+
43
+ def _run(animals: list[dict], total_duration: float):
44
+ return [
45
+ _emission(
46
+ value=get_live_animal_emission_value(animals, total_duration, lookup_col=LOOKUPS["liveAnimal"]),
47
+ tier=TIER,
48
+ term_id=TERM_ID
49
+ )
50
+ ]
51
+
52
+
53
+ def run(cycle: dict):
54
+ should_run, animals, total_duration = should_run_animal(
55
+ cycle=cycle, model=MODEL, term=TERM_ID, tier=TIER
56
+ )
57
+ return _run(animals, total_duration) if should_run else []
@@ -2,8 +2,7 @@ from hestia_earth.schema import EmissionMethodTier
2
2
  from hestia_earth.utils.tools import list_sum
3
3
 
4
4
  from hestia_earth.models.log import logRequirements, logShouldRun
5
- from hestia_earth.models.utils.emission import _new_emission
6
- from .utils import get_fuel_values
5
+ from .utils import get_fuel_values, _emission
7
6
  from . import MODEL
8
7
 
9
8
  REQUIREMENTS = {
@@ -32,16 +31,9 @@ TERM_ID = 'so2ToAirFuelCombustion'
32
31
  TIER = EmissionMethodTier.TIER_1.value
33
32
 
34
33
 
35
- def _emission(value: float):
36
- emission = _new_emission(TERM_ID, MODEL)
37
- emission['value'] = [value]
38
- emission['methodTier'] = TIER
39
- return emission
40
-
41
-
42
34
  def _run(fuel_values: list):
43
35
  value = list_sum(fuel_values)
44
- return [_emission(value)]
36
+ return [_emission(value=value, tier=TIER, term_id=TERM_ID)]
45
37
 
46
38
 
47
39
  def _should_run(cycle: dict):
@@ -0,0 +1,57 @@
1
+ """
2
+ When `otherSites` are provided, `otherSitesDuration` must be set as well so we know the length of time the animals
3
+ spend on each Site.
4
+ """
5
+ from hestia_earth.schema import EmissionMethodTier
6
+
7
+ from .utils import _emission, get_live_animal_emission_value, should_run_animal
8
+ from . import MODEL
9
+
10
+ REQUIREMENTS = {
11
+ "Cycle": {
12
+ "completeness.animalPopulation": "True",
13
+ "site": {"@type": "Site"},
14
+ "siteDuration": "",
15
+ "animals": [
16
+ {
17
+ "@type": "Animal",
18
+ "term.termType": "liveAnimal",
19
+ "value": "",
20
+ "referencePeriod": "average"
21
+ }
22
+ ],
23
+ "optional": {
24
+ "otherSites": [{"@type": "Site"}],
25
+ "otherSitesDuration": ""
26
+ }
27
+ }
28
+ }
29
+ RETURNS = {
30
+ "Emission": [{
31
+ "value": "",
32
+ "methodTier": "tier 1"
33
+ }]
34
+ }
35
+ LOOKUPS = {
36
+ "liveAnimal": "tspToAirEea2019",
37
+ "operation": "tspToAirAnimalHousingEmepEea2019"
38
+ }
39
+ TERM_ID = 'tspToAirAnimalHousing'
40
+ TIER = EmissionMethodTier.TIER_1.value
41
+
42
+
43
+ def _run(animals: list[dict], total_duration: float):
44
+ return [
45
+ _emission(
46
+ value=get_live_animal_emission_value(animals, total_duration, lookup_col=LOOKUPS["liveAnimal"]),
47
+ tier=TIER,
48
+ term_id=TERM_ID
49
+ )
50
+ ]
51
+
52
+
53
+ def run(cycle: dict):
54
+ should_run, animals, total_duration = should_run_animal(
55
+ cycle=cycle, model=MODEL, term=TERM_ID, tier=TIER
56
+ )
57
+ return _run(animals, total_duration) if should_run else []
@@ -1,13 +1,23 @@
1
- from hestia_earth.schema import TermTermType
1
+ from hestia_earth.schema import TermTermType, SiteSiteType
2
2
  from hestia_earth.utils.model import filter_list_term_type
3
3
  from hestia_earth.utils.lookup import extract_grouped_data
4
4
  from hestia_earth.utils.tools import list_sum, safe_parse_float, non_empty_list
5
5
 
6
+ from hestia_earth.models.log import logRequirements, logShouldRun
6
7
  from hestia_earth.models.utils.completeness import _is_term_type_complete
7
8
  from hestia_earth.models.utils.term import get_lookup_value
9
+ from hestia_earth.models.utils.cycle import get_animals_by_period
10
+ from hestia_earth.models.utils.emission import _new_emission
8
11
  from . import MODEL
9
12
 
10
13
 
14
+ def _emission(value: float, tier: str, term_id: str):
15
+ emission = _new_emission(term_id, MODEL)
16
+ emission['value'] = [value]
17
+ emission['methodTier'] = tier
18
+ return emission
19
+
20
+
11
21
  def _get_fuel_input_value(term_id: str, lookup_col: str):
12
22
  def get_value(input: dict):
13
23
  input_term = input.get('term', {})
@@ -33,3 +43,52 @@ def get_fuel_values(term_id: str, cycle: dict, lookup_col: str):
33
43
  len(values) == 0,
34
44
  _is_term_type_complete(cycle, 'electricityFuel')
35
45
  ]) else values
46
+
47
+
48
+ def _get_emissions_factor(animal: dict, lookup_col: str) -> float:
49
+ return safe_parse_float(
50
+ get_lookup_value(animal.get("term", {}), lookup_col, model=MODEL, term=animal.get("term", ""))
51
+ )
52
+
53
+
54
+ def _duration_in_housing(cycle: dict) -> int:
55
+ other_sites = cycle.get("otherSites", [])
56
+ other_durations = cycle.get("otherSitesDuration", [])
57
+ return list_sum([
58
+ cycle.get("siteDuration", cycle.get("cycleDuration", 0))
59
+ if cycle.get("site", {}).get("siteType", "") == SiteSiteType.ANIMAL_HOUSING.value else 0
60
+ ] + ([
61
+ other_durations[x]
62
+ for x in range(len(other_sites))
63
+ if other_sites[x].get("siteType", "") == SiteSiteType.ANIMAL_HOUSING.value
64
+ ] if len(other_sites) == len(other_durations) else []))
65
+
66
+
67
+ def get_live_animal_emission_value(animals: list[dict], duration: float, lookup_col: str) -> float:
68
+ return list_sum([
69
+ animal.get('value') * _get_emissions_factor(animal=animal, lookup_col=lookup_col)
70
+ for animal in animals
71
+ ]) * duration / 365
72
+
73
+
74
+ def should_run_animal(cycle: dict, model: str, term: str, tier: str) -> tuple[list, bool]:
75
+ term_type_animalPopulation_complete = _is_term_type_complete(cycle=cycle, term="animalPopulation")
76
+
77
+ # models will be set as not relevant is primary `siteType` does not match, so check only `otherSites`.
78
+
79
+ total_duration = _duration_in_housing(cycle)
80
+
81
+ has_other_sites_and_duration = len(cycle.get("otherSites", [])) == len(cycle.get("otherSitesDuration", []))
82
+
83
+ animals = get_animals_by_period(cycle)
84
+ has_animals = len(animals) > 0
85
+
86
+ logRequirements(cycle, model=model, term=term,
87
+ term_type_animalPopulation_complete=term_type_animalPopulation_complete,
88
+ has_animals=has_animals,
89
+ has_other_sites_and_duration=has_other_sites_and_duration,
90
+ number_of_days_in_animal_housing=total_duration)
91
+
92
+ should_run = all([term_type_animalPopulation_complete, has_animals, has_other_sites_and_duration])
93
+ logShouldRun(cycle, model, term, should_run, methodTier=tier)
94
+ return should_run, animals, total_duration