Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
2bed6d5
Implemented separate run
matthew7838 Apr 3, 2026
e868d0d
Removed animal related input
matthew7838 Apr 3, 2026
45f5495
Merge e868d0d1d5bcf684d7528ff2bc126a5fe601a1bb into 1916ac3030007deca…
matthew7838 Apr 3, 2026
31a3249
Apply Black Formatting
github-actions[bot] Apr 3, 2026
81e3c96
Added field with storage run
matthew7838 Apr 4, 2026
fd33f8f
Merge remote-tracking branch 'origin/separate-feed-run' into separate…
matthew7838 Apr 4, 2026
1cea3bb
Merge fd33f8fa7df4687df6bff576dbaf2d8b798fa79a into 444b6552298175e99…
matthew7838 Apr 4, 2026
c23078c
Apply Black Formatting
github-actions[bot] Apr 4, 2026
88522d4
Updated metadata and added overwritten functions
matthew7838 Apr 4, 2026
2d74255
Merge remote-tracking branch 'origin/separate-feed-run' into separate…
matthew7838 Apr 4, 2026
2be5a5c
Merge 2d74255f49cef14e9d4404953ea0ce52e40499ac into 444b6552298175e99…
matthew7838 Apr 4, 2026
3f81d92
Apply Black Formatting
github-actions[bot] Apr 4, 2026
f269a2c
Update badges on README
matthew7838 Apr 4, 2026
368935a
Updated changelog.md
matthew7838 Apr 4, 2026
bb4b63f
Merge remote-tracking branch 'origin/separate-feed-run' into separate…
matthew7838 Apr 4, 2026
6b93d7b
Updated tests
matthew7838 Apr 4, 2026
e3024df
Merge 6b93d7ba728ba78872b25b5a1d7612a55bed97ab into 444b6552298175e99…
matthew7838 Apr 4, 2026
3e9212d
Apply Black Formatting
github-actions[bot] Apr 4, 2026
cffe1a5
Update badges on README
matthew7838 Apr 4, 2026
2c11dcf
Merge branch 'dev' into separate-feed-run
matthew7838 Apr 6, 2026
dfc46c3
Merge 2c11dcf0f52d853d28f99e4b86895f1c487c5b62 into f794363cc15eba689…
matthew7838 Apr 6, 2026
114c46f
Apply Black Formatting
github-actions[bot] Apr 6, 2026
d130f10
Merge branch 'dev' into separate-feed-run
matthew7838 Apr 7, 2026
4282c4d
Merge d130f10a3654780ab9ec37fd0c57d3099031adbc into 472a3fefa4c4fc717…
matthew7838 Apr 7, 2026
27f0566
Addressed Niko's comments on improving unit test
matthew7838 Apr 7, 2026
9c9f991
Revert changes for testing purpose
matthew7838 Apr 7, 2026
5915db4
Apply Black Formatting
github-actions[bot] Apr 7, 2026
ade0c02
Merge remote-tracking branch 'origin/separate-feed-run' into separate…
matthew7838 Apr 7, 2026
ec65507
Merge ade0c029cb146ed698fcd4ad5fe8f62cf2dc220f into 472a3fefa4c4fc717…
matthew7838 Apr 7, 2026
f5f8ff1
Apply Black Formatting
github-actions[bot] Apr 7, 2026
0391a47
Merge branch 'dev' into separate-feed-run
matthew7838 Apr 7, 2026
3e9b2d0
Merge 0391a47adc54c469cc3ed46993254e1d79a52b91 into 53349675447bf39b6…
matthew7838 Apr 7, 2026
a31591f
Apply Black Formatting
github-actions[bot] Apr 7, 2026
27d9be5
Separate field manure supplier into data structures
matthew7838 Apr 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion RUFAS/biophysical/manure/manure_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from dataclasses import replace
from typing import Any

