hestia-earth-models 0.57.2__py3-none-any.whl → 0.59.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of hestia-earth-models might be problematic. Click here for more details.

Files changed (109) hide show
  1. hestia_earth/models/cycle/aboveGroundCropResidueTotal.py +17 -12
  2. hestia_earth/models/cycle/excretaKgMass.py +4 -5
  3. hestia_earth/models/cycle/excretaKgN.py +4 -5
  4. hestia_earth/models/cycle/excretaKgVs.py +4 -5
  5. hestia_earth/models/cycle/inorganicFertiliser.py +2 -2
  6. hestia_earth/models/cycle/{irrigated.py → irrigatedTypeUnspecified.py} +4 -4
  7. hestia_earth/models/cycle/liveAnimal.py +9 -11
  8. hestia_earth/models/cycle/milkYield.py +154 -0
  9. hestia_earth/models/cycle/residueIncorporated.py +1 -1
  10. hestia_earth/models/cycle/utils.py +6 -0
  11. hestia_earth/models/emepEea2019/nh3ToAirInorganicFertiliser.py +3 -3
  12. hestia_earth/models/faostat2018/seed.py +2 -3
  13. hestia_earth/models/geospatialDatabase/clayContent.py +17 -4
  14. hestia_earth/models/geospatialDatabase/sandContent.py +17 -4
  15. hestia_earth/models/geospatialDatabase/siltContent.py +2 -2
  16. hestia_earth/models/impact_assessment/irrigated.py +0 -3
  17. hestia_earth/models/ipcc2006/co2ToAirOrganicSoilCultivation.py +2 -2
  18. hestia_earth/models/ipcc2006/n2OToAirCropResidueDecompositionIndirect.py +2 -2
  19. hestia_earth/models/ipcc2006/n2OToAirExcretaDirect.py +1 -1
  20. hestia_earth/models/ipcc2006/n2OToAirExcretaIndirect.py +8 -4
  21. hestia_earth/models/ipcc2006/n2OToAirInorganicFertiliserDirect.py +4 -1
  22. hestia_earth/models/ipcc2006/n2OToAirInorganicFertiliserIndirect.py +1 -1
  23. hestia_earth/models/ipcc2006/n2OToAirOrganicFertiliserDirect.py +1 -1
  24. hestia_earth/models/ipcc2006/n2OToAirOrganicFertiliserIndirect.py +1 -1
  25. hestia_earth/models/ipcc2006/utils.py +11 -8
  26. hestia_earth/models/ipcc2019/ch4ToAirEntericFermentation.py +4 -4
  27. hestia_earth/models/ipcc2019/ch4ToAirFloodedRice.py +16 -7
  28. hestia_earth/models/ipcc2019/co2ToAirSoilCarbonStockChangeManagementChange.py +759 -0
  29. hestia_earth/models/ipcc2019/croppingDuration.py +12 -6
  30. hestia_earth/models/ipcc2019/n2OToAirCropResidueDecompositionDirect.py +5 -52
  31. hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserDirect.py +104 -0
  32. hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserIndirect.py +1 -1
  33. hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserDirect.py +105 -0
  34. hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserIndirect.py +1 -1
  35. hestia_earth/models/ipcc2019/no3ToGroundwaterCropResidueDecomposition.py +1 -1
  36. hestia_earth/models/ipcc2019/no3ToGroundwaterExcreta.py +1 -1
  37. hestia_earth/models/ipcc2019/no3ToGroundwaterInorganicFertiliser.py +1 -1
  38. hestia_earth/models/ipcc2019/no3ToGroundwaterOrganicFertiliser.py +1 -1
  39. hestia_earth/models/ipcc2019/organicCarbonPerHa.py +1088 -1268
  40. hestia_earth/models/ipcc2019/pastureGrass.py +4 -4
  41. hestia_earth/models/ipcc2019/utils.py +102 -1
  42. hestia_earth/models/koble2014/aboveGroundCropResidue.py +15 -17
  43. hestia_earth/models/koble2014/cropResidueManagement.py +2 -2
  44. hestia_earth/models/koble2014/utils.py +19 -3
  45. hestia_earth/models/linkedImpactAssessment/__init__.py +4 -2
  46. hestia_earth/models/log.py +15 -3
  47. hestia_earth/models/mocking/search-results.json +184 -118
  48. hestia_earth/models/pooreNemecek2018/excretaKgN.py +6 -7
  49. hestia_earth/models/pooreNemecek2018/excretaKgVs.py +7 -6
  50. hestia_earth/models/pooreNemecek2018/no3ToGroundwaterCropResidueDecomposition.py +3 -2
  51. hestia_earth/models/pooreNemecek2018/no3ToGroundwaterExcreta.py +3 -2
  52. hestia_earth/models/pooreNemecek2018/no3ToGroundwaterInorganicFertiliser.py +3 -2
  53. hestia_earth/models/pooreNemecek2018/saplings.py +0 -1
  54. hestia_earth/models/site/management.py +168 -0
  55. hestia_earth/models/site/organicCarbonPerHa.py +251 -89
  56. hestia_earth/models/stehfestBouwman2006/n2OToAirCropResidueDecompositionDirect.py +3 -2
  57. hestia_earth/models/stehfestBouwman2006/n2OToAirExcretaDirect.py +3 -2
  58. hestia_earth/models/stehfestBouwman2006/n2OToAirInorganicFertiliserDirect.py +3 -2
  59. hestia_earth/models/stehfestBouwman2006/n2OToAirOrganicFertiliserDirect.py +3 -2
  60. hestia_earth/models/stehfestBouwman2006/noxToAirCropResidueDecomposition.py +3 -2
  61. hestia_earth/models/stehfestBouwman2006/noxToAirExcreta.py +3 -2
  62. hestia_earth/models/stehfestBouwman2006/noxToAirInorganicFertiliser.py +3 -2
  63. hestia_earth/models/stehfestBouwman2006/noxToAirOrganicFertiliser.py +3 -2
  64. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirCropResidueDecomposition.py +3 -2
  65. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirExcreta.py +3 -2
  66. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirInorganicFertiliser.py +3 -2
  67. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirOrganicFertiliser.py +3 -2
  68. hestia_earth/models/utils/aggregated.py +1 -0
  69. hestia_earth/models/utils/blank_node.py +394 -72
  70. hestia_earth/models/utils/cropResidue.py +13 -0
  71. hestia_earth/models/utils/cycle.py +18 -9
  72. hestia_earth/models/utils/measurement.py +1 -1
  73. hestia_earth/models/utils/property.py +4 -4
  74. hestia_earth/models/utils/term.py +48 -3
  75. hestia_earth/models/version.py +1 -1
  76. {hestia_earth_models-0.57.2.dist-info → hestia_earth_models-0.59.0.dist-info}/METADATA +5 -9
  77. {hestia_earth_models-0.57.2.dist-info → hestia_earth_models-0.59.0.dist-info}/RECORD +109 -97
  78. {hestia_earth_models-0.57.2.dist-info → hestia_earth_models-0.59.0.dist-info}/WHEEL +1 -1
  79. tests/models/cycle/animal/input/test_hestiaAggregatedData.py +2 -14
  80. tests/models/cycle/input/test_hestiaAggregatedData.py +4 -16
  81. tests/models/cycle/test_coldCarcassWeightPerHead.py +1 -1
  82. tests/models/cycle/test_coldDressedCarcassWeightPerHead.py +1 -1
  83. tests/models/cycle/{test_irrigated.py → test_irrigatedTypeUnspecified.py} +1 -1
  84. tests/models/cycle/test_milkYield.py +58 -0
  85. tests/models/cycle/test_readyToCookWeightPerHead.py +1 -1
  86. tests/models/emepEea2019/test_nh3ToAirInorganicFertiliser.py +1 -1
  87. tests/models/geospatialDatabase/test_clayContent.py +9 -3
  88. tests/models/geospatialDatabase/test_sandContent.py +9 -3
  89. tests/models/ipcc2006/test_n2OToAirExcretaDirect.py +7 -2
  90. tests/models/ipcc2006/test_n2OToAirExcretaIndirect.py +1 -1
  91. tests/models/ipcc2006/test_n2OToAirInorganicFertiliserDirect.py +7 -2
  92. tests/models/ipcc2006/test_n2OToAirInorganicFertiliserIndirect.py +7 -2
  93. tests/models/ipcc2006/test_n2OToAirOrganicFertiliserDirect.py +7 -2
  94. tests/models/ipcc2006/test_n2OToAirOrganicFertiliserIndirect.py +7 -2
  95. tests/models/ipcc2019/test_ch4ToAirEntericFermentation.py +1 -1
  96. tests/models/ipcc2019/test_co2ToAirSoilCarbonStockChangeManagementChange.py +228 -0
  97. tests/models/ipcc2019/test_n2OToAirInorganicFertiliserDirect.py +74 -0
  98. tests/models/ipcc2019/test_n2OToAirOrganicFertiliserDirect.py +74 -0
  99. tests/models/ipcc2019/test_organicCarbonPerHa.py +303 -1044
  100. tests/models/koble2014/test_residueBurnt.py +1 -2
  101. tests/models/koble2014/test_residueLeftOnField.py +1 -2
  102. tests/models/koble2014/test_residueRemoved.py +1 -2
  103. tests/models/koble2014/test_utils.py +52 -0
  104. tests/models/site/test_management.py +117 -0
  105. tests/models/site/test_organicCarbonPerHa.py +51 -5
  106. tests/models/utils/test_blank_node.py +230 -34
  107. tests/models/utils/test_term.py +17 -3
  108. {hestia_earth_models-0.57.2.dist-info → hestia_earth_models-0.59.0.dist-info}/LICENSE +0 -0
  109. {hestia_earth_models-0.57.2.dist-info → hestia_earth_models-0.59.0.dist-info}/top_level.txt +0 -0
@@ -31,7 +31,7 @@ def test_should_run():
31
31
  }
32
32
  ]
33
33
  should_run, *args = _should_run(cycle)
34
- assert should_run
34
+ assert should_run is True
35
35
 
36
36
 
37
37
  @patch(f"{class_path}._new_property", side_effect=fake_new_property)
@@ -31,7 +31,7 @@ def test_should_run():
31
31
  }
32
32
  ]
33
33
  should_run, *args = _should_run(cycle)
34
- assert should_run
34
+ assert should_run is True
35
35
 
36
36
 
37
37
  @patch(f"{class_path}._new_property", side_effect=fake_new_property)
@@ -2,7 +2,7 @@ from unittest.mock import patch
2
2
  import json
3
3
  from tests.utils import fixtures_path, fake_new_practice
4
4
 
5
- from hestia_earth.models.cycle.irrigated import MODEL, TERM_ID, run
5
+ from hestia_earth.models.cycle.irrigatedTypeUnspecified import MODEL, TERM_ID, run
6
6
 
7
7
  class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
8
8
  fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
@@ -0,0 +1,58 @@
1
+ from unittest.mock import patch
2
+ import json
3
+ from hestia_earth.schema import TermTermType
4
+
5
+ from tests.utils import fixtures_path, fake_new_practice
6
+ from hestia_earth.models.cycle.milkYield import MODEL, MODEL_KEY, run, _should_run
7
+
8
+ class_path = f"hestia_earth.models.{MODEL}.{MODEL_KEY}"
9
+ fixtures_folder = f"{fixtures_path}/{MODEL}/{MODEL_KEY}"
10
+
11
+
12
+ @patch(f"{class_path}._get_liveAnimal_term_id", return_value='chicken')
13
+ @patch(f"{class_path}.get_lookup_value", return_value='id')
14
+ @patch(f"{class_path}.valid_site_type", return_value=False)
15
+ def test_should_run(mock_valid_site_type, *args):
16
+ cycle = {'cycleDuration': 365}
17
+
18
+ # with valid siteType => not run
19
+ mock_valid_site_type.return_value = True
20
+ should_run, *args = _should_run(cycle)
21
+ assert not should_run
22
+
23
+ # no animal product => no run
24
+ cycle['products'] = []
25
+ should_run, *args = _should_run(cycle)
26
+ assert not should_run
27
+
28
+ # with animal product => not run
29
+ cycle['products'] = [
30
+ {
31
+ 'term': {'termType': TermTermType.ANIMALPRODUCT.value},
32
+ 'value': [2]
33
+ }
34
+ ]
35
+ should_run, *args = _should_run(cycle)
36
+ assert not should_run
37
+
38
+ # with matching animal => run
39
+ cycle['animals'] = [
40
+ {
41
+ 'term': {'termType': TermTermType.LIVEANIMAL.value, '@id': 'chicken'},
42
+ 'value': 2
43
+ }
44
+ ]
45
+ should_run, *args = _should_run(cycle)
46
+ assert should_run is True
47
+
48
+
49
+ @patch(f"{class_path}._new_practice", side_effect=fake_new_practice)
50
+ def test_run(*argsm):
51
+ with open(f"{fixtures_folder}/cycle.jsonld", encoding='utf-8') as f:
52
+ cycle = json.load(f)
53
+
54
+ with open(f"{fixtures_folder}/result.jsonld", encoding='utf-8') as f:
55
+ expected = json.load(f)
56
+
57
+ value = run(cycle)
58
+ assert value == expected
@@ -31,7 +31,7 @@ def test_should_run():
31
31
  }
