hestia-earth-models 0.64.11__py3-none-any.whl → 0.64.13__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 (196) hide show
  1. hestia_earth/models/aware/scarcityWeightedWaterUse.py +1 -1
  2. hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandOccupation.py +1 -1
  3. hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandTransformation.py +1 -1
  4. hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsTotalLandUseEffects.py +3 -2
  5. hestia_earth/models/cml2001Baseline/eutrophicationPotentialExcludingFate.py +2 -2
  6. hestia_earth/models/cml2001Baseline/terrestrialAcidificationPotentialIncludingFateAverageEurope.py +2 -2
  7. hestia_earth/models/cml2001NonBaseline/eutrophicationPotentialIncludingFateAverageEurope.py +2 -2
  8. hestia_earth/models/cml2001NonBaseline/terrestrialAcidificationPotentialExcludingFate.py +2 -2
  9. hestia_earth/models/cycle/completeness/seed.py +6 -4
  10. hestia_earth/models/cycle/concentrateFeed.py +36 -19
  11. hestia_earth/models/cycle/endDate.py +10 -1
  12. hestia_earth/models/cycle/milkYield.py +6 -5
  13. hestia_earth/models/cycle/startDate.py +6 -4
  14. hestia_earth/models/edip2003/ozoneDepletionPotential.py +2 -2
  15. hestia_earth/models/emissionNotRelevant/__init__.py +3 -2
  16. hestia_earth/models/environmentalFootprintV3/freshwaterEcotoxicityPotentialCtue.py +2 -3
  17. hestia_earth/models/faostat2018/utils.py +72 -12
  18. hestia_earth/models/hestia/__init__.py +13 -0
  19. hestia_earth/models/hestia/landCover.py +727 -0
  20. hestia_earth/models/ipcc2013ExcludingFeedbacks/gwp100.py +2 -2
  21. hestia_earth/models/ipcc2013IncludingFeedbacks/gwp100.py +2 -2
  22. hestia_earth/models/ipcc2019/aboveGroundBiomass.py +4 -8
  23. hestia_earth/models/ipcc2019/animal/fatContent.py +1 -1
  24. hestia_earth/models/ipcc2019/animal/milkYieldPerAnimal.py +91 -0
  25. hestia_earth/models/ipcc2019/animal/trueProteinContent.py +1 -1
  26. hestia_earth/models/ipcc2019/animal/utils.py +17 -12
  27. hestia_earth/models/ipcc2019/belowGroundBiomass.py +4 -8
  28. hestia_earth/models/ipcc2019/biomass_utils.py +11 -0
  29. hestia_earth/models/ipcc2019/ch4ToAirEntericFermentation.py +1 -1
  30. hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChange.py +8 -4
  31. hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChange.py +7 -3
  32. hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +52 -6
  33. hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChange.py +7 -3
  34. hestia_earth/models/ipcc2019/n2OToAirExcretaDirect.py +14 -9
  35. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +9 -3
  36. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +1 -2
  37. hestia_earth/models/koble2014/aboveGroundCropResidue.py +5 -1
  38. hestia_earth/models/lcImpactAllEffects100Years/damageToFreshwaterEcosystemsClimateChange.py +2 -2
  39. hestia_earth/models/lcImpactAllEffects100Years/damageToFreshwaterEcosystemsFreshwaterEcotoxicity.py +2 -3
  40. hestia_earth/models/lcImpactAllEffects100Years/damageToFreshwaterEcosystemsPdfYear.py +2 -2
  41. hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealth.py +2 -2
  42. hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthClimateChange.py +2 -2
  43. hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthHumanToxicityCancerogenic.py +2 -3
  44. hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthHumanToxicityNonCancerogenic.py +2 -3
  45. hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthStratosphericOzoneDepletion.py +2 -2
  46. hestia_earth/models/lcImpactAllEffects100Years/damageToMarineEcosystemsMarineEcotoxicity.py +2 -3
  47. hestia_earth/models/lcImpactAllEffects100Years/damageToMarineEcosystemsPdfYear.py +2 -2
  48. hestia_earth/models/lcImpactAllEffects100Years/damageToTerrestrialEcosystemsClimateChange.py +2 -2
  49. hestia_earth/models/lcImpactAllEffects100Years/damageToTerrestrialEcosystemsPdfYear.py +2 -2
  50. hestia_earth/models/lcImpactAllEffects100Years/damageToTerrestrialEcosystemsTerrestrialEcotoxicity.py +2 -3
  51. hestia_earth/models/lcImpactAllEffectsInfinite/damageToFreshwaterEcosystemsClimateChange.py +2 -2
  52. hestia_earth/models/lcImpactAllEffectsInfinite/damageToFreshwaterEcosystemsFreshwaterEcotoxicity.py +2 -3
  53. hestia_earth/models/lcImpactAllEffectsInfinite/damageToFreshwaterEcosystemsPdfYear.py +2 -2
  54. hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealth.py +2 -2
  55. hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthClimateChange.py +2 -2
  56. hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthHumanToxicityCancerogenic.py +2 -3
  57. hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthHumanToxicityNonCancerogenic.py +2 -3
  58. hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthStratosphericOzoneDepletion.py +2 -2
  59. hestia_earth/models/lcImpactAllEffectsInfinite/damageToMarineEcosystemsMarineEcotoxicity.py +2 -3
  60. hestia_earth/models/lcImpactAllEffectsInfinite/damageToMarineEcosystemsPdfYear.py +2 -2
  61. hestia_earth/models/lcImpactAllEffectsInfinite/damageToTerrestrialEcosystemsClimateChange.py +2 -2
  62. hestia_earth/models/lcImpactAllEffectsInfinite/damageToTerrestrialEcosystemsPdfYear.py +2 -2
  63. hestia_earth/models/lcImpactAllEffectsInfinite/damageToTerrestrialEcosystemsTerrestrialEcotoxicity.py +2 -3
  64. hestia_earth/models/lcImpactCertainEffects100Years/damageToFreshwaterEcosystemsFreshwaterEcotoxicity.py +2 -3
  65. hestia_earth/models/lcImpactCertainEffects100Years/damageToFreshwaterEcosystemsPdfYear.py +2 -2
  66. hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealth.py +2 -2
  67. hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthClimateChange.py +2 -2
  68. hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthHumanToxicityCancerogenic.py +2 -3
  69. hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthHumanToxicityNonCancerogenic.py +2 -3
  70. hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthStratosphericOzoneDepletion.py +2 -2
  71. hestia_earth/models/lcImpactCertainEffects100Years/damageToMarineEcosystemsMarineEcotoxicity.py +2 -3
  72. hestia_earth/models/lcImpactCertainEffects100Years/damageToMarineEcosystemsPdfYear.py +2 -2
  73. hestia_earth/models/lcImpactCertainEffects100Years/damageToTerrestrialEcosystemsClimateChange.py +2 -2
  74. hestia_earth/models/lcImpactCertainEffects100Years/damageToTerrestrialEcosystemsPdfYear.py +2 -2
  75. hestia_earth/models/lcImpactCertainEffects100Years/damageToTerrestrialEcosystemsTerrestrialEcotoxicity.py +2 -3
  76. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToFreshwaterEcosystemsFreshwaterEcotoxicity.py +2 -3
  77. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToFreshwaterEcosystemsPdfYear.py +2 -2
  78. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealth.py +2 -2
  79. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthClimateChange.py +2 -2
  80. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthHumanToxicityCancerogenic.py +2 -3
  81. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthHumanToxicityNonCancerogenic.py +2 -3
  82. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthStratosphericOzoneDepletion.py +2 -2
  83. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToMarineEcosystemsMarineEcotoxicity.py +2 -3
  84. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToMarineEcosystemsPdfYear.py +2 -2
  85. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToTerrestrialEcosystemsClimateChange.py +2 -2
  86. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToTerrestrialEcosystemsPdfYear.py +2 -2
  87. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToTerrestrialEcosystemsTerrestrialEcotoxicity.py +2 -3
  88. hestia_earth/models/linkedImpactAssessment/emissions.py +3 -0
  89. hestia_earth/models/log.py +4 -3
  90. hestia_earth/models/mocking/search-results.json +575 -575
  91. hestia_earth/models/pooreNemecek2018/excretaKgN.py +4 -4
  92. hestia_earth/models/pooreNemecek2018/excretaKgVs.py +4 -4
  93. hestia_earth/models/pooreNemecek2018/no3ToGroundwaterCropResidueDecomposition.py +1 -1
  94. hestia_earth/models/pooreNemecek2018/no3ToGroundwaterExcreta.py +1 -1
  95. hestia_earth/models/pooreNemecek2018/no3ToGroundwaterInorganicFertiliser.py +1 -1
  96. hestia_earth/models/pooreNemecek2018/no3ToGroundwaterOrganicFertiliser.py +1 -1
  97. hestia_earth/models/pooreNemecek2018/{saplings.py → saplingsDepreciatedAmountPerCycle.py} +1 -1
  98. hestia_earth/models/pooreNemecek2018/utils.py +7 -1
  99. hestia_earth/models/recipe2016Egalitarian/damageToFreshwaterEcosystemsSpeciesYear.py +2 -2
  100. hestia_earth/models/recipe2016Egalitarian/damageToHumanHealth.py +2 -2
  101. hestia_earth/models/recipe2016Egalitarian/damageToMarineEcosystemsSpeciesYear.py +2 -2
  102. hestia_earth/models/recipe2016Egalitarian/damageToTerrestrialEcosystemsSpeciesYear.py +2 -2
  103. hestia_earth/models/recipe2016Egalitarian/freshwaterAquaticEcotoxicityPotential14Dcbeq.py +2 -3
  104. hestia_earth/models/recipe2016Egalitarian/freshwaterEutrophicationPotential.py +2 -2
  105. hestia_earth/models/recipe2016Egalitarian/humanCarcinogenicToxicity.py +2 -3
  106. hestia_earth/models/recipe2016Egalitarian/humanNonCarcinogenicToxicity.py +2 -3
  107. hestia_earth/models/recipe2016Egalitarian/marineAquaticEcotoxicityPotential14Dcbeq.py +2 -3
  108. hestia_earth/models/recipe2016Egalitarian/marineEutrophicationPotential.py +2 -2
  109. hestia_earth/models/recipe2016Egalitarian/ozoneDepletionPotential.py +2 -2
  110. hestia_earth/models/recipe2016Egalitarian/terrestrialAcidificationPotential.py +2 -2
  111. hestia_earth/models/recipe2016Egalitarian/terrestrialEcotoxicityPotential14Dcbeq.py +2 -3
  112. hestia_earth/models/recipe2016Hierarchist/damageToFreshwaterEcosystemsSpeciesYear.py +2 -2
  113. hestia_earth/models/recipe2016Hierarchist/damageToHumanHealth.py +2 -2
  114. hestia_earth/models/recipe2016Hierarchist/damageToMarineEcosystemsSpeciesYear.py +2 -2
  115. hestia_earth/models/recipe2016Hierarchist/damageToTerrestrialEcosystemsSpeciesYear.py +2 -2
  116. hestia_earth/models/recipe2016Hierarchist/freshwaterAquaticEcotoxicityPotential14Dcbeq.py +2 -3
  117. hestia_earth/models/recipe2016Hierarchist/freshwaterEutrophicationPotential.py +2 -2
  118. hestia_earth/models/recipe2016Hierarchist/humanCarcinogenicToxicity.py +2 -3
  119. hestia_earth/models/recipe2016Hierarchist/humanNonCarcinogenicToxicity.py +2 -3
  120. hestia_earth/models/recipe2016Hierarchist/marineAquaticEcotoxicityPotential14Dcbeq.py +2 -3
  121. hestia_earth/models/recipe2016Hierarchist/marineEutrophicationPotential.py +2 -2
  122. hestia_earth/models/recipe2016Hierarchist/ozoneDepletionPotential.py +2 -2
  123. hestia_earth/models/recipe2016Hierarchist/terrestrialAcidificationPotential.py +2 -2
  124. hestia_earth/models/recipe2016Hierarchist/terrestrialEcotoxicityPotential14Dcbeq.py +2 -3
  125. hestia_earth/models/recipe2016Individualist/damageToFreshwaterEcosystemsSpeciesYear.py +2 -2
  126. hestia_earth/models/recipe2016Individualist/damageToHumanHealth.py +2 -2
  127. hestia_earth/models/recipe2016Individualist/damageToMarineEcosystemsSpeciesYear.py +2 -2
  128. hestia_earth/models/recipe2016Individualist/damageToTerrestrialEcosystemsSpeciesYear.py +2 -2
  129. hestia_earth/models/recipe2016Individualist/freshwaterAquaticEcotoxicityPotential14Dcbeq.py +2 -3
  130. hestia_earth/models/recipe2016Individualist/freshwaterEutrophicationPotential.py +2 -2
  131. hestia_earth/models/recipe2016Individualist/humanCarcinogenicToxicity.py +2 -3
  132. hestia_earth/models/recipe2016Individualist/humanNonCarcinogenicToxicity.py +2 -3
  133. hestia_earth/models/recipe2016Individualist/marineAquaticEcotoxicityPotential14Dcbeq.py +2 -3
  134. hestia_earth/models/recipe2016Individualist/marineEutrophicationPotential.py +2 -2
  135. hestia_earth/models/recipe2016Individualist/ozoneDepletionPotential.py +2 -2
  136. hestia_earth/models/recipe2016Individualist/terrestrialAcidificationPotential.py +2 -2
  137. hestia_earth/models/recipe2016Individualist/terrestrialEcotoxicityPotential14Dcbeq.py +2 -3
  138. hestia_earth/models/site/management.py +142 -144
  139. hestia_earth/models/stehfestBouwman2006/n2OToAirCropResidueDecompositionDirect.py +1 -1
  140. hestia_earth/models/stehfestBouwman2006/n2OToAirExcretaDirect.py +1 -1
  141. hestia_earth/models/stehfestBouwman2006/n2OToAirInorganicFertiliserDirect.py +1 -1
  142. hestia_earth/models/stehfestBouwman2006/n2OToAirOrganicFertiliserDirect.py +1 -1
  143. hestia_earth/models/stehfestBouwman2006/noxToAirCropResidueDecomposition.py +1 -1
  144. hestia_earth/models/stehfestBouwman2006/noxToAirExcreta.py +1 -1
  145. hestia_earth/models/stehfestBouwman2006/noxToAirInorganicFertiliser.py +1 -1
  146. hestia_earth/models/stehfestBouwman2006/noxToAirOrganicFertiliser.py +1 -1
  147. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirCropResidueDecomposition.py +1 -1
  148. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirExcreta.py +1 -1
  149. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirInorganicFertiliser.py +1 -1
  150. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirOrganicFertiliser.py +1 -1
  151. hestia_earth/models/usetoxV2/freshwaterEcotoxicityPotentialCtue.py +2 -3
  152. hestia_earth/models/utils/__init__.py +4 -1
  153. hestia_earth/models/utils/blank_node.py +34 -14
  154. hestia_earth/models/utils/emission.py +1 -8
  155. hestia_earth/models/utils/lookup.py +2 -1
  156. hestia_earth/models/utils/management.py +11 -0
  157. hestia_earth/models/utils/pesticideAI.py +11 -17
  158. hestia_earth/models/utils/term.py +2 -1
  159. hestia_earth/models/version.py +1 -1
  160. {hestia_earth_models-0.64.11.dist-info → hestia_earth_models-0.64.13.dist-info}/METADATA +4 -4
  161. {hestia_earth_models-0.64.11.dist-info → hestia_earth_models-0.64.13.dist-info}/RECORD +192 -188
  162. {hestia_earth_models-0.64.11.dist-info → hestia_earth_models-0.64.13.dist-info}/WHEEL +1 -1
  163. tests/models/cycle/completeness/test_seed.py +1 -1
  164. tests/models/cycle/test_endDate.py +18 -2
  165. tests/models/cycle/test_startDate.py +21 -3
  166. tests/models/faostat2018/test_faostat_utils.py +84 -0
  167. tests/models/hestia/__init__.py +0 -0
  168. tests/models/hestia/test_landCover.py +210 -0
  169. tests/models/ipcc2019/animal/test_milkYieldPerAnimal.py +21 -0
  170. tests/models/ipcc2019/test_aboveGroundBiomass.py +2 -1
  171. tests/models/ipcc2019/test_belowGroundBiomass.py +2 -1
  172. tests/models/ipcc2019/test_co2ToAirAboveGroundBiomassStockChange.py +4 -3
  173. tests/models/ipcc2019/test_co2ToAirBelowGroundBiomassStockChange.py +3 -3
  174. tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChange.py +49 -2
  175. tests/models/ipcc2019/test_n2OToAirExcretaDirect.py +12 -0
  176. tests/models/ipcc2019/test_organicCarbonPerHa.py +1 -0
  177. tests/models/koble2014/test_aboveGroundCropResidue.py +13 -0
  178. tests/models/pooreNemecek2018/test_no3ToGroundwaterCropResidueDecomposition.py +3 -2
  179. tests/models/pooreNemecek2018/test_no3ToGroundwaterExcreta.py +3 -2
  180. tests/models/pooreNemecek2018/test_no3ToGroundwaterInorganicFertiliser.py +3 -2
  181. tests/models/pooreNemecek2018/test_no3ToGroundwaterOrganicFertiliser.py +3 -2
  182. tests/models/pooreNemecek2018/{test_saplings.py → test_saplingsDepreciatedAmountPerCycle.py} +1 -1
  183. tests/models/site/test_management.py +18 -151
  184. tests/models/utils/test_blank_node.py +57 -1
  185. tests/models/utils/test_emission.py +1 -6
  186. tests/models/utils/test_site.py +33 -2
  187. tests/models/pooreNemecek2018/test_no3ToGroundwaterSoilFlux.py +0 -90
  188. tests/models/stehfestBouwman2006/test_n2OToAirSoilFlux.py +0 -41
  189. tests/models/stehfestBouwman2006/test_noxToAirSoilFlux.py +0 -40
  190. tests/models/stehfestBouwman2006GisImplementation/test_noxToAirSoilFlux.py +0 -33
  191. /hestia_earth/models/pooreNemecek2018/{no3ToGroundwaterSoilFlux.py → no3ToGroundwaterSoilFlux_utils.py} +0 -0
  192. /hestia_earth/models/stehfestBouwman2006/{n2OToAirSoilFlux.py → n2OToAirSoilFlux_utils.py} +0 -0
  193. /hestia_earth/models/stehfestBouwman2006/{noxToAirSoilFlux.py → noxToAirSoilFlux_utils.py} +0 -0
  194. /hestia_earth/models/stehfestBouwman2006GisImplementation/{noxToAirSoilFlux.py → noxToAirSoilFlux_utils.py} +0 -0
  195. {hestia_earth_models-0.64.11.dist-info → hestia_earth_models-0.64.13.dist-info}/LICENSE +0 -0
  196. {hestia_earth_models-0.64.11.dist-info → hestia_earth_models-0.64.13.dist-info}/top_level.txt +0 -0
