From 663277d915acd332824a8ac08c5464c22a2d615c Mon Sep 17 00:00:00 2001 From: Niko <70217952+ew3361zh@users.noreply.github.com> Date: Fri, 11 Sep 2026 18:24:58 -0400 Subject: [PATCH 1/8] updates bedded pack mcf table --- .../biophysical/manure/storage/bedded_pack.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/RUFAS/biophysical/manure/storage/bedded_pack.py b/RUFAS/biophysical/manure/storage/bedded_pack.py index bad668b4bf..8e89da1851 100644 --- a/RUFAS/biophysical/manure/storage/bedded_pack.py +++ b/RUFAS/biophysical/manure/storage/bedded_pack.py @@ -23,18 +23,16 @@ class Mixing(Enum): BEDDED_PACK_MCF_TABLE: dict[Mixing, dict[tuple[float, float], float]] = { Mixing.MIXED: { - (-math.inf, 4.6): 0.5, - (4.6, 5.8): 0.5, - (5.8, 13.9): 1.0, - (13.9, 25.1): 1.0, - (25.1, math.inf): 1.5, + (-math.inf, 0): 0.5, + (0, 10): 0.5, + (10, 18): 1, + (18, math.inf): 1.5, }, Mixing.UNMIXED: { - (-math.inf, 4.6): 21.0, - (4.6, 5.8): 26.0, - (5.8, 13.9): 37.0, - (13.9, 25.1): 41.0, - (25.1, math.inf): 74.0, + (-math.inf, 0): 14.0, + (0, 10): 21.0, + (10, 18): 37.0, + (18, math.inf): 73.0, }, } From 550d62b2bfd3c774625e4bb70f3f1cb6ccd140a1 Mon Sep 17 00:00:00 2001 From: Niko <70217952+ew3361zh@users.noreply.github.com> Date: Mon, 14 Sep 2026 10:05:44 -0400 Subject: [PATCH 2/8] adds lookup table for mcf bedded pack --- .../biophysical/manure/storage/bedded_pack.py | 53 +++++++++++------- .../test_storage/test_bedded_pack.py | 54 +++++++++++-------- 2 files changed, 67 insertions(+), 40 deletions(-) diff --git a/RUFAS/biophysical/manure/storage/bedded_pack.py b/RUFAS/biophysical/manure/storage/bedded_pack.py index 8e89da1851..14e080a5fd 100644 --- a/RUFAS/biophysical/manure/storage/bedded_pack.py +++ b/RUFAS/biophysical/manure/storage/bedded_pack.py @@ -20,22 +20,26 @@ class Mixing(Enum): MIXED = True UNMIXED = False +BEDDED_PACK_MCF_MIXED: dict[tuple[float, float], float] = { + (-math.inf, 0): 0.5, + (0, 10): 0.5, + (10, 18): 1.0, + (18, math.inf): 1.5, +} -BEDDED_PACK_MCF_TABLE: dict[Mixing, dict[tuple[float, float], float]] = { - Mixing.MIXED: { - (-math.inf, 0): 0.5, - (0, 10): 0.5, - (10, 18): 1, - (18, math.inf): 1.5, - }, - Mixing.UNMIXED: { - (-math.inf, 0): 14.0, - (0, 10): 21.0, - (10, 18): 37.0, - (18, math.inf): 73.0, - }, +BEDDED_PACK_MCF_UNMIXED_SHORT: dict[tuple[float, float], float] = { + (-math.inf, 0): 2.75, + (0, 10): 2.75, + (10, 18): 6.5, + (18, math.inf): 18.0, } +BEDDED_PACK_MCF_UNMIXED_LONG: dict[tuple[float, float], float] = { + (-math.inf, 0): 14.0, + (0, 10): 21.0, + (10, 18): 37.0, + (18, math.inf): 73.0, +} class BeddedPack(Storage): """ @@ -107,6 +111,7 @@ def process_manure(self, current_day_conditions: CurrentDayConditions, time: Ruf + self._manure_to_process.non_degradable_volatile_solids, self._determine_barn_temperature(manure_annual_temperature), self._manure_to_process.methane_production_potential, + self._storage_time_period ) else: storage_methane = 0 @@ -374,7 +379,8 @@ def _calculate_bedded_pack_ammonia_emission(received_nitrogen: float, is_mixed: @staticmethod def calculate_bedded_pack_methane_emission( - is_mixed: bool, manure_volatile_solids: float, manure_temperature: float, methane_production_potential: float + is_mixed: bool, manure_volatile_solids: float, manure_temperature: float, methane_production_potential: float, + storage_time_period: int | None ) -> float: """ Calculates emission of methane on the current day based on methodology from IPCC 2019 @@ -390,6 +396,7 @@ def calculate_bedded_pack_methane_emission( The annual average temperature of the barn (Celsius). methane_production_potential : float Achievable emission of methane from dairy manure (m^3 methane / kg volatile solids). + storage_time_period : int | None Raises ------ @@ -414,7 +421,7 @@ def calculate_bedded_pack_methane_emission( raise ValueError(f"Manure volatile solids mass must be positive. Received {manure_volatile_solids}.") Bo = methane_production_potential methane_conversion_factor = BeddedPack.calculate_bedded_pack_methane_conversion_factor( - is_mixed, manure_temperature + is_mixed, manure_temperature, storage_time_period ) methane_emissions_in_kg = ( manure_volatile_solids * Bo * UserConstants.METHANE_FACTOR * methane_conversion_factor @@ -422,7 +429,8 @@ def calculate_bedded_pack_methane_emission( return methane_emissions_in_kg @staticmethod - def calculate_bedded_pack_methane_conversion_factor(is_mixed: bool, manure_temperature: float) -> float: + def calculate_bedded_pack_methane_conversion_factor(is_mixed: bool, manure_temperature: float, + storage_time_period: int | None) -> float: """ Calculates the Methane Conversion Factor (MCF) for the bedded pack based on annual temperature and whether or not the bedded pack is mixed. @@ -433,6 +441,8 @@ def calculate_bedded_pack_methane_conversion_factor(is_mixed: bool, manure_tempe Indicates whether this bedded pack is mixed or not. manure_temperature : float The annual average temperature of the barn (Celsius). + storage_time_period : int | None + How long manure is stored for before emptying the storage (days). None if the storage is never emptied. Raises ------ @@ -449,10 +459,17 @@ def calculate_bedded_pack_methane_conversion_factor(is_mixed: bool, manure_tempe 2024 USDA GHG inventory methods table 4-9. """ - mix = Mixing.MIXED if is_mixed else Mixing.UNMIXED - for (lower_bound, upper_bound), mcf in BEDDED_PACK_MCF_TABLE[mix].items(): + if is_mixed: + mcf_table = BEDDED_PACK_MCF_MIXED + elif storage_time_period is not None and storage_time_period < 30: + mcf_table = BEDDED_PACK_MCF_UNMIXED_SHORT + else: + mcf_table = BEDDED_PACK_MCF_UNMIXED_LONG + + for (lower_bound, upper_bound), mcf in mcf_table.items(): if lower_bound < manure_temperature <= upper_bound: return mcf + OutputManager().add_error( "BeddedPack manure temp error", f"Temperature {manure_temperature}°C out of any defined bin", diff --git a/tests/test_biophysical/test_manure/test_storage/test_bedded_pack.py b/tests/test_biophysical/test_manure/test_storage/test_bedded_pack.py index 252991a4de..d2e6c17e44 100644 --- a/tests/test_biophysical/test_manure/test_storage/test_bedded_pack.py +++ b/tests/test_biophysical/test_manure/test_storage/test_bedded_pack.py @@ -352,33 +352,42 @@ def test_calculate_bedded_pack_methane_emission(bedded_pack: BeddedPack, mocker: manure_volatile_solids = 1000.0 expected = (manure_volatile_solids * 0.24 * 0.67 * 1.0) / 100 - actual = bedded_pack.calculate_bedded_pack_methane_emission(True, manure_volatile_solids, 1.0, 0.24) + actual = bedded_pack.calculate_bedded_pack_methane_emission(True, manure_volatile_solids, 1.0, 0.24, 31) - mock_conversion_factor.assert_called_once_with(True, 1.0) + mock_conversion_factor.assert_called_once_with(True, 1.0, 31) assert actual == pytest.approx(expected) @pytest.mark.parametrize( - "is_mixed, manure_temperature, expected_mcf", + "is_mixed, manure_temperature, expected_mcf, storage_duration", [ # mixed - (True, -10.0, 0.5), # Falls in (-inf, 4.6] - (True, 0.0, 0.5), # “ - (True, 4.6, 0.5), # upper bound bin 1 - (True, 4.7, 0.5), # lower bound bin 2 - (True, 5.8, 0.5), # upper bound bin 2 (first match) - (True, 10.0, 1.0), # middle bin 3 - (True, 14.0, 1.0), # lower bound bin 4 - (True, 25.2, 1.5), # lower bound bin 5 - # unmixed - (False, -10.0, 21.0), - (False, 0.0, 21.0), - (False, 4.6, 21.0), - (False, 4.7, 26.0), - (False, 5.8, 26.0), - (False, 10.0, 37.0), - (False, 14.0, 41.0), - (False, 25.2, 74.0), + (True, -10.0, 0.5, 30), # Falls in (-inf, 4.6] + (True, 0.0, 0.5, 30), # “ + (True, 4.6, 0.5, 30), # upper bound bin 1 + (True, 4.7, 0.5, 30), # lower bound bin 2 + (True, 5.8, 0.5, 30), # upper bound bin 2 (first match) + (True, 10.0, 1.0, 30), # middle bin 3 + (True, 14.0, 1.0, 30), # lower bound bin 4 + (True, 25.2, 1.5, 30), # lower bound bin 5 + # unmixed, long storage duration + (False, -10.0, 21.0, 31), + (False, 0.0, 21.0, 31), + (False, 4.6, 21.0, 31), + (False, 4.7, 26.0, 31), + (False, 5.8, 26.0, None), + (False, 10.0, 37.0, None), + (False, 14.0, 41.0, None), + (False, 25.2, 74.0, None), + # unmixed, short storage duration + (False, -10.0, 21.0, 29), + (False, 0.0, 21.0, 29), + (False, 4.6, 21.0, 29), + (False, 4.7, 26.0, 29), + (False, 5.8, 26.0, 29), + (False, 10.0, 37.0, 29), + (False, 14.0, 41.0, 29), + (False, 25.2, 74.0, 29), ], ) def test_calculate_bedded_pack_mcf_returns_expected( @@ -386,9 +395,10 @@ def test_calculate_bedded_pack_mcf_returns_expected( is_mixed: bool, manure_temperature: float, expected_mcf: float, + storage_duration: int | None ) -> None: """Tests calculate_bedded_pack_mcf_returns_expected().""" - result = bedded_pack.calculate_bedded_pack_methane_conversion_factor(is_mixed, manure_temperature) + result = bedded_pack.calculate_bedded_pack_methane_conversion_factor(is_mixed, manure_temperature, storage_duration) assert result == expected_mcf @@ -396,7 +406,7 @@ def test_calculate_bedded_pack_mcf_raises_for_temperature_gap(bedded_pack: Bedde """Tests calculate_bedded_pack_mcf_returns_expected() for fall back cases.""" mock_add_error = mocker.patch.object(bedded_pack._om, "add_error") with pytest.raises(ValueError) as excinfo: - bedded_pack.calculate_bedded_pack_methane_conversion_factor(True, math.nan) + bedded_pack.calculate_bedded_pack_methane_conversion_factor(True, math.nan, 30) assert "out of any defined bin" in str(excinfo.value) mock_add_error.assert_called_once() From 3b2ce07c2dc3fd828bf33344408cd4dc2025a9e4 Mon Sep 17 00:00:00 2001 From: Niko <70217952+ew3361zh@users.noreply.github.com> Date: Mon, 14 Sep 2026 10:13:19 -0400 Subject: [PATCH 3/8] changelog --- changelog_WIP.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog_WIP.md b/changelog_WIP.md index 882ccd2dc7..467065fcbb 100644 --- a/changelog_WIP.md +++ b/changelog_WIP.md @@ -121,3 +121,4 @@ This **WIP Changelog** records development changes in progress and not yet inclu - [3235](https://github.com/RuminantFarmSystems/RuFaS/pull/3235) - [minor change] [Dependabot] [NoInputChange] [NoOutputChange] Updates file-target of dependabot-change PRs for tagging dev-team members for review. - [3256](https://github.com/RuminantFarmSystems/RuFaS/pull/3256) - [minor change] [Branch Alignment] [NoInputChange] [NoOutputChange] Aligning `dev` branch with bug-fixing code from PR 3214 that was merged into `test`. - [3260](https://github.com/RuminantFarmSystems/RuFaS/pull/3260) - [minor change] [OutputManager] [NoInputChange] [NoOutputChange] Removes duplicative `report` naming mechanism in `OutputManager`. +- [3285](https://github.com/RuminantFarmSystems/RuFaS/pull/3285) - [minor change] [Manure] [NoInputChange] [NoOutputChange] Adds MCF factor lookup table option for short-term storage duration `BeddedPack`. From c49e2cec1d9487232ebe9003ae4052319bfb9f19 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 14 Sep 2026 14:24:09 +0000 Subject: [PATCH 4/8] Apply Black Formatting --- RUFAS/biophysical/manure/storage/bedded_pack.py | 16 +++++++++++----- .../test_manure/test_storage/test_bedded_pack.py | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/RUFAS/biophysical/manure/storage/bedded_pack.py b/RUFAS/biophysical/manure/storage/bedded_pack.py index 14e080a5fd..04f509fcba 100644 --- a/RUFAS/biophysical/manure/storage/bedded_pack.py +++ b/RUFAS/biophysical/manure/storage/bedded_pack.py @@ -20,6 +20,7 @@ class Mixing(Enum): MIXED = True UNMIXED = False + BEDDED_PACK_MCF_MIXED: dict[tuple[float, float], float] = { (-math.inf, 0): 0.5, (0, 10): 0.5, @@ -41,6 +42,7 @@ class Mixing(Enum): (18, math.inf): 73.0, } + class BeddedPack(Storage): """ The BeddedPack storage class. @@ -111,7 +113,7 @@ def process_manure(self, current_day_conditions: CurrentDayConditions, time: Ruf + self._manure_to_process.non_degradable_volatile_solids, self._determine_barn_temperature(manure_annual_temperature), self._manure_to_process.methane_production_potential, - self._storage_time_period + self._storage_time_period, ) else: storage_methane = 0 @@ -379,8 +381,11 @@ def _calculate_bedded_pack_ammonia_emission(received_nitrogen: float, is_mixed: @staticmethod def calculate_bedded_pack_methane_emission( - is_mixed: bool, manure_volatile_solids: float, manure_temperature: float, methane_production_potential: float, - storage_time_period: int | None + is_mixed: bool, + manure_volatile_solids: float, + manure_temperature: float, + methane_production_potential: float, + storage_time_period: int | None, ) -> float: """ Calculates emission of methane on the current day based on methodology from IPCC 2019 @@ -429,8 +434,9 @@ def calculate_bedded_pack_methane_emission( return methane_emissions_in_kg @staticmethod - def calculate_bedded_pack_methane_conversion_factor(is_mixed: bool, manure_temperature: float, - storage_time_period: int | None) -> float: + def calculate_bedded_pack_methane_conversion_factor( + is_mixed: bool, manure_temperature: float, storage_time_period: int | None + ) -> float: """ Calculates the Methane Conversion Factor (MCF) for the bedded pack based on annual temperature and whether or not the bedded pack is mixed. diff --git a/tests/test_biophysical/test_manure/test_storage/test_bedded_pack.py b/tests/test_biophysical/test_manure/test_storage/test_bedded_pack.py index d2e6c17e44..a021405112 100644 --- a/tests/test_biophysical/test_manure/test_storage/test_bedded_pack.py +++ b/tests/test_biophysical/test_manure/test_storage/test_bedded_pack.py @@ -395,7 +395,7 @@ def test_calculate_bedded_pack_mcf_returns_expected( is_mixed: bool, manure_temperature: float, expected_mcf: float, - storage_duration: int | None + storage_duration: int | None, ) -> None: """Tests calculate_bedded_pack_mcf_returns_expected().""" result = bedded_pack.calculate_bedded_pack_methane_conversion_factor(is_mixed, manure_temperature, storage_duration) From d1dd59dc42f192e9f8ca94c8f753a6616990a9fa Mon Sep 17 00:00:00 2001 From: ew3361zh Date: Mon, 14 Sep 2026 14:28:39 +0000 Subject: [PATCH 5/8] Update badges on README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ed66b0c6ba..c58c0facf1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Flake8](https://img.shields.io/badge/Flake8-passed-brightgreen)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml) -[![Pytest](https://img.shields.io/badge/Pytest-passed-brightgreen)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml) -[![Coverage](https://img.shields.io/badge/Coverage-99%25-brightgreen)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml) +[![Pytest](https://img.shields.io/badge/Pytest-failed-red)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml) +[![Coverage](https://img.shields.io/badge/Coverage-%25-red)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml) [![Mypy](https://img.shields.io/badge/Mypy-1164%20errors-red)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml) From a193e88285fb555475631e87e233c83845c1533c Mon Sep 17 00:00:00 2001 From: Niko <70217952+ew3361zh@users.noreply.github.com> Date: Mon, 14 Sep 2026 11:43:46 -0400 Subject: [PATCH 6/8] fixes unit tests and updates names of lookup tables --- .../biophysical/manure/storage/bedded_pack.py | 8 ++--- .../test_storage/test_bedded_pack.py | 32 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/RUFAS/biophysical/manure/storage/bedded_pack.py b/RUFAS/biophysical/manure/storage/bedded_pack.py index 04f509fcba..11893fe5c2 100644 --- a/RUFAS/biophysical/manure/storage/bedded_pack.py +++ b/RUFAS/biophysical/manure/storage/bedded_pack.py @@ -28,14 +28,14 @@ class Mixing(Enum): (18, math.inf): 1.5, } -BEDDED_PACK_MCF_UNMIXED_SHORT: dict[tuple[float, float], float] = { +BEDDED_PACK_MCF_UNMIXED_UNDER_30_DAYS: dict[tuple[float, float], float] = { (-math.inf, 0): 2.75, (0, 10): 2.75, (10, 18): 6.5, (18, math.inf): 18.0, } -BEDDED_PACK_MCF_UNMIXED_LONG: dict[tuple[float, float], float] = { +BEDDED_PACK_MCF_UNMIXED_30_DAYS_OR_MORE: dict[tuple[float, float], float] = { (-math.inf, 0): 14.0, (0, 10): 21.0, (10, 18): 37.0, @@ -468,9 +468,9 @@ def calculate_bedded_pack_methane_conversion_factor( if is_mixed: mcf_table = BEDDED_PACK_MCF_MIXED elif storage_time_period is not None and storage_time_period < 30: - mcf_table = BEDDED_PACK_MCF_UNMIXED_SHORT + mcf_table = BEDDED_PACK_MCF_UNMIXED_UNDER_30_DAYS else: - mcf_table = BEDDED_PACK_MCF_UNMIXED_LONG + mcf_table = BEDDED_PACK_MCF_UNMIXED_30_DAYS_OR_MORE for (lower_bound, upper_bound), mcf in mcf_table.items(): if lower_bound < manure_temperature <= upper_bound: diff --git a/tests/test_biophysical/test_manure/test_storage/test_bedded_pack.py b/tests/test_biophysical/test_manure/test_storage/test_bedded_pack.py index a021405112..496e39f8cd 100644 --- a/tests/test_biophysical/test_manure/test_storage/test_bedded_pack.py +++ b/tests/test_biophysical/test_manure/test_storage/test_bedded_pack.py @@ -367,27 +367,27 @@ def test_calculate_bedded_pack_methane_emission(bedded_pack: BeddedPack, mocker: (True, 4.6, 0.5, 30), # upper bound bin 1 (True, 4.7, 0.5, 30), # lower bound bin 2 (True, 5.8, 0.5, 30), # upper bound bin 2 (first match) - (True, 10.0, 1.0, 30), # middle bin 3 + (True, 10.0, 0.5, 30), # middle bin 3 (True, 14.0, 1.0, 30), # lower bound bin 4 (True, 25.2, 1.5, 30), # lower bound bin 5 # unmixed, long storage duration - (False, -10.0, 21.0, 31), - (False, 0.0, 21.0, 31), + (False, -10.0, 14.0, 31), + (False, 0.0, 14.0, 31), (False, 4.6, 21.0, 31), - (False, 4.7, 26.0, 31), - (False, 5.8, 26.0, None), - (False, 10.0, 37.0, None), - (False, 14.0, 41.0, None), - (False, 25.2, 74.0, None), + (False, 4.7, 21.0, 31), + (False, 5.8, 21.0, None), + (False, 10.0, 21.0, None), + (False, 14.0, 37.0, None), + (False, 25.2, 73.0, None), # unmixed, short storage duration - (False, -10.0, 21.0, 29), - (False, 0.0, 21.0, 29), - (False, 4.6, 21.0, 29), - (False, 4.7, 26.0, 29), - (False, 5.8, 26.0, 29), - (False, 10.0, 37.0, 29), - (False, 14.0, 41.0, 29), - (False, 25.2, 74.0, 29), + (False, -10.0, 2.75, 29), + (False, 0.0, 2.75, 29), + (False, 4.6, 2.75, 29), + (False, 4.7, 2.75, 29), + (False, 5.8, 2.75, 29), + (False, 10.0, 2.75, 29), + (False, 14.0, 6.5, 29), + (False, 25.2, 18.0, 29), ], ) def test_calculate_bedded_pack_mcf_returns_expected( From 03da7abf50aa4f91afdec772e0c0f3b90bb5c06b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 14 Sep 2026 15:45:42 +0000 Subject: [PATCH 7/8] Apply Black Formatting From a8b5044951d1e575e87fd8bc3aef4a1cfa6483c6 Mon Sep 17 00:00:00 2001 From: ew3361zh Date: Mon, 14 Sep 2026 15:50:34 +0000 Subject: [PATCH 8/8] Update badges on README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c58c0facf1..ed66b0c6ba 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Flake8](https://img.shields.io/badge/Flake8-passed-brightgreen)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml) -[![Pytest](https://img.shields.io/badge/Pytest-failed-red)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml) -[![Coverage](https://img.shields.io/badge/Coverage-%25-red)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml) +[![Pytest](https://img.shields.io/badge/Pytest-passed-brightgreen)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml) +[![Coverage](https://img.shields.io/badge/Coverage-99%25-brightgreen)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml) [![Mypy](https://img.shields.io/badge/Mypy-1164%20errors-red)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml)