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.
- hestia_earth/models/cycle/aboveGroundCropResidueTotal.py +17 -12
- hestia_earth/models/cycle/excretaKgMass.py +4 -5
- hestia_earth/models/cycle/excretaKgN.py +4 -5
- hestia_earth/models/cycle/excretaKgVs.py +4 -5
- hestia_earth/models/cycle/inorganicFertiliser.py +2 -2
- hestia_earth/models/cycle/{irrigated.py → irrigatedTypeUnspecified.py} +4 -4
- hestia_earth/models/cycle/liveAnimal.py +9 -11
- hestia_earth/models/cycle/milkYield.py +154 -0
- hestia_earth/models/cycle/residueIncorporated.py +1 -1
- hestia_earth/models/cycle/utils.py +6 -0
- hestia_earth/models/emepEea2019/nh3ToAirInorganicFertiliser.py +3 -3
- hestia_earth/models/faostat2018/seed.py +2 -3
- hestia_earth/models/geospatialDatabase/clayContent.py +17 -4
- hestia_earth/models/geospatialDatabase/sandContent.py +17 -4
- hestia_earth/models/geospatialDatabase/siltContent.py +2 -2
- hestia_earth/models/impact_assessment/irrigated.py +0 -3
- hestia_earth/models/ipcc2006/co2ToAirOrganicSoilCultivation.py +2 -2
- hestia_earth/models/ipcc2006/n2OToAirCropResidueDecompositionIndirect.py +2 -2
- hestia_earth/models/ipcc2006/n2OToAirExcretaDirect.py +1 -1
- hestia_earth/models/ipcc2006/n2OToAirExcretaIndirect.py +8 -4
- hestia_earth/models/ipcc2006/n2OToAirInorganicFertiliserDirect.py +4 -1
- hestia_earth/models/ipcc2006/n2OToAirInorganicFertiliserIndirect.py +1 -1
- hestia_earth/models/ipcc2006/n2OToAirOrganicFertiliserDirect.py +1 -1
- hestia_earth/models/ipcc2006/n2OToAirOrganicFertiliserIndirect.py +1 -1
- hestia_earth/models/ipcc2006/utils.py +11 -8
- hestia_earth/models/ipcc2019/ch4ToAirEntericFermentation.py +4 -4
- hestia_earth/models/ipcc2019/ch4ToAirFloodedRice.py +16 -7
- hestia_earth/models/ipcc2019/co2ToAirSoilCarbonStockChangeManagementChange.py +759 -0
- hestia_earth/models/ipcc2019/croppingDuration.py +12 -6
- hestia_earth/models/ipcc2019/n2OToAirCropResidueDecompositionDirect.py +5 -52
- hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserDirect.py +104 -0
- hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserIndirect.py +1 -1
- hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserDirect.py +105 -0
- hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserIndirect.py +1 -1
- hestia_earth/models/ipcc2019/no3ToGroundwaterCropResidueDecomposition.py +1 -1
- hestia_earth/models/ipcc2019/no3ToGroundwaterExcreta.py +1 -1
- hestia_earth/models/ipcc2019/no3ToGroundwaterInorganicFertiliser.py +1 -1
- hestia_earth/models/ipcc2019/no3ToGroundwaterOrganicFertiliser.py +1 -1
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +1088 -1268
- hestia_earth/models/ipcc2019/pastureGrass.py +4 -4
- hestia_earth/models/ipcc2019/utils.py +102 -1
- hestia_earth/models/koble2014/aboveGroundCropResidue.py +15 -17
- hestia_earth/models/koble2014/cropResidueManagement.py +2 -2
- hestia_earth/models/koble2014/utils.py +19 -3
- hestia_earth/models/linkedImpactAssessment/__init__.py +4 -2
- hestia_earth/models/log.py +15 -3
- hestia_earth/models/mocking/search-results.json +184 -118
- hestia_earth/models/pooreNemecek2018/excretaKgN.py +6 -7
- hestia_earth/models/pooreNemecek2018/excretaKgVs.py +7 -6
- hestia_earth/models/pooreNemecek2018/no3ToGroundwaterCropResidueDecomposition.py +3 -2
- hestia_earth/models/pooreNemecek2018/no3ToGroundwaterExcreta.py +3 -2
- hestia_earth/models/pooreNemecek2018/no3ToGroundwaterInorganicFertiliser.py +3 -2
- hestia_earth/models/pooreNemecek2018/saplings.py +0 -1
- hestia_earth/models/site/management.py +168 -0
- hestia_earth/models/site/organicCarbonPerHa.py +251 -89
- hestia_earth/models/stehfestBouwman2006/n2OToAirCropResidueDecompositionDirect.py +3 -2
- hestia_earth/models/stehfestBouwman2006/n2OToAirExcretaDirect.py +3 -2
- hestia_earth/models/stehfestBouwman2006/n2OToAirInorganicFertiliserDirect.py +3 -2
- hestia_earth/models/stehfestBouwman2006/n2OToAirOrganicFertiliserDirect.py +3 -2
- hestia_earth/models/stehfestBouwman2006/noxToAirCropResidueDecomposition.py +3 -2
- hestia_earth/models/stehfestBouwman2006/noxToAirExcreta.py +3 -2
- hestia_earth/models/stehfestBouwman2006/noxToAirInorganicFertiliser.py +3 -2
- hestia_earth/models/stehfestBouwman2006/noxToAirOrganicFertiliser.py +3 -2
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirCropResidueDecomposition.py +3 -2
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirExcreta.py +3 -2
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirInorganicFertiliser.py +3 -2
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirOrganicFertiliser.py +3 -2
- hestia_earth/models/utils/aggregated.py +1 -0
- hestia_earth/models/utils/blank_node.py +394 -72
- hestia_earth/models/utils/cropResidue.py +13 -0
- hestia_earth/models/utils/cycle.py +18 -9
- hestia_earth/models/utils/measurement.py +1 -1
- hestia_earth/models/utils/property.py +4 -4
- hestia_earth/models/utils/term.py +48 -3
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.57.2.dist-info → hestia_earth_models-0.59.0.dist-info}/METADATA +5 -9
- {hestia_earth_models-0.57.2.dist-info → hestia_earth_models-0.59.0.dist-info}/RECORD +109 -97
- {hestia_earth_models-0.57.2.dist-info → hestia_earth_models-0.59.0.dist-info}/WHEEL +1 -1
- tests/models/cycle/animal/input/test_hestiaAggregatedData.py +2 -14
- tests/models/cycle/input/test_hestiaAggregatedData.py +4 -16
- tests/models/cycle/test_coldCarcassWeightPerHead.py +1 -1
- tests/models/cycle/test_coldDressedCarcassWeightPerHead.py +1 -1
- tests/models/cycle/{test_irrigated.py → test_irrigatedTypeUnspecified.py} +1 -1
- tests/models/cycle/test_milkYield.py +58 -0
- tests/models/cycle/test_readyToCookWeightPerHead.py +1 -1
- tests/models/emepEea2019/test_nh3ToAirInorganicFertiliser.py +1 -1
- tests/models/geospatialDatabase/test_clayContent.py +9 -3
- tests/models/geospatialDatabase/test_sandContent.py +9 -3
- tests/models/ipcc2006/test_n2OToAirExcretaDirect.py +7 -2
- tests/models/ipcc2006/test_n2OToAirExcretaIndirect.py +1 -1
- tests/models/ipcc2006/test_n2OToAirInorganicFertiliserDirect.py +7 -2
- tests/models/ipcc2006/test_n2OToAirInorganicFertiliserIndirect.py +7 -2
- tests/models/ipcc2006/test_n2OToAirOrganicFertiliserDirect.py +7 -2
- tests/models/ipcc2006/test_n2OToAirOrganicFertiliserIndirect.py +7 -2
- tests/models/ipcc2019/test_ch4ToAirEntericFermentation.py +1 -1
- tests/models/ipcc2019/test_co2ToAirSoilCarbonStockChangeManagementChange.py +228 -0
- tests/models/ipcc2019/test_n2OToAirInorganicFertiliserDirect.py +74 -0
- tests/models/ipcc2019/test_n2OToAirOrganicFertiliserDirect.py +74 -0
- tests/models/ipcc2019/test_organicCarbonPerHa.py +303 -1044
- tests/models/koble2014/test_residueBurnt.py +1 -2
- tests/models/koble2014/test_residueLeftOnField.py +1 -2
- tests/models/koble2014/test_residueRemoved.py +1 -2
- tests/models/koble2014/test_utils.py +52 -0
- tests/models/site/test_management.py +117 -0
- tests/models/site/test_organicCarbonPerHa.py +51 -5
- tests/models/utils/test_blank_node.py +230 -34
- tests/models/utils/test_term.py +17 -3
- {hestia_earth_models-0.57.2.dist-info → hestia_earth_models-0.59.0.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.57.2.dist-info → hestia_earth_models-0.59.0.dist-info}/top_level.txt +0 -0
|
@@ -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.
|
|
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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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':
|
|
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':
|
|
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':
|
|
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':
|
|
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':
|
|
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, "
|
|
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
|