@@ -29,5 +29,5 @@ def run(impact_assessment: dict):
29
29
  value = impact_emission_lookup_value(MODEL, TERM_ID, impact_assessment, LOOKUPS['emission'])
30
30
  logRequirements(impact_assessment, model=MODEL, term=TERM_ID,
31
31
  value=value)
32
- logShouldRun(impact_assessment, MODEL, TERM_ID, True)
33
- return _indicator(value)
32
+ logShouldRun(impact_assessment, MODEL, TERM_ID, value is not None)
33
+ return _indicator(value) if value is not None else None
@@ -29,5 +29,5 @@ def run(impact_assessment: dict):
29
29
  value = impact_emission_lookup_value(MODEL, TERM_ID, impact_assessment, LOOKUPS['emission'])
30
30
  logRequirements(impact_assessment, model=MODEL, term=TERM_ID,
31
31
  value=value)
32
- logShouldRun(impact_assessment, MODEL, TERM_ID, True)
33
- return _indicator(value)
32
+ logShouldRun(impact_assessment, MODEL, TERM_ID, value is not None)
33
+ return _indicator(value) if value is not None else None
@@ -7,11 +7,9 @@ from typing import Optional, Union
7
7
  from hestia_earth.schema import (
8
8
  MeasurementMethodClassification,
9
9
  MeasurementStatsDefinition,
10
- SiteSiteType,
11
- TermTermType
10
+ SiteSiteType
12
11
  )