32
32
  ]
33
33
  should_run, *args = _should_run(cycle)
34
- assert should_run
34
+ assert should_run is True
35
35
 
36
36
 
37
37
  @patch(f"{class_path}._new_property", side_effect=fake_new_property)
@@ -24,7 +24,7 @@ def test_should_run(mock_measurement, *args):
24
24
  # with country @id => run
25
25
  cycle['site'] = {'country': {'@id': 'GADM-AUS'}}
26
26
  should_run, *args = _should_run(cycle)
27
- assert should_run
27
+ assert should_run is True
28
28
 
29
29
 
30
30
  @patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
@@ -11,11 +11,17 @@ fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
11
11
  @patch(f"{class_path}.should_download", return_value=True)
12
12
  @patch(f"{class_path}.has_geospatial_data")
13
13
  def test_should_run(mock_has_geospatial_data, *args):
14
- mock_has_geospatial_data.return_value = True
15
- assert _should_run({}) is True
14
+ site = {}
16
15
 
17
16
  mock_has_geospatial_data.return_value = False
18
- assert not _should_run({})
17
+ assert not _should_run(site)
18
+
19
+ mock_has_geospatial_data.return_value = True
20
+ assert _should_run(site) is True
21
+
22
+ # with other texture measurements => not run
23
+ site['measurements'] = [{'term': {'@id': 'clayContent'}}]
24
+ assert not _should_run(site)
19
25
 
20
26
 
21
27
  @patch(f"{class_path}.get_source", return_value={})
@@ -11,11 +11,17 @@ fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
11
11
  @patch(f"{class_path}.should_download", return_value=True)
12
12
  @patch(f"{class_path}.has_geospatial_data")
13
13
  def test_should_run(mock_has_geospatial_data, *args):
14
- mock_has_geospatial_data.return_value = True
15
- assert _should_run({}) is True
14
+ site = {}
16
15
 
17
16
  mock_has_geospatial_data.return_value = False
18
- assert not _should_run({})
17
+ assert not _should_run(site)
18
+
19
+ mock_has_geospatial_data.return_value = True
20
+ assert _should_run(site) is True
21
+
22
+ # with other texture measurements => not run
23
+ site['measurements'] = [{'term': {'@id': 'clayContent'}}]
24
+ assert not _should_run(site)
19
25
 
20
26
 
21
27
  @patch(f"{class_path}.get_source", return_value={})
@@ -10,11 +10,16 @@ fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
10
10
 
11
11
 
12
12
  def test_should_run():
13
- # no inputs => no run
14
- cycle = {'completeness': {'excreta': True}, 'inputs': []}
13
+ # no inputs not complete => no run
14
+ cycle = {'completeness': {'excreta': False}, 'inputs': []}
15
15
  should_run, *args = _should_run(cycle)
16
16
  assert not should_run
17
17
 
18
+ # complete => run
19
+ cycle['completeness'] = {'excreta': True}
20
+ should_run, *args = _should_run(cycle)
21
+ assert should_run is True
22
+
18
23
  # with kg N inputs => run