from RUFAS.biophysical.manure.field_manure_supplier import FieldManureSupplier
from RUFAS.data_structures.field_manure_supplier import FieldManureSupplier
from RUFAS.biophysical.manure.handler.handler import Handler
from RUFAS.biophysical.manure.manure_nutrient_manager import ManureNutrientManager
from RUFAS.biophysical.manure.processor import Processor
Expand Down
58 changes: 51 additions & 7 deletions RUFAS/simulation_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from RUFAS.data_structures.animal_to_manure_connection import ManureStream
from RUFAS.data_structures.crop_soil_to_feed_storage_connection import HarvestedCrop
from RUFAS.data_structures.feed_storage_to_animal_connection import NutrientStandard
from RUFAS.data_structures.field_manure_supplier import FieldManureSupplier
from RUFAS.data_structures.manure_to_crop_soil_connection import ManureEventNutrientRequestResults
from RUFAS.input_manager import InputManager
from RUFAS.output_manager import OutputManager
Expand All @@ -36,6 +37,8 @@ class SimulationType(Enum):

FULL_FARM = "full_farm"
FIELD_AND_FEED = "field_and_feed"
FIELD_ONLY = "field_only"
FIELD_WITH_STORAGE = "field_with_storage"

@property
def simulate_animals(self) -> bool:
Expand All @@ -47,6 +50,8 @@ def _non_animal_simulation_types(cls) -> set["SimulationType"]:
"""Return the set of simulation types that do not simulate animals."""
return {
cls.FIELD_AND_FEED,
cls.FIELD_ONLY,
cls.FIELD_WITH_STORAGE,
}

@classmethod
Expand Down Expand Up @@ -110,6 +115,8 @@ def __init__(self, simulation_type: SimulationType) -> None:
self._simulation_type_to_daily_simulation_function = {
SimulationType.FULL_FARM: self._execute_full_farm_daily_simulation,
SimulationType.FIELD_AND_FEED: self._execute_field_and_feed_daily_simulation,
simulation_type.FIELD_ONLY: self._execute_field_only_simulation,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for these simulations where we're isolating single parts of the model we want to be sure they can run without any other parts of the model being initialized.

FIELD_ONLY still depends on a Manure module. I know we'd talked about how to get manure applications out of the Manure module potentially - did you look into that at all?

I'm working on something that was initially separating out feed management from ration planning but has grown into initializing only modules that should be required for the simulation being run by the user. Maybe we can take a look at it and see how it can fit together with this.

simulation_type.FIELD_WITH_STORAGE: self._execute_field_with_storage_simulation,
}

self._initialize_simulation()
Expand Down Expand Up @@ -211,7 +218,7 @@ def _execute_full_farm_daily_simulation(self) -> None:
7. Advance simulation date

"""
daily_harvested_crops = self._execute_daily_field_operations()
daily_harvested_crops = self._execute_daily_field_with_storage_operations()

harvest_schedule = self._build_harvest_schedule(daily_harvested_crops)
self._execute_feed_planning(harvest_schedule)
Expand All @@ -226,6 +233,38 @@ def _execute_full_farm_daily_simulation(self) -> None:

self._advance_time()

def _execute_field_with_storage_simulation(self) -> None:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this differ from the FIELD_AND_FEED simulation in scientific outcomes/intent?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent was to have an option where ration formulation was not performed, requiring no animal or nutritiion inputs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should probably be doing this in field and feed tbh. I only had it in because ration formulation was tied up with feed degradations/storage. I have it removed on the latest version of #2939 and I think my preference would be to not create this separate function.

"""
Executes the daily simulation routines for a farm with only the field and storage modules.

Daily Field With Storage Simulation Process:
1. Field operations (manure applications, harvesting)
2. Record keeping (time, weather, purchased feeds fed emissions)
3. Advance simulation date

"""
self._execute_daily_field_with_storage_operations()

self._report_daily_records()

self._advance_time()

def _execute_field_only_simulation(self) -> None:
"""
Executes the daily simulation routines for a field only modules.