13
12
 
14
- from hestia_earth.utils.model import filter_list_term_type
15
13
  from hestia_earth.utils.tools import non_empty_list
16
14
 
17
15
  from hestia_earth.models.log import log_as_table, logRequirements, logShouldRun
@@ -24,8 +22,8 @@ from hestia_earth.models.utils.measurement import _new_measurement
24
22
 
25
23
  from . import MODEL
26
24
  from .biomass_utils import (
27
- BiomassCategory, detect_land_cover_change, group_by_biomass_category, group_by_term_id, sample_biomass_equilibrium,
28
- summarise_land_cover_nodes
25
+ BiomassCategory, get_valid_land_cover_terms, detect_land_cover_change, group_by_biomass_category, group_by_term_id,
26
+ sample_biomass_equilibrium, summarise_land_cover_nodes
29
27
  )
30
28
 
31
29
 
@@ -94,8 +92,6 @@ _ITERATIONS = 10000
94
92
  _METHOD_CLASSIFICATION = MeasurementMethodClassification.TIER_1_MODEL.value
95
93
  _STATS_DEFINITION = MeasurementStatsDefinition.SIMULATED.value
96
94
 
97
- _LAND_COVER_TERM_TYPE = TermTermType.LANDCOVER
98
-
99
95
  _EQUILIBRIUM_TRANSITION_PERIOD = 20
100
96
  _EXCLUDED_ECO_CLIMATE_ZONES = {EcoClimateZone.POLAR_MOIST, EcoClimateZone.POLAR_DRY}
101
97
  _EXCLUDED_SITE_TYPES = {
@@ -156,7 +152,7 @@ def _should_run(site: dict) -> tuple[bool, dict, dict]:
156
152
  site_type = site.get("siteType")
157
153
  eco_climate_zone = get_eco_climate_zone_value(site, as_enum=True)
158
154
 
159
- land_cover = filter_list_term_type(site.get("management", []), _LAND_COVER_TERM_TYPE)
155
+ land_cover = get_valid_land_cover_terms(site)
160
156
 
161
157
  has_valid_site_type = site_type not in _EXCLUDED_SITE_TYPES
162
158
  has_valid_eco_climate_zone = all([
@@ -33,6 +33,6 @@ TERM_ID = 'fatContent'
33
33
 
34
34
  def run(cycle: dict):
35
35
  animals = should_run_by_productivity_lookup(
36
- TERM_ID, cycle, list(LOOKUPS.keys())[0], practice_id=LOOKUPS['liveAnimal']
36
+ TERM_ID, cycle, list(LOOKUPS.keys())[0], practice_column=LOOKUPS['liveAnimal']
37
37
  )
38
38
  return list(map(run_animal_by_productivity(TERM_ID, include_practice=True), animals))
@@ -0,0 +1,91 @@
1
+ """
2
+ Milk Yield Per Animal
3
+
4
+ This model gap-fills the practice "Milk yield per X, raw" for live animals added to the animal node,
5
+ taking values from a lookup file.
6
+ """
7
+ from hestia_earth.models.log import logRequirements, logShouldRun
8
+ from hestia_earth.models.utils.blank_node import merge_blank_nodes, get_lookup_value
9
+ from hestia_earth.models.utils.practice import _new_practice
10
+ from .utils import map_live_animals_by_productivity_lookup
11
+ from .. import MODEL
12
+
13
+ REQUIREMENTS = {
14
+ "Cycle": {
15
+ "site": {
16
+ "@type": "Site",
17
+ "country": {"@type": "Term", "termType": "region"}
18
+ },
19
+ "animals": [{
20
+ "@type": "Animal",
21
+ "term.termType": "liveAnimal",
22
+ "practices": {
23
+ "@type": "Practice",
24
+ "term.termType": "animalManagement"
25
+ }
26
+ }]
27
+ }
28
+ }
29
+ LOOKUPS = {
30
+ "region-liveAnimal-milkYieldPerAnimal": "yield value",
31
+ "liveAnimal": ["milkYieldPracticeTermIds", "ipcc2019MilkYieldPerAnimalTermId"]
32
+ }
33
+ RETURNS = {
34
+ "Animal": [{
35
+ "practices": [{
36
+ "@type": "Practice",
37
+ "value": ""
38
+ }]
39
+ }]
40
+ }
41
+ MODEL_KEY = 'milkYieldPerAnimal'
42
+
43
+
44
+ def _practice(term_id: str, value: float):
45
+ node = _new_practice(term_id, MODEL)
46
+ node['value'] = [value]
47
+ return node
48
+
49
+
50
+ def _run_animal(data: dict):
51
+ animal = data.get('animal')
52
+ value = data.get('value')
53
+ practice_id = get_lookup_value(animal.get('term'), LOOKUPS['liveAnimal'][1])
54
+ return animal | ({
55
+ 'practices': merge_blank_nodes(animal.get('practices', []), [_practice(practice_id, value)])
56
+ } if practice_id else {})
57
+
58
+
59
+ def _should_run(cycle: dict):
60
+ country = cycle.get('site', {}).get('country', {})
61
+ country_id = country.get('@id')
62
+ live_animals_with_value = map_live_animals_by_productivity_lookup(
63
+ None, cycle, list(LOOKUPS.keys())[0], practice_column=LOOKUPS['liveAnimal'][0]
64
+ )
65
+
66
+ def _should_run_animal(value: dict):
67
+ animal = value.get('animal')
68
+ lookup_value = value.get('value')
69
+ term_id = animal.get('term').get('@id')
70
+ practice = value.get('practice')
71
+
72
+ logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
73
+ country_id=country_id,
74
+ practice=practice.get('term', {}).get('@id'))
75
+
76
+ should_run = all([
77
+ country_id,
78
+ lookup_value is not None,
79
+ # must not have the practice already
80
+ not practice
81
+ ])
82
+ logShouldRun(cycle, MODEL, term_id, should_run, model_key=MODEL_KEY)
83
+
84
+ return should_run
85
+
86
+ return list(filter(_should_run_animal, live_animals_with_value))
87
+
88
+
89
+ def run(cycle: dict):
90
+ animals = _should_run(cycle)
91
+ return list(map(_run_animal, animals))
@@ -33,6 +33,6 @@ TERM_ID = 'trueProteinContent'
33
33
 
34
34
  def run(cycle: dict):
35
35
  animals = should_run_by_productivity_lookup(
36
- TERM_ID, cycle, list(LOOKUPS.keys())[0], practice_id=LOOKUPS['liveAnimal']
36
+ TERM_ID, cycle, list(LOOKUPS.keys())[0], practice_column=LOOKUPS['liveAnimal']
37
37
  )
38
38
  return list(map(run_animal_by_productivity(TERM_ID, include_practice=True), animals))
@@ -11,9 +11,9 @@ from hestia_earth.models.utils.term import get_lookup_value
11
11
  from .. import MODEL
12
12
 
13
13
 
14
- def _get_practice(term_id: str, animal: dict, practice_id: str):
14
+ def _get_practice(term_id: str, animal: dict, practice_column: str):
15
15
  term = animal.get('term', {})
16
- value = get_lookup_value(term, practice_id, model=MODEL, term=term_id)
16
+ value = get_lookup_value(term, practice_column, model=MODEL, term=term_id)
17
17
  practice_ids = non_empty_list((value or '').split(';'))
18
18
  return next(
19
19
  (p for p in animal.get('practices', []) if p.get('term', {}).get('@id') in practice_ids),
@@ -36,7 +36,7 @@ def productivity_lookup_value(term_id: str, lookup: str, country: dict, animal:
36
36
  )
37
37
 
38
38
 
39
- def map_live_animals_by_productivity_lookup(term_id: str, cycle: dict, lookup_col: str, practice_id: str = None):
39
+ def map_live_animals_by_productivity_lookup(term_id: str, cycle: dict, lookup_col: str, practice_column: str = None):
40
40
  country = cycle.get('site', {}).get('country', {})
41
41
  live_animals = filter_list_term_type(cycle.get('animals', []), TermTermType.LIVEANIMAL)
42
42
  live_animals = list(filter(node_has_no_property(term_id), live_animals))
@@ -44,35 +44,40 @@ def map_live_animals_by_productivity_lookup(term_id: str, cycle: dict, lookup_co
44
44
  'animal': animal,
45
45
  'value': productivity_lookup_value(term_id, lookup_col, country, animal)
46
46
  } | ({
47
- 'practice': _get_practice(term_id, animal, practice_id)
48
- } if practice_id else {}) for animal in live_animals]
47
+ 'practice': _get_practice(term_id, animal, practice_column)
48
+ } if practice_column else {}) for animal in live_animals]
49
49
 