19
24
  cycle['inputs'] = [{
20
25
  'term': {
@@ -10,7 +10,7 @@ fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
10
10
 
11
11
  def test_should_run():
12
12
  # no emissions => no run
13
- cycle = {'emissions': []}
13
+ cycle = {'completeness': {'excreta': True}, 'emissions': []}
14
14
  should_run, *args = _should_run(cycle)
15
15
  assert not should_run
16
16
 
@@ -9,11 +9,16 @@ fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
9
9
 
10
10
 
11
11
  def test_should_run():
12
- # no inputs => no run
13
- cycle = {'completeness': {'fertiliser': True}, 'inputs': []}
12
+ # no inputs not complete => no run
13
+ cycle = {'completeness': {'fertiliser': False}, 'inputs': []}
14
14
  should_run, *args = _should_run(cycle)
15
15
  assert not should_run
16
16
 
17
+ # complete => run
18
+ cycle['completeness'] = {'fertiliser': True}
19
+ should_run, *args = _should_run(cycle)
20
+ assert should_run is True
21
+
17
22
  # with kg N inputs => no run
18
23
  cycle['inputs'] = [{
19
24
  'term': {
@@ -9,11 +9,16 @@ fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
9
9
 
10
10
 
11
11
  def test_should_run():
12
- # no inputs => no run
13
- cycle = {'completeness': {'fertiliser': True}, 'inputs': []}
12
+ # no inputs not complete => no run
13
+ cycle = {'completeness': {'fertiliser': False}, 'inputs': []}
14
14
  should_run, *args = _should_run(cycle)
15
15
  assert not should_run
16
16
 
17
+ # complete => run
18
+ cycle['completeness'] = {'fertiliser': True}
19
+ should_run, *args = _should_run(cycle)
20
+ assert should_run is True
21
+
17
22
  # with kg N inputs => run
18
23
  cycle['inputs'] = [{
19
24
  'term': {
@@ -9,11 +9,16 @@ fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
9
9
 
10
10
 
11
11
  def test_should_run():
12
- # no inputs => no run
13
- cycle = {'completeness': {'fertiliser': True}, 'inputs': []}
12
+ # no inputs not complete => no run
13
+ cycle = {'completeness': {'fertiliser': False}, 'inputs': []}
14
14
  should_run, *args = _should_run(cycle)
15
15
  assert not should_run
16
16
 
17
+ # complete => run
18
+ cycle['completeness'] = {'fertiliser': True}
19
+ should_run, *args = _should_run(cycle)
20
+ assert should_run is True
21
+
17
22
  # with kg N inputs => no run
18
23
  cycle['inputs'] = [{
19
24
  'term': {
@@ -9,11 +9,16 @@ fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
9
9
 
10
10
 
11
11
  def test_should_run():
12
- # no inputs => no run
13
- cycle = {'completeness': {'fertiliser': True}, 'inputs': []}
12
+ # no inputs not complete => no run
13
+ cycle = {'completeness': {'fertiliser': False}, 'inputs': []}
14
14
  should_run, *args = _should_run(cycle)
15
15
  assert not should_run
16
16
 
17
+ # complete => run
18
+ cycle['completeness'] = {'fertiliser': True}
19
+ should_run, *args = _should_run(cycle)
20
+ assert should_run is True
21
+
17
22
  # with kg N inputs => run
18
23
  cycle['inputs'] = [{
19
24
  'term': {
@@ -14,7 +14,7 @@ fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
14
14
  @patch(f"{class_path}.get_total_value_converted_with_min_ratio", return_value=0)
15
15
  def test_should_run(mock_feed, mock_lookup_value, *args):
16
16
  cycle = {
17
- "completeness": {"animalFeed": True, "grazedForage": True},
17
+ "completeness": {"animalFeed": True, "freshForage": True},
18
18
  "inputs": [
19
19
  {
20
20
  "term": {
@@ -0,0 +1,228 @@
1
+ from functools import reduce
2
+ import json
3
+ from pytest import mark
4
+ from unittest.mock import patch
5
+
6
+ from hestia_earth.schema import MeasurementMethodClassification
7
+
8
+ from hestia_earth.models.ipcc2019.co2ToAirSoilCarbonStockChangeManagementChange import (
9
+ _calc_soc_stock_change,
10
+ _convert_c_to_co2,
11
+ _get_max_measurement_method,
12
+ _get_min_measurement_method,
13
+ _linear_interpolate_soc_stock,
14
+ _nodes_to_soc_stock,
15
+ MODEL,
16
+ run,
17
+ SocStock,
18
+ TERM_ID
19
+ )
20
+
21
+ from tests.utils import (
22
+ fake_new_emission,
23
+ fixtures_path
24
+ )
25
+
26
+ class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
27
+ fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
28
+
29
+ RUN_SCENARIOS = [
30
+ ("no-overlapping-cycles", 3),
31
+ ("overlapping-cycles", 4),
32
+ ("complex-overlapping-cycles", 5),
33
+ ("missing-measurement-dates", 3),
34
+ ("no-organic-carbon-measurements", 1) # Closes issue #700
35
+ ]
36
+ """List of (subfolder: str, num_cycles: int)."""
37
+
38
+ RUN_PARAMS = reduce(
39
+ lambda params, scenario: params + [(scenario[0], scenario[1], i) for i in range(scenario[1])],
40
+ RUN_SCENARIOS,
41
+ list()
42
+ )
43
+ """List of (subfolder: str, num_cycles: int, cycle_index: int)."""
44
+
45
+ RUN_IDS = [f"{param[0]}, cycle{param[2]}" for param in RUN_PARAMS]
46
+
47
+
48
+ def _load_fixture(path: str):
49
+ with open(path, encoding="utf-8") as f:
50
+ fixture = json.load(f)
51
+ return fixture
52
+
53
+
54
+ def _get_site_path(subfolder: str) -> str:
55
+ return f"{fixtures_folder}/{subfolder}/site.jsonld"
56
+
57
+
58
+ def _get_cycle_path(subfolder: str, index: int) -> str:
59
+ return f"{fixtures_folder}/{subfolder}/cycle{index}.jsonld"
60
+
61
+
62
+ def _get_result_path(subfolder: str, index: int) -> str:
63
+ return f"{fixtures_folder}/{subfolder}/result{index}.jsonld"
64
+
65
+
66
+ def test_calc_soc_stock_change():
67
+ START_SOC_STOCK = SocStock(20000, MeasurementMethodClassification.ON_SITE_PHYSICAL_MEASUREMENT)
68
+ END_SOC_STOCK = SocStock(21000, MeasurementMethodClassification.TIER_1_MODEL)
69
+ EXPECTED = SocStock(1000, MeasurementMethodClassification.TIER_1_MODEL)
70
+
71
+ result = _calc_soc_stock_change(START_SOC_STOCK, END_SOC_STOCK)
72
+ assert result == EXPECTED
73
+
74
+
75
+ def test_convert_c_to_co2():
76
+ KG_C = 1000
77
+ EXPECTED = 3663.836163836164
78
+
79
+ assert _convert_c_to_co2(KG_C) == EXPECTED
80
+
81
+
82
+ def test_get_max_measurement_method():
83
+ EXPECTED = MeasurementMethodClassification.ON_SITE_PHYSICAL_MEASUREMENT
84
+ result = _get_max_measurement_method(
85
+ MeasurementMethodClassification.ON_SITE_PHYSICAL_MEASUREMENT,
86
+ MeasurementMethodClassification.TIER_2_MODEL,
87
+ MeasurementMethodClassification.TIER_1_MODEL
88
+ )
89
+ assert result == EXPECTED
90
+
91
+
92
+ def test_get_max_measurement_method_list():
93
+ EXPECTED = MeasurementMethodClassification.ON_SITE_PHYSICAL_MEASUREMENT
94
+ METHODS = [
95
+ MeasurementMethodClassification.ON_SITE_PHYSICAL_MEASUREMENT,
96
+ MeasurementMethodClassification.TIER_2_MODEL,
97
+ MeasurementMethodClassification.TIER_1_MODEL
98
+ ]
99
+ result = _get_max_measurement_method(METHODS)
100
+ assert result == EXPECTED
101
+
102
+
103
+ def test_get_max_measurement_method_no_methods():
104
+ EXPECTED = MeasurementMethodClassification.ON_SITE_PHYSICAL_MEASUREMENT
105
+ result = _get_max_measurement_method()
106
+ assert result == EXPECTED
107
+
108
+
109
+ def test_get_min_measurement_method():
110
+ EXPECTED = MeasurementMethodClassification.TIER_1_MODEL
111
+ result = _get_min_measurement_method(
112
+ MeasurementMethodClassification.ON_SITE_PHYSICAL_MEASUREMENT,
113
+ MeasurementMethodClassification.TIER_2_MODEL,
114
+ MeasurementMethodClassification.TIER_1_MODEL
115
+ )
116
+ assert result == EXPECTED
117
+
118
+
119
+ def test_get_min_measurement_method_list():
120
+ EXPECTED = MeasurementMethodClassification.TIER_1_MODEL
121
+ METHODS = [
122
+ MeasurementMethodClassification.ON_SITE_PHYSICAL_MEASUREMENT,
123
+ MeasurementMethodClassification.TIER_2_MODEL,
124
+ MeasurementMethodClassification.TIER_1_MODEL
125
+ ]
126
+ result = _get_min_measurement_method(METHODS)
127
+ assert result == EXPECTED
128
+
129
+
130
+ def test_get_min_measurement_method_no_methods():
131
+ EXPECTED = MeasurementMethodClassification.UNSOURCED_ASSUMPTION
132
+ result = _get_min_measurement_method()
133
+ assert result == EXPECTED
134
+
135
+
136
+ def test_linear_interpolate_soc_stock():
137
+ START_YEAR = 2000
138
+ END_YEAR = 2002
139
+ START_SOC_STOCK = SocStock(20000, MeasurementMethodClassification.ON_SITE_PHYSICAL_MEASUREMENT)
140
+ END_SOC_STOCK = SocStock(22000, MeasurementMethodClassification.ON_SITE_PHYSICAL_MEASUREMENT)
141
+ TARGET_YEAR = 2001
142
+ EXPECTED = SocStock(21000, MeasurementMethodClassification.TIER_1_MODEL)
143
+
144
+ result = _linear_interpolate_soc_stock(
145
+ START_YEAR, END_YEAR, START_SOC_STOCK, END_SOC_STOCK, TARGET_YEAR
146
+ )
147
+ assert result == EXPECTED
148
+
149
+
150
+ def test_nodes_to_soc_stock_diff_dates():
151
+ NODES = [
152
+ {
153
+ "value": [1000],
154
+ "dates": ["2020"],
155
+ "methodClassification": "modelled using other measurements"
156
+ },
157
+ {
158
+ "value": [12000],
159
+ "dates": ["2020-12-31"],
160
+ "methodClassification": "tier 1 model"
161
+ }
162
+ ]
163
+ EXPECTED = SocStock(12000, MeasurementMethodClassification.TIER_1_MODEL)
164
+
165
+ assert _nodes_to_soc_stock(2020, NODES) == EXPECTED
166
+
167
+
168
+ def test_nodes_to_soc_stock_same_dates():
169
+ NODES = [
170
+ {
171
+ "value": [10000],
172
+ "dates": ["2020-12-31"],
173
+ "methodClassification": "modelled using other measurements"
174
+ },
175
+ {
176
+ "value": [12000],
177
+ "dates": ["2020-12-31"],
178
+ "methodClassification": "tier 1 model"
179
+ },
180
+ {
181
+ "value": [11000],
182
+ "dates": ["2020-06-01"],
183
+ "methodClassification": "on-site physical measurement"
184
+ }
185
+ ]
186
+ EXPECTED = SocStock(10000, MeasurementMethodClassification.MODELLED_USING_OTHER_MEASUREMENTS)
187
+
188
+ assert _nodes_to_soc_stock(2020, NODES) == EXPECTED
189
+
190
+
191
+ @mark.parametrize("subfolder, num_cycles, cycle_index", RUN_PARAMS, ids=RUN_IDS)
192
+ @patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
193
+ @patch(f"{class_path}.related_cycles")
194
+ @patch(f"{class_path}.get_site")
195
+ def test_run(_get_site_mock, related_cycles_mock, _new_emission_mock, subfolder, num_cycles, cycle_index):
196
+ """
197
+ Test `run` function for each cycle in each scenario.
198
+ """
199
+ site = _load_fixture(_get_site_path(subfolder))
200
+ cycle = _load_fixture(_get_cycle_path(subfolder, cycle_index))
201
+ expected = _load_fixture(_get_result_path(subfolder, cycle_index))
202
+
203
+ cycles = [
204
+ _load_fixture(_get_cycle_path(subfolder, i)) for i in range(num_cycles)
205
+ ]
206
+
207
+ _get_site_mock.return_value = site
208
+ related_cycles_mock.return_value = cycles
209
+
210
+ result = run(cycle)
211
+ assert result == expected
212
+
213
+
214
+ @patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
215
+ @patch(f"{class_path}.related_cycles")
216
+ @patch(f"{class_path}.get_site")
217
+ def test_run_empty(_get_site_mock, related_cycles_mock, _new_emission_mock):
218
+ """
219
+ Test `run` function for each cycle in each scenario.
220
+ """
221
+ CYCLE = {}
222
+ EXPECTED = []
223
+
224
+ _get_site_mock.return_value = {}
225
+ related_cycles_mock.return_value = [CYCLE]
226
+
227
+ result = run(CYCLE)
228
+ assert result == EXPECTED
@@ -0,0 +1,74 @@
1
+ from unittest.mock import patch
2
+ import json
3
+ from tests.utils import fixtures_path, fake_new_emission
4
+
5
+ from hestia_earth.models.ipcc2019.n2OToAirInorganicFertiliserDirect import MODEL, TERM_ID, run, _should_run
6
+
7
+ class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
8
+ fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
9
+
10
+
11
+ @patch(f"{class_path}._is_term_type_complete", return_value=False)
12
+ @patch(f"{class_path}.get_inorganic_fertiliser_N_total", return_value=0)
13
+ def test_should_run(mock_N_total, mock_complete):
14
+ # no N => no run
15
+ should_run, *args = _should_run({})
16
+ assert not should_run
17
+
18
+ # with N => no run
19
+ mock_N_total.return_value = 10
20
+ should_run, *args = _should_run({})
21
+ assert not should_run
22
+
23
+ # is complete => run
24
+ mock_complete.return_value = True
25
+ should_run, *args = _should_run({})
26
+ assert should_run is True
27
+
28
+
29
+ @patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
30
+ def test_run(*args):
31
+ with open(f"{fixtures_folder}/cycle.jsonld", encoding='utf-8') as f:
32
+ cycle = json.load(f)
33
+
34
+ with open(f"{fixtures_folder}/result.jsonld", encoding='utf-8') as f:
35
+ expected = json.load(f)
36
+
37
+ value = run(cycle)
38
+ assert value == expected
39
+
40
+
41
+ @patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
42
+ def test_run_wet(*args):
43
+ with open(f"{fixtures_folder}/ecoClimateZone-wet/cycle.jsonld", encoding='utf-8') as f:
44
+ cycle = json.load(f)
45
+
46
+ with open(f"{fixtures_folder}/ecoClimateZone-wet/result.jsonld", encoding='utf-8') as f:
47
+ expected = json.load(f)
48
+
49
+ value = run(cycle)
50
+ assert value == expected
51
+
52
+
53
+ @patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
54
+ def test_run_dry(*args):
55
+ with open(f"{fixtures_folder}/ecoClimateZone-dry/cycle.jsonld", encoding='utf-8') as f:
56
+ cycle = json.load(f)
57
+
58
+ with open(f"{fixtures_folder}/ecoClimateZone-dry/result.jsonld", encoding='utf-8') as f:
59
+ expected = json.load(f)
60
+
61
+ value = run(cycle)
62
+ assert value == expected
63
+
64
+
65
+ @patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
66
+ def test_run_flooded_rice(*args):
67
+ with open(f"{fixtures_folder}/with-flooded-rice/cycle.jsonld", encoding='utf-8') as f:
68
+ cycle = json.load(f)
69
+
70
+ with open(f"{fixtures_folder}/with-flooded-rice/result.jsonld", encoding='utf-8') as f:
71
+ expected = json.load(f)
72
+
73
+ value = run(cycle)
74
+ assert value == expected
@@ -0,0 +1,74 @@
1
+ from unittest.mock import patch
2
+ import json
3
+ from tests.utils import fixtures_path, fake_new_emission
4
+
5
+ from hestia_earth.models.ipcc2019.n2OToAirOrganicFertiliserDirect import MODEL, TERM_ID, run, _should_run
6
+
7
+ class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
8
+ fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
9
+
10
+
11
+ @patch(f"{class_path}._is_term_type_complete", return_value=False)
12
+ @patch(f"{class_path}.get_organic_fertiliser_N_total", return_value=0)
13
+ def test_should_run(mock_N_total, mock_complete):
14
+ # no N => no run
15
+ should_run, *args = _should_run({})
16
+ assert not should_run
17
+
18
+ # with N => no run
19
+ mock_N_total.return_value = 10
20
+ should_run, *args = _should_run({})
21
+ assert not should_run
22
+
23
+ # is complete => run
24
+ mock_complete.return_value = True
25
+ should_run, *args = _should_run({})
26
+ assert should_run is True
27
+
28
+
29
+ @patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
30
+ def test_run(*args):
31
+ with open(f"{fixtures_folder}/cycle.jsonld", encoding='utf-8') as f:
32
+ cycle = json.load(f)
33
+
34
+ with open(f"{fixtures_folder}/result.jsonld", encoding='utf-8') as f:
35
+ expected = json.load(f)
36
+
37
+ value = run(cycle)
38
+ assert value == expected
39
+
40
+
41
+ @patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
42
+ def test_run_wet(*args):
43
+ with open(f"{fixtures_folder}/ecoClimateZone-wet/cycle.jsonld", encoding='utf-8') as f:
44
+ cycle = json.load(f)
45
+
46
+ with open(f"{fixtures_folder}/ecoClimateZone-wet/result.jsonld", encoding='utf-8') as f:
47
+ expected = json.load(f)
48
+
49
+ value = run(cycle)
50
+ assert value == expected
51
+
52
+
53
+ @patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
54
+ def test_run_dry(*args):
55
+ with open(f"{fixtures_folder}/ecoClimateZone-dry/cycle.jsonld", encoding='utf-8') as f:
56
+ cycle = json.load(f)
57
+
58
+ with open(f"{fixtures_folder}/ecoClimateZone-dry/result.jsonld", encoding='utf-8') as f:
59
+ expected = json.load(f)
60
+
61
+ value = run(cycle)
62
+ assert value == expected
63
+
64
+
65
+ @patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
66
+ def test_run_flooded_rice(*args):
67
+ with open(f"{fixtures_folder}/with-flooded-rice/cycle.jsonld", encoding='utf-8') as f:
68
+ cycle = json.load(f)
69
+
70
+ with open(f"{fixtures_folder}/with-flooded-rice/result.jsonld", encoding='utf-8') as f:
71
+ expected = json.load(f)
72
+
73
+ value = run(cycle)
74
+ assert value == expected