Daily Field Process:
1. Field operations without sending the crops to the feed manager
2. Record keeping (time, weather, purchased feeds fed emissions)
3. Advance simulation date

"""
self._execute_daily_field_only_operations()

self._report_daily_records()

self._advance_time()

def _execute_field_and_feed_daily_simulation(self) -> None:
"""
Executes the daily simulation routines for a farm with only the field and feed modules.
Expand All @@ -239,7 +278,7 @@ def _execute_field_and_feed_daily_simulation(self) -> None:
5. Advance simulation date

"""
daily_harvested_crops = self._execute_daily_field_operations()
daily_harvested_crops = self._execute_daily_field_with_storage_operations()

harvest_schedule = self._build_harvest_schedule(daily_harvested_crops)
self._execute_feed_planning(harvest_schedule)
Expand All @@ -250,13 +289,20 @@ def _execute_field_and_feed_daily_simulation(self) -> None:

self._advance_time()

def _execute_daily_field_operations(self) -> list[HarvestedCrop]:
"""Handles daily field operations including manure applications and crop harvesting/receiving."""
def _execute_daily_field_only_operations(self) -> list[HarvestedCrop]:
"""Handles daily field operations including manure applications."""
manure_applications: list[ManureEventNutrientRequestResults] = self.generate_daily_manure_applications()

harvested_crops: list[HarvestedCrop] = self.field_manager.daily_update_routine(
self.weather, self.time, manure_applications
)

return harvested_crops

def _execute_daily_field_with_storage_operations(self) -> list[HarvestedCrop]:
"""Handles daily field operations including manure applications and crop harvesting/receiving."""
harvested_crops: list[HarvestedCrop] = self._execute_daily_field_only_operations()

for crop in harvested_crops:
self.feed_manager.receive_crop(crop, self.time.simulation_day)

Expand All @@ -279,9 +325,7 @@ def generate_daily_manure_applications(self) -> list[ManureEventNutrientRequestR
manure_request = manure_event_request.nutrient_request
manure_request_results = None
if manure_request is not None:
manure_request_results = self.manure_manager.request_nutrients(
manure_request, self.simulate_animals, self.time
)
manure_request_results = FieldManureSupplier.request_nutrients(manure_request)
manure_applications.append(ManureEventNutrientRequestResults(field_name, event, manure_request_results))
return manure_applications

Expand Down
2 changes: 1 addition & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ v1.0.0
- [2921](https://github.com/RuminantFarmSystems/RuFaS/pull/2921) - [minor change] [OutputManager][NoInputChange] [NoOutputChange] Override incorrect fill type for complex data structure data padding.
- [2924](https://github.com/RuminantFarmSystems/RuFaS/pull/2924) - [minor change] [NoInputChange] [NoOutputChange] Updated advance purchase allowance to prevent excessive warnings for example run.
- [2929](https://github.com/RuminantFarmSystems/RuFaS/pull/2929) - [minor change] [GraphGenerator] [NoInputChange] [NoOutputChange] Sanitizes non-numerical data sent to graph generator to allow graphing to occur despite.
- [2932](https://github.com/RuminantFarmSystems/RuFaS/pull/2932) - [minor change] [SimulationEngine] [NoInputChange] [NoOutputChange] Enabled Field only and Field with storage simulation.
- [2925](https://github.com/RuminantFarmSystems/RuFaS/pull/2925) - [minor change] [NoInputChange] [NoOutputChange] Fix the `graph_and_report` option in report_generation.py.
- [2907](https://github.com/RuminantFarmSystems/RuFaS/pull/2907) - [minor change] [NoInputChange] [OutputChange] Fix the FarmGrownFeed Emissions unit issue. The mirror issue of [Fix FarmGrownFeed Emissions Unit on test #2908](https://github.com/RuminantFarmSystems/MASM/pull/2908) to update `dev`.


### v1.0.0

- [2081](https://github.com/RuminantFarmSystems/RuFaS/pull/2081) - [minor change] [Crop & Soil] Break down the `_setup_field()` function in `FieldManager`.
Expand Down
10 changes: 10 additions & 0 deletions input/data/config/example_field_only_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"start_date": "2013:1",
"end_date": "2019:365",
"random_seed": 42,
"set_seed": true,
"simulation_type": "field_only",
"nutrient_standard": "NASEM",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed, nutrient_standard is one of the many parameters not strictly required by "field_only" (and possibly "field_and_storage"). I think this is covered by #2894, but wanted to mention it here to keep it on the radar.

"FIPS_county_code": 55025,
"include_detailed_values": false
}
10 changes: 10 additions & 0 deletions input/data/config/example_field_with_storage_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"start_date": "2013:1",
"end_date": "2019:365",
"random_seed": 42,
"set_seed": true,
"simulation_type": "field_with_storage",
"nutrient_standard": "NASEM",
"FIPS_county_code": 55025,
"include_detailed_values": false
}
40 changes: 40 additions & 0 deletions input/data/tasks/example_field_only_task.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"parallel_workers": 4,
"input_data_csv_export_path": "output/saved_input_data/.",
"input_data_csv_import_path": ".",
"export_input_data_to_csv": false,
"tasks": [
{
"task_type": "SIMULATION_SINGLE_RUN",
"metadata_file_path": "input/metadata/example_field_only_metadata.json",
"output_prefix": "field_only",
"log_verbosity": "errors",
"random_seed": 42,
"SA_load_balancing_stop": 1,
"SA_load_balancing_start": 0,
"sampler_n": 2,
"skip_values": 0,
"sampler": "sobol",
"multi_run_counts": 4,
"maximum_memory_usage_percent": 80,
"maximum_memory_usage": 0,
"save_chunk_threshold_call_count": 0,
"chunkification": false,
"suppress_log_files": false,
"logs_directory": "output/logs/.",
"properties_file_path": "input/metadata/properties/default.json",
"comparison_properties_file_path": "input/metadata/properties/default.json",
"variable_name_style": "basic",
"exclude_info_maps": false,
"init_herd": false,
"save_animals": false,
"save_animals_directory": "output/.",
"filters_directory": "output/output_filters/.",
"csv_output_directory": "output/CSVs/.",
"json_output_directory": "output/JSONs/.",
"report_directory": "output/reports/.",
"graphics_directory": "output/graphics/.",
"cross_validation_file_paths": []
}
]
}
40 changes: 40 additions & 0 deletions input/data/tasks/example_field_with_storage_task.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"parallel_workers": 4,
"input_data_csv_export_path": "output/saved_input_data/.",
"input_data_csv_import_path": ".",
"export_input_data_to_csv": false,
"tasks": [
{
"task_type": "SIMULATION_SINGLE_RUN",
"metadata_file_path": "input/metadata/example_field_with_storage_metadata.json",
"output_prefix": "field_only",
"log_verbosity": "errors",
"random_seed": 42,
"SA_load_balancing_stop": 1,
"SA_load_balancing_start": 0,
"sampler_n": 2,
"skip_values": 0,
"sampler": "sobol",
"multi_run_counts": 4,
"maximum_memory_usage_percent": 80,
"maximum_memory_usage": 0,
"save_chunk_threshold_call_count": 0,
"chunkification": false,
"suppress_log_files": false,
"logs_directory": "output/logs/.",
"properties_file_path": "input/metadata/properties/default.json",
"comparison_properties_file_path": "input/metadata/properties/default.json",
"variable_name_style": "basic",
"exclude_info_maps": false,
"init_herd": false,
"save_animals": false,
"save_animals_directory": "output/.",
"filters_directory": "output/output_filters/.",
"csv_output_directory": "output/CSVs/.",
"json_output_directory": "output/JSONs/.",
"report_directory": "output/reports/.",
"graphics_directory": "output/graphics/.",
"cross_validation_file_paths": []
}
]
}
Loading