50
50
 
51
- def should_run_by_productivity_lookup(term_id: str, cycle: dict, lookup_col: str, practice_id: str = None):
51
+ def should_run_by_productivity_lookup(
52
+ term_id: str,
53
+ cycle: dict,
54
+ lookup_col: str,
55
+ practice_column: str = None
56
+ ):
52
57
  country = cycle.get('site', {}).get('country', {})
53
58
  country_id = country.get('@id')
54
- live_animals_with_value = map_live_animals_by_productivity_lookup(term_id, cycle, lookup_col, practice_id)
59
+ live_animals_with_value = map_live_animals_by_productivity_lookup(term_id, cycle, lookup_col, practice_column)
55
60
 
56
61
  def _should_run_animal(value: dict):
57
62
  animal = value.get('animal')
58
63
  lookup_value = value.get('value')
59
64
  practice = value.get('practice')
60
- term_id = animal.get('term').get('@id')
65
+ animal_term_id = animal.get('term').get('@id')
61
66
 
62
- logRequirements(cycle, model=MODEL, term=term_id, property=term_id,
67
+ logRequirements(cycle, model=MODEL, term=animal_term_id, property=term_id,
63
68
  country_id=country_id,
64
69
  **({
65
70
  lookup_col.replace('-', '_'): lookup_value
66
71
  } | ({
67
72
  'practice': practice.get('term', {}).get('@id')
68
- } if practice_id else {})))
73
+ } if practice_column else {})))
69
74
 
70
75
  should_run = all([
71
76
  country_id,
72
- not practice_id or bool(practice),
77
+ not practice_column or bool(practice),
73
78
  lookup_value is not None
74
79
  ])
75
- logShouldRun(cycle, MODEL, term_id, should_run, property=term_id)
80
+ logShouldRun(cycle, MODEL, animal_term_id, should_run, property=term_id)
76
81
 
77
82
  return should_run
78
83
 
@@ -7,11 +7,9 @@ from typing import Optional, Union
7
7
  from hestia_earth.schema import (
8
8
  MeasurementMethodClassification,
9
9
  MeasurementStatsDefinition,
10
- SiteSiteType,
11
- TermTermType
10
+ SiteSiteType
12
11
  )
13
12
 
14
- from hestia_earth.utils.model import filter_list_term_type
15
13
  from hestia_earth.utils.tools import non_empty_list
16
14
 
17
15
  from hestia_earth.models.log import log_as_table, logRequirements, logShouldRun
@@ -24,8 +22,8 @@ from hestia_earth.models.utils.measurement import _new_measurement
24
22
 
25
23
  from . import MODEL
26
24
  from .biomass_utils import (
27
- BiomassCategory, detect_land_cover_change, group_by_biomass_category, sample_biomass_equilibrium,
28
- summarise_land_cover_nodes
25
+ BiomassCategory, get_valid_land_cover_terms, detect_land_cover_change, group_by_biomass_category,
26
+ sample_biomass_equilibrium, summarise_land_cover_nodes
29
27
  )
30
28
 
31
29
 
@@ -81,8 +79,6 @@ _ITERATIONS = 10000
81
79
  _METHOD_CLASSIFICATION = MeasurementMethodClassification.TIER_1_MODEL.value
82
80
  _STATS_DEFINITION = MeasurementStatsDefinition.SIMULATED.value
83
81
 
84
- _LAND_COVER_TERM_TYPE = TermTermType.LANDCOVER
85
-
86
82
  _EQUILIBRIUM_TRANSITION_PERIOD = 20
87
83
  _EXCLUDED_ECO_CLIMATE_ZONES = {EcoClimateZone.POLAR_MOIST, EcoClimateZone.POLAR_DRY}
88
84
  _EXCLUDED_SITE_TYPES = {
@@ -149,7 +145,7 @@ def _should_run(site: dict) -> tuple[bool, dict, dict]:
149
145
  site_type = site.get("siteType")
150
146
  eco_climate_zone = get_eco_climate_zone_value(site, as_enum=True)
151
147
 
152
- land_cover = filter_list_term_type(site.get("management", []), _LAND_COVER_TERM_TYPE)
148
+ land_cover = get_valid_land_cover_terms(site)
153
149
 
154
150
  has_valid_site_type = site_type not in _EXCLUDED_SITE_TYPES
155
151
  has_valid_eco_climate_zone = all([
@@ -5,9 +5,12 @@ from numpy import random
5
5
  from numpy.typing import NDArray
6
6
  from typing import Callable, Optional, Union
7
7
 
8
+ from hestia_earth.schema import TermTermType
8
9
  from hestia_earth.utils.blank_node import get_node_value
10
+ from hestia_earth.utils.model import filter_list_term_type
9
11
 
10
12
  from hestia_earth.models.utils.array_builders import repeat_single, truncated_normal_1d
13
+ from hestia_earth.models.utils.blank_node import validate_start_date_end_date
11
14
  from hestia_earth.models.utils.ecoClimateZone import EcoClimateZone, get_ecoClimateZone_lookup_grouped_value
12
15
  from hestia_earth.models.utils.term import get_lookup_value
13
16
 
@@ -404,3 +407,11 @@ _KWARGS_TO_SAMPLE_FUNC = {
404
407
  ("value", "error"): sample_plus_minus_error,
405
408
  ("value",): sample_constant
406
409
  }
410
+
411
+
412
+ def get_valid_land_cover_terms(site: dict) -> list[dict]:
413
+ """Retrieve valid `landCover` nodes from a site's management."""
414
+ return [
415
+ node for node in filter_list_term_type(site.get("management", []), TermTermType.LANDCOVER)
416
+ if validate_start_date_end_date(node)
417
+ ]
@@ -250,7 +250,7 @@ def _should_run(cycle: dict):
250
250
  milk_yield=milk_yield,
251
251
  enteric_factor=enteric_factor,
252
252
  enteric_sd=enteric_sd,
253
- default_values=default_values)
253
+ default_values=';'.join(map(str, default_values)))
254
254
 
255
255
  logRequirements(cycle, model=MODEL, term=TERM_ID,
256
256
  term_type_animalFeed_complete=is_animalFeed_complete,
@@ -77,7 +77,9 @@ def _emission(
77
77
  min: list[float] = None,
78
78
  max: list[float] = None,
79
79
  statsDefinition: str = None,
80
- observations: list[int] = None
80
+ observations: list[int] = None,
81
+ start_date: str,
82
+ end_date: str
81
83
  ) -> dict:
82
84
  """
83
85
  Create an emission node based on the provided value and method tier.
@@ -105,7 +107,9 @@ def _emission(
105
107
  "max": max,
106
108
  "statsDefinition": statsDefinition,
107
109
  "observations": observations,
108
- "methodTier": method_tier.value
110
+ "startDate": start_date,
111
+ "endDate": end_date,
112
+ "methodTier": method_tier.value,
109
113
  }
110
114
  emission = _new_emission(term_id, MODEL) | {
111
115
  key: value for key, value in update_dict.items() if value
@@ -141,13 +145,13 @@ def run(cycle: dict) -> list[dict]:
141
145
  management_change_emission_term_id=_MG_EMISSION_TERM_ID
142
146
  )
143
147
 
144
- should_run, cycle_id, inventory, logs = should_run_exec(cycle)
148
+ should_run, kwargs, logs = should_run_exec(cycle)
145
149
 
146
150
  for term_id in [_LU_EMISSION_TERM_ID, _MG_EMISSION_TERM_ID]:
147
151
  logRequirements(cycle, model=MODEL, term=term_id, **logs)
148
152
  logShouldRun(cycle, MODEL, term_id, should_run)
149
153
 
150
- return run_exec(cycle_id, inventory) if should_run else []
154
+ return run_exec(**kwargs) if should_run else []
151
155
 
152
156
 
153
157
  def _should_compile_inventory_func(
@@ -68,7 +68,9 @@ def _emission(
68
68
  min: list[float] = None,
69
69
  max: list[float] = None,
70
70
  statsDefinition: str = None,
71
- observations: list[int] = None
71
+ observations: list[int] = None,
72
+ start_date: str,
73
+ end_date: str
72
74
  ) -> dict:
73
75
  """
74
76
  Create an emission node based on the provided value and method tier.
@@ -96,6 +98,8 @@ def _emission(
96
98
  "max": max,
97
99
  "statsDefinition": statsDefinition,
98
100
  "observations": observations,
101
+ "startDate": start_date,
102
+ "endDate": end_date,
99
103
  "methodTier": method_tier.value,
100
104
  "depth": _DEPTH_LOWER
101
105
  }
@@ -133,13 +137,13 @@ def run(cycle: dict) -> list[dict]:
133
137
  management_change_emission_term_id=_MG_EMISSION_TERM_ID
134
138
  )
135
139
 
136
- should_run, cycle_id, inventory, logs = should_run_exec(cycle)
140
+ should_run, kwargs, logs = should_run_exec(cycle)
137
141
 
138
142
  for term_id in [_LU_EMISSION_TERM_ID, _MG_EMISSION_TERM_ID]:
139
143
  logRequirements(cycle, model=MODEL, term=term_id, **logs)
140
144
  logShouldRun(cycle, MODEL, term_id, should_run)
141
145
 
142
- return run_exec(cycle_id, inventory) if should_run else []
146
+ return run_exec(**kwargs) if should_run else []
143
147
 
144
148
 
145
149
  def _should_run_measurement_func(node: dict) -> bool:
@@ -3,7 +3,7 @@ Utilities for calculating CO2 emissions based on changes in carbon stocks (e.g.,
3
3
  `aboveGroundBiomass` and `belowGroundBiomass`).
4
4
  """
5
5
 
6
- from datetime import datetime
6
+ from datetime import datetime, timedelta
7
7
  from enum import Enum
8
8
  from functools import reduce
9
9
  from itertools import product
@@ -24,7 +24,7 @@ from hestia_earth.models.utils import pairwise
24
24
  from hestia_earth.models.utils.array_builders import correlated_normal_2d, gen_seed
25
25
  from hestia_earth.models.utils.blank_node import (
26
26
  _gapfill_datestr, _get_datestr_format, DatestrGapfillMode, DatestrFormat, group_nodes_by_year, node_term_match,
27
- split_node_by_dates
27
+ split_node_by_dates, validate_start_date_end_date
28
28
  )
29
29
  from hestia_earth.models.utils.constant import Units, get_atomic_conversion
30
30
  from hestia_earth.models.utils.descriptive_stats import calc_descriptive_stats
@@ -401,11 +401,15 @@ def create_should_run_function(
401
401
  `(should_run, cycle_id, inventory, logs)`
402
402
  """
403
403
  cycle_id = cycle.get("@id")
404
+ cycle_start_date = cycle.get("startDate")
405
+ cycle_end_date = cycle.get("endDate")
406
+
404
407
  site = _get_site(cycle)
405
408
  cycles = related_cycles(site)
406
409
 
407
410
  carbon_stock_measurements = [
408
- node for node in site.get("measurements", []) if all([
411
+ node for node in site.get("measurements", [])
412
+ if all([
409
413
  node_term_match(node, carbon_stock_term_id),
410
414
  _has_valid_array_fields(node),
411
415
  _has_valid_dates(node),
@@ -414,7 +418,10 @@ def create_should_run_function(
414
418
  ])
415
419
  ]
416
420
 
417
- land_cover_nodes = filter_list_term_type(site.get("management", []), TermTermType.LANDCOVER)
421
+ land_cover_nodes = [
422
+ node for node in filter_list_term_type(site.get("management", []), TermTermType.LANDCOVER)
423
+ if validate_start_date_end_date(node)
424
+ ]
418
425
 
419
426
  seed = gen_seed(site) # All cycles linked to the same site should be consistent
420
427
  rng = random.default_rng(seed)
@@ -445,13 +452,20 @@ def create_should_run_function(
445
452
 
446
453
  should_run_ = all([has_valid_inventory, has_consecutive_years])
447
454
 
455
+ kwargs = {
456
+ "cycle_id": cycle_id,
457
+ "cycle_start_date": cycle_start_date,
458
+ "cycle_end_date": cycle_end_date,
459
+ "inventory": inventory
460
+ }
461
+
448
462
  logs = should_compile_logs | inventory_logs | {
449
463
  "seed": seed,
450
464
  "has_valid_inventory": has_valid_inventory,
451
465
  "has_consecutive_years": has_consecutive_years
452
466
  }
453
467
 
454
- return should_run_, cycle_id, inventory, logs
468
+ return should_run_, kwargs, logs
455
469
 
456
470
  return should_run
457
471
 
@@ -1413,7 +1427,7 @@ def create_run_function(
1413
1427
 
1414
1428
  return result | update_dict
1415
1429
 
1416
- def run(cycle_id: str, inventory: dict) -> list[dict]:
1430
+ def run(cycle_id: str, cycle_start_date: str, cycle_end_date: str, inventory: dict) -> list[dict]:
1417
1431
  """
1418
1432
  Calculate emissions for a specific cycle using from a carbon stock change using pre-compiled inventory data.
1419
1433
 
@@ -1442,6 +1456,8 @@ def create_run_function(
1442
1456
  new_emission_func(
1443
1457
  term_id=emission_term_id,
1444
1458
  method_tier=total_emission.method,
1459
+ start_date=_get_emission_start_date(total_emission, cycle_start_date),
1460
+ end_date=_get_emission_end_date(total_emission, cycle_end_date),
1445
1461
  **calc_descriptive_stats(
1446
1462
  total_emission.value,
1447
1463
  EmissionStatsDefinition.SIMULATED,
@@ -1451,3 +1467,33 @@ def create_run_function(
1451
1467
  ]
1452
1468
 
1453
1469
  return run
1470
+
1471
+
1472
+ def _get_emission_start_date(emission: CarbonStockChangeEmission, cycle_start_date: str) -> str:
1473
+ cycle_datetime = safe_parse_date(_gapfill_datestr(cycle_start_date))
1474
+ emission_datetime = safe_parse_date(emission.start_date)
1475
+
1476
+ should_run = (
1477
+ cycle_datetime and emission_datetime
1478
+ and cycle_datetime <= emission_datetime # If the cycle starts before the emission, add a `startDate`
1479
+ )
1480
+
1481
+ return (
1482
+ (emission_datetime + timedelta(seconds=1)).strftime(DatestrFormat.YEAR_MONTH_DAY.value) if should_run
1483
+ else None
1484
+ )
1485
+
1486
+
1487
+ def _get_emission_end_date(emission: CarbonStockChangeEmission, cycle_end_date: str) -> str:
1488
+ cycle_datetime = safe_parse_date(_gapfill_datestr(cycle_end_date))
1489
+ emission_datetime = safe_parse_date(emission.end_date)
1490
+
1491
+ should_run = (
1492
+ cycle_datetime and emission_datetime
1493
+ and cycle_datetime >= emission_datetime # If the cycle ends after the emission, add an `endDate`
1494
+ )
1495
+
1496
+ return (
1497
+ emission_datetime.strftime(DatestrFormat.YEAR_MONTH_DAY.value) if should_run
1498
+ else None
1499
+ )
@@ -68,7 +68,9 @@ def _emission(
68
68
  min: list[float] = None,
69
69
  max: list[float] = None,
70
70
  statsDefinition: str = None,
71
- observations: list[int] = None
71
+ observations: list[int] = None,
72
+ start_date: str,
73
+ end_date: str
72
74
  ) -> dict:
73
75
  """
74
76
  Create an emission node based on the provided value and method tier.
@@ -96,6 +98,8 @@ def _emission(
96
98
  "max": max,
97
99
  "statsDefinition": statsDefinition,
98
100
  "observations": observations,
101
+ "startDate": start_date,
102
+ "endDate": end_date,
99
103
  "methodTier": method_tier.value,
100
104
  "depth": _DEPTH_LOWER
101
105
  }
@@ -133,13 +137,13 @@ def run(cycle: dict) -> list[dict]:
133
137
  management_change_emission_term_id=_MG_EMISSION_TERM_ID
134
138
  )
135
139
 
136
- should_run, cycle_id, inventory, logs = should_run_exec(cycle)
140
+ should_run, kwargs, logs = should_run_exec(cycle)
137
141
 
138
142
  for term_id in [_LU_EMISSION_TERM_ID, _MG_EMISSION_TERM_ID]:
139
143
  logRequirements(cycle, model=MODEL, term=term_id, **logs)
140
144
  logShouldRun(cycle, MODEL, term_id, should_run)
141
145
 
142
- return run_exec(cycle_id, inventory) if should_run else []
146
+ return run_exec(**kwargs) if should_run else []
143
147
 
144
148
 
145
149
  def _should_run_measurement_func(node: dict) -> bool:
@@ -10,7 +10,12 @@ from . import MODEL
10
10
 
11
11
  REQUIREMENTS = {
12
12
  "Cycle": {
13
- "completeness.excreta": "True",
13
+ "or": {
14
+ "completeness.excreta": "True",
15
+ "inputs": [
16
+ {"@type": "Input", "term.termType": "excreta", "units": "kg N"}
17
+ ]
18
+ },
14
19
  "practices": [
15
20
  {"@type": "Practice", "value": "", "term.termType": "excretaManagement"}
16
21
  ]
@@ -36,26 +41,26 @@ def _emission(value: float):
36
41
  return emission
37
42
 
38
43
 
39
- def _run(excretaKgN: float, N2O_N_EF: float):
40
- value = N2O_N_EF * excretaKgN * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
44
+ def _run(N_total: float, N2O_N_EF: float):
45
+ value = (N2O_N_EF or 0) * (N_total or 0) * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
41
46
  return [_emission(value)]
42
47
 
43
48
 
44
49
  def _should_run(cycle: dict):
45
- excretaKgN = total_excreta(cycle.get('inputs', []))
50
+ N_total = total_excreta(cycle.get('inputs', []))
46
51
  N2O_N_EF = get_lookup_factor(cycle.get('practices', []), LOOKUPS['excretaManagement'])
47
52
  term_type_complete = _is_term_type_complete(cycle, TermTermType.EXCRETA)
48
53
 
49
54
  logRequirements(cycle, model=MODEL, term=TERM_ID,
50
- excretaKgN=excretaKgN,
55
+ N_total=N_total,
51
56
  N2O_N_EF=N2O_N_EF,
52
57
  term_type_excreta_complete=term_type_complete)
53
58
 
54
- should_run = all([excretaKgN, N2O_N_EF])
59
+ should_run = all([N_total, N2O_N_EF]) or all([not N_total, term_type_complete])
55
60
  logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
56
- return should_run, excretaKgN, N2O_N_EF
61
+ return should_run, N_total, N2O_N_EF
57
62
 
58
63
 
59
64
  def run(cycle: dict):
60
- should_run, excretaKgN, N2O_N_EF = _should_run(cycle)
61
- return _run(excretaKgN, N2O_N_EF) if should_run else []
65
+ should_run, N_total, N2O_N_EF = _should_run(cycle)
66
+ return _run(N_total, N2O_N_EF) if should_run else []
@@ -24,7 +24,7 @@ from hestia_earth.utils.blank_node import get_node_value
24
24
  from hestia_earth.models.utils.array_builders import gen_seed
25
25
  from hestia_earth.models.utils.blank_node import (
26
26
  cumulative_nodes_match, cumulative_nodes_lookup_match, cumulative_nodes_term_match,
27
- node_lookup_match, node_term_match, group_nodes_by_year
27
+ node_lookup_match, node_term_match, group_nodes_by_year, validate_start_date_end_date
28
28
  )
29
29
  from hestia_earth.models.utils.ecoClimateZone import EcoClimateZone, get_eco_climate_zone_value
30
30
  from hestia_earth.models.utils.descriptive_stats import calc_descriptive_stats
@@ -1209,8 +1209,14 @@ def _assign_ipcc_land_use_category(
1209
1209
  DECISION_TREE = _LAND_USE_CATEGORY_DECISION_TREE
1210
1210
  DEFAULT = IpccLandUseCategory.OTHER
1211
1211
 
1212
- land_cover_nodes = filter_list_term_type(management_nodes, [TermTermType.LANDCOVER])
1213
- water_regime_nodes = filter_list_term_type(management_nodes, [TermTermType.WATERREGIME])
1212
+ land_cover_nodes = [
1213
+ node for node in filter_list_term_type(management_nodes, [TermTermType.LANDCOVER])
1214
+ if validate_start_date_end_date(node)
1215
+ ]
1216
+ water_regime_nodes = [
1217
+ node for node in filter_list_term_type(management_nodes, [TermTermType.WATERREGIME])
1218
+ if validate_start_date_end_date(node)
1219
+ ]
1214
1220
 
1215
1221
  has_irrigation = check_irrigation(water_regime_nodes)
1216
1222
  has_upland_rice = _has_upland_rice(land_cover_nodes)
@@ -1271,8 +1271,7 @@ def _get_irrigated_monthly(year: int, cycles: list[dict]) -> list[bool]:
1271
1271
  irrigation_nodes = non_empty_list(flatten([
1272
1272
  [
1273
1273
  {
1274
- "startDate": cycle.get("startDate"),
1275
- "endDate": cycle.get("endDate"),
1274
+ **{key: cycle.get(key) for key in ["startDate", "endDate"] if cycle.get(key)},
1276
1275
  **node
1277
1276
  } for node in cycle.get("practices", [])
1278
1277
  ] for cycle in cycles
@@ -94,6 +94,10 @@ def _model_value(term_id: str, products: list):
94
94
  return list_average(values) if len(values) > 0 else 0
95
95
 
96
96
 
97
+ def _remaining_model_value(products: list):
98
+ return list_sum(find_term_match(products, REMAINING_MODEL).get('value', []), 0)
99
+
100
+
97
101
  def _run(cycle: dict, total_values: list):
98
102
  products = cycle.get('products', [])
99
103
  total_value = list_average(total_values)
@@ -126,7 +130,7 @@ def _run(cycle: dict, total_values: list):
126
130
 
127
131
  return values + [
128
132
  # whatever remains is "left on field"
129
- _product(REMAINING_MODEL, remaining_value)
133
+ _product(REMAINING_MODEL, remaining_value + _remaining_model_value(products))
130
134
  ] if remaining_value > 0 else values
131
135
 
132
136
 
@@ -29,5 +29,5 @@ def run(impact_assessment: dict):
29
29
  value = impact_emission_lookup_value(MODEL, TERM_ID, impact_assessment, LOOKUPS['emission'])
30
30
  logRequirements(impact_assessment, model=MODEL, term=TERM_ID,
31
31
  value=value)
32
- logShouldRun(impact_assessment, MODEL, TERM_ID, True)
33
- return _indicator(value)
32
+ logShouldRun(impact_assessment, MODEL, TERM_ID, value is not None)
33
+ return _indicator(value) if value is not None else None