From 2bed6d52aacc5a4483699bcbe4bc5b3f69e82534 Mon Sep 17 00:00:00 2001 From: Matthew Liu Date: Sat, 4 Apr 2026 00:56:21 +0900 Subject: [PATCH 01/19] Implemented separate run --- RUFAS/simulation_engine.py | 13 + .../config/example_field_only_config.json | 10 + input/data/tasks/example_field_only_task.json | 40 +++ .../metadata/example_field_only_metadata.json | 254 ++++++++++++++++++ input/task_manager_metadata.json | 2 +- 5 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 input/data/config/example_field_only_config.json create mode 100644 input/data/tasks/example_field_only_task.json create mode 100644 input/metadata/example_field_only_metadata.json diff --git a/RUFAS/simulation_engine.py b/RUFAS/simulation_engine.py index 8e70d48fae..bb350fdc9a 100644 --- a/RUFAS/simulation_engine.py +++ b/RUFAS/simulation_engine.py @@ -35,6 +35,7 @@ class SimulationType(Enum): FULL_FARM = "full_farm" FIELD_AND_FEED = "field_and_feed" + FIELD_ONLY = "field_only" @property def simulate_animals(self) -> bool: @@ -109,6 +110,7 @@ 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 } self._initialize_simulation() @@ -223,6 +225,17 @@ def _execute_full_farm_daily_simulation(self) -> None: self._advance_time() + def _execute_field_only_simulation(self) -> None: + manure_applications: list[ManureEventNutrientRequestResults] = self.generate_daily_manure_applications() + + self.field_manager.daily_update_routine( + self.weather, self.time, manure_applications + ) + + 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. diff --git a/input/data/config/example_field_only_config.json b/input/data/config/example_field_only_config.json new file mode 100644 index 0000000000..a0816edbc8 --- /dev/null +++ b/input/data/config/example_field_only_config.json @@ -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", + "FIPS_county_code": 55025, + "include_detailed_values": false +} \ No newline at end of file diff --git a/input/data/tasks/example_field_only_task.json b/input/data/tasks/example_field_only_task.json new file mode 100644 index 0000000000..88fed6bba6 --- /dev/null +++ b/input/data/tasks/example_field_only_task.json @@ -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_and_feed_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": [] + } + ] +} \ No newline at end of file diff --git a/input/metadata/example_field_only_metadata.json b/input/metadata/example_field_only_metadata.json new file mode 100644 index 0000000000..85eabda25f --- /dev/null +++ b/input/metadata/example_field_only_metadata.json @@ -0,0 +1,254 @@ +{ + "files": { + "config": { + "title": "Config Data", + "description": "Configuration file for general simulation parameters.", + "path": "input/data/config/example_field_only_config.json", + "type": "json", + "properties": "config_properties" + }, + "animal": { + "title": "Animal data", + "description": "Input data and configuration information for animal management decisions, herd attributes, and housing properties.", + "path": "input/data/animal/example_freestall_animal.json", + "type": "json", + "properties": "animal_properties" + }, + "animal_population": { + "title": "Animal population data", + "description": "Animal objects that herd initialization draws from during the simulation. A similar file can be generated using the -I and -s command line arguments simultaneously.", + "path": "input/data/animal/animal_population.json", + "type": "json", + "properties": "animal_population_properties" + }, + "animal_net_merit": { + "title": "Animal Net Merit data", + "description": "The net merit value for cows.", + "path": "input/data/animal/animal_genetics/NetMerit_HO.csv", + "type": "csv", + "properties": "animal_net_merit_properties" + }, + "animal_top_listing_semen": { + "title": "Animal Top Listing Semen data", + "description": "The top listing semen value for new born calves.", + "path": "input/data/animal/animal_genetics/TopListingSemen_HO.csv", + "type": "csv", + "properties": "animal_top_listing_semen_properties" + }, + "lactation": { + "title": "Lactation curve adjustment values", + "description": "Values for the adjustment of the three wood parameters based on farm specific data", + "path": "input/data/animal/lactation_curve_adjustment_inputs.json", + "type": "json", + "properties": "lactation_properties" + }, + "economy": { + "title": "Economy data", + "description": "Energy prices used in the EEE module.", + "path": "input/data/EEE/default_costs.csv", + "type": "csv", + "properties": "economic_properties" + }, + "emission": { + "title": "Emissions data", + "description": "General emission values used in the EEE module.", + "path": "input/data/EEE/default_emissions.csv", + "type": "csv", + "properties": "emissions_properties" + }, + "purchased_feeds_emissions": { + "title": "Emissions from purchased feeds", + "description": "Purchased feeds emission values used in the EEE module. Missing data interpolated following script in helpful_scripts/emissions_interpolation.", + "path": "input/data/EEE/full_feeds_emissions_July2024_interpolated_regional_average.csv", + "type": "csv", + "properties": "feed_emissions_properties" + }, + "purchased_feed_land_use_change_emissions": { + "title": "Land Use Change emissions from purchased feeds", + "description": "Purchased feeds land use change emission values used in the EEE module. Missing data interpolated following script in helpful_scripts/emissions_interpolation.", + "path": "input/data/EEE/full_feeds_land_use_change_emissions_July2024_interpolated_regional_average.csv", + "type": "csv", + "properties": "feed_emissions_properties" + }, + "feed": { + "title": "Feed data", + "description": "Feeds available for each animal combination, purchased feeds and their prices, feed storage options, and user-defined ration percentages and associated parameters.", + "path": "input/data/feed/example_Midwest_feed.json", + "type": "json", + "properties": "feed_properties" + }, + "NRC_Comp": { + "title": "NRC Comp data", + "description": "Nutritional information for each feed, following NRC (2001) guidelines.", + "path": "input/data/feed/NRC_comp.csv", + "type": "csv", + "properties": "NRC_Comp_properties" + }, + "NASEM_Comp": { + "title": "NASEM Comp data", + "description": "Nutritional information for each feed, following NASEM (2021) guidelines.", + "path": "input/data/feed/NASEM_Comp_with_TDN_urea.csv", + "type": "csv", + "properties": "NASEM_Comp_properties" + }, + "manure_management": { + "title": "Manure Processor Configurations", + "description": "Configurations of manure processors (handlers, separators, digesters, and storages) used in Manure module.", + "path": "input/data/manure/example_freestall_processor_configs.json", + "type": "json", + "properties": "manure_management_properties" + }, + "manure_processor_connection": { + "title": "Manure Processor Connections", + "description": "The connection configs for all manure processors.", + "path": "input/data/manure/example_freestall_processor_connections.json", + "type": "json", + "properties": "manure_processor_connection_properties" + }, + "crop_configurations": { + "title": "Crop Configurations", + "description": "Configurations for how crops grow and are managed.", + "path": "input/data/crop_configurations/default_crop_configs.json", + "type": "json", + "properties": "crop_configuration_properties" + }, + "field_1": { + "title": "Field specification", + "description": "Field characteristics and references to field management specifications.", + "path": "input/data/field/example_small_field_corn_alf_silage.json", + "type": "json", + "properties": "field_properties" + }, + "soil_1": { + "title": "Soil data", + "description": "Characteristic soil information, including composition, slope, and layer details.", + "path": "input/data/soil/example_soil.json", + "type": "json", + "properties": "soil_profile_properties" + }, + "Corn-Alf-Silage": { + "title": "Crop data", + "description": "Crop selection and detailed rotation schedules.", + "path": "input/data/crop/example_alf_corn_silage_rotation.json", + "type": "json", + "properties": "crop_schedule_properties" + }, + "fertilizer_schedule_1": { + "title": "Fertilizer schedule.", + "description": "Fertilizer types available, and their application schedule.", + "path": "input/data/fertilizer_schedule/example_sm_alf_corn_fertilizer.json", + "type": "json", + "properties": "fertilizer_schedule_properties" + }, + "manure_schedule_1": { + "title": "Manure schedule.", + "description": "Specifies manure applications to a field.", + "path": "input/data/manure_schedule/example_sm_corn_alf_manure_schedule.json", + "type": "json", + "properties": "manure_schedule_properties" + }, + "tillage_schedule_1": { + "title": "Tillage schedule.", + "description": "Schedule of tillage applications for a field.", + "path": "input/data/tillage_schedule/no_till.json", + "type": "json", + "properties": "tillage_schedule_properties" + }, + "field_2": { + "title": "Field specification", + "description": "Field characteristics and references to field management specifications.", + "path": "input/data/field/example_small_field_corn_grain_alf_hay.json", + "type": "json", + "properties": "field_properties" + }, + "soil_2": { + "title": "Soil data", + "description": "Characteristic soil information, including composition, slope, and layer details.", + "path": "input/data/soil/example_soil_2.json", + "type": "json", + "properties": "soil_profile_properties" + }, + "CornGrain-AlfHay": { + "title": "Crop data", + "description": "Crop selection and detailed rotation schedules.", + "path": "input/data/crop/example_alf_hay_corn_grain_rotation.json", + "type": "json", + "properties": "crop_schedule_properties" + }, + "fertilizer_schedule_2": { + "title": "Fertilizer schedule.", + "description": "Fertilizer types available, and their application schedule.", + "path": "input/data/fertilizer_schedule/example_sm_alf_corn_fertilizer.json", + "type": "json", + "properties": "fertilizer_schedule_properties" + }, + "manure_schedule_2": { + "title": "Manure schedule.", + "description": "Specifies manure applications to a field.", + "path": "input/data/manure_schedule/example_sm_corn_alf_manure_schedule.json", + "type": "json", + "properties": "manure_schedule_properties" + }, + "tillage_schedule_2": { + "title": "Tillage schedule.", + "description": "Schedule of tillage applications for a field.", + "path": "input/data/tillage_schedule/no_till.json", + "type": "json", + "properties": "tillage_schedule_properties" + }, + "weather": { + "title": "Weather data", + "description": "Weather data used during the simulation, including date, precipitation, temperature, etc.", + "path": "input/data/weather/example_temperate_weather.csv", + "type": "csv", + "properties": "weather_properties" + }, + "user_feeds": { + "title": "User Feed", + "description": "Summary of feeds available for use in the simulation, including their RuFaS ID, short descriptors, and which nutrient composition file they are included in.", + "path": "input/data/feed/user_feeds.csv", + "type": "csv", + "properties": "user_feeds_properties" + }, + "tractor_dataset": { + "title": "Tractor Dataset", + "description": "Details agricultural machinery operations for various crops, including tractor size, operations (planting, mowing, collection, etc.), implement details, operational parameters (depth, width, mass), and throughput metrics, spanning different crop types and soil management practices.", + "path": "input/data/EEE/tractor_dataset.csv", + "type": "csv", + "properties": "tractor_dataset_properties" + }, + "EEE_constants": { + "title": "EEE Constants", + "description": "The constants that are used in EEE module.", + "path": "input/data/EEE/constants.json", + "type": "json", + "properties": "EEE_constants_properties" + }, + "feed_management": { + "title": "Feed Management", + "description": "Configurations for feed storage units.", + "path": "input/data/feed_management/example_feed_storage_configs.json", + "type": "json", + "properties": "feed_storage_configurations" + }, + "feed_storage_configurations": { + "title": "Feed Management", + "description": "Configurations for feed storage units.", + "path": "input/data/feed_management/example_feed_storage_configs.json", + "type": "json", + "properties": "feed_storage_configurations" + }, + "feed_storage_instances": { + "title": "Feed Storages", + "description": "Names of feed storage configs used in the simulation.", + "path": "input/data/feed_management/example_freestall_feed_storages.json", + "type": "json", + "properties": "feed_storage_instances" + } + }, + "runtime_metadata": { + "EEE_econ": { + "path": "input/metadata/EEE/econ_metadata.json" + } + } +} \ No newline at end of file diff --git a/input/task_manager_metadata.json b/input/task_manager_metadata.json index 5674a5fcdc..bdcc1f0bc2 100644 --- a/input/task_manager_metadata.json +++ b/input/task_manager_metadata.json @@ -3,7 +3,7 @@ "tasks": { "title": "Task manager data", "description": "Configuration file for general simulation parameters.", - "path": "input/data/tasks/example_freestall_task.json", + "path": "input/data/tasks/example_field_only_task.json", "type": "json", "properties": "tasks_properties" } From e868d0d1d5bcf684d7528ff2bc126a5fe601a1bb Mon Sep 17 00:00:00 2001 From: Matthew Liu Date: Sat, 4 Apr 2026 01:18:50 +0900 Subject: [PATCH 02/19] Removed animal related input --- .../metadata/example_field_only_metadata.json | 70 ------------------- 1 file changed, 70 deletions(-) diff --git a/input/metadata/example_field_only_metadata.json b/input/metadata/example_field_only_metadata.json index 85eabda25f..e2a8ea97d5 100644 --- a/input/metadata/example_field_only_metadata.json +++ b/input/metadata/example_field_only_metadata.json @@ -7,41 +7,6 @@ "type": "json", "properties": "config_properties" }, - "animal": { - "title": "Animal data", - "description": "Input data and configuration information for animal management decisions, herd attributes, and housing properties.", - "path": "input/data/animal/example_freestall_animal.json", - "type": "json", - "properties": "animal_properties" - }, - "animal_population": { - "title": "Animal population data", - "description": "Animal objects that herd initialization draws from during the simulation. A similar file can be generated using the -I and -s command line arguments simultaneously.", - "path": "input/data/animal/animal_population.json", - "type": "json", - "properties": "animal_population_properties" - }, - "animal_net_merit": { - "title": "Animal Net Merit data", - "description": "The net merit value for cows.", - "path": "input/data/animal/animal_genetics/NetMerit_HO.csv", - "type": "csv", - "properties": "animal_net_merit_properties" - }, - "animal_top_listing_semen": { - "title": "Animal Top Listing Semen data", - "description": "The top listing semen value for new born calves.", - "path": "input/data/animal/animal_genetics/TopListingSemen_HO.csv", - "type": "csv", - "properties": "animal_top_listing_semen_properties" - }, - "lactation": { - "title": "Lactation curve adjustment values", - "description": "Values for the adjustment of the three wood parameters based on farm specific data", - "path": "input/data/animal/lactation_curve_adjustment_inputs.json", - "type": "json", - "properties": "lactation_properties" - }, "economy": { "title": "Economy data", "description": "Energy prices used in the EEE module.", @@ -70,41 +35,6 @@ "type": "csv", "properties": "feed_emissions_properties" }, - "feed": { - "title": "Feed data", - "description": "Feeds available for each animal combination, purchased feeds and their prices, feed storage options, and user-defined ration percentages and associated parameters.", - "path": "input/data/feed/example_Midwest_feed.json", - "type": "json", - "properties": "feed_properties" - }, - "NRC_Comp": { - "title": "NRC Comp data", - "description": "Nutritional information for each feed, following NRC (2001) guidelines.", - "path": "input/data/feed/NRC_comp.csv", - "type": "csv", - "properties": "NRC_Comp_properties" - }, - "NASEM_Comp": { - "title": "NASEM Comp data", - "description": "Nutritional information for each feed, following NASEM (2021) guidelines.", - "path": "input/data/feed/NASEM_Comp_with_TDN_urea.csv", - "type": "csv", - "properties": "NASEM_Comp_properties" - }, - "manure_management": { - "title": "Manure Processor Configurations", - "description": "Configurations of manure processors (handlers, separators, digesters, and storages) used in Manure module.", - "path": "input/data/manure/example_freestall_processor_configs.json", - "type": "json", - "properties": "manure_management_properties" - }, - "manure_processor_connection": { - "title": "Manure Processor Connections", - "description": "The connection configs for all manure processors.", - "path": "input/data/manure/example_freestall_processor_connections.json", - "type": "json", - "properties": "manure_processor_connection_properties" - }, "crop_configurations": { "title": "Crop Configurations", "description": "Configurations for how crops grow and are managed.", From 31a32495607092af0c4bb9a005d578a2b80e7180 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 3 Apr 2026 16:21:54 +0000 Subject: [PATCH 03/19] Apply Black Formatting --- RUFAS/simulation_engine.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/RUFAS/simulation_engine.py b/RUFAS/simulation_engine.py index bb350fdc9a..73ded7368b 100644 --- a/RUFAS/simulation_engine.py +++ b/RUFAS/simulation_engine.py @@ -110,7 +110,7 @@ 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 + simulation_type.FIELD_ONLY: self._execute_field_only_simulation, } self._initialize_simulation() @@ -228,9 +228,7 @@ def _execute_full_farm_daily_simulation(self) -> None: def _execute_field_only_simulation(self) -> None: manure_applications: list[ManureEventNutrientRequestResults] = self.generate_daily_manure_applications() - self.field_manager.daily_update_routine( - self.weather, self.time, manure_applications - ) + self.field_manager.daily_update_routine(self.weather, self.time, manure_applications) self._report_daily_records() From 81e3c962d33b568310eb4c634d900af0a06f6606 Mon Sep 17 00:00:00 2001 From: Matthew Liu Date: Sat, 4 Apr 2026 19:04:28 +0900 Subject: [PATCH 04/19] Added field with storage run --- RUFAS/simulation_engine.py | 11 +- .../example_field_with_storage_config.json | 10 + input/data/tasks/example_field_only_task.json | 2 +- .../example_field_with_storage_task.json | 40 +++ .../metadata/example_field_only_metadata.json | 70 +++++ .../example_field_with_storage_metadata.json | 254 ++++++++++++++++++ input/task_manager_metadata.json | 2 +- 7 files changed, 386 insertions(+), 3 deletions(-) create mode 100644 input/data/config/example_field_with_storage_config.json create mode 100644 input/data/tasks/example_field_with_storage_task.json create mode 100644 input/metadata/example_field_with_storage_metadata.json diff --git a/RUFAS/simulation_engine.py b/RUFAS/simulation_engine.py index bb350fdc9a..7af4aa2bf9 100644 --- a/RUFAS/simulation_engine.py +++ b/RUFAS/simulation_engine.py @@ -36,6 +36,7 @@ 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: @@ -110,7 +111,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 + simulation_type.FIELD_ONLY: self._execute_field_only_simulation, + simulation_type.FIELD_WITH_STORAGE: self._execute_field_with_storage } self._initialize_simulation() @@ -236,6 +238,13 @@ def _execute_field_only_simulation(self) -> None: self._advance_time() + def _execute_field_with_storage(self) -> None: + self._execute_daily_field_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. diff --git a/input/data/config/example_field_with_storage_config.json b/input/data/config/example_field_with_storage_config.json new file mode 100644 index 0000000000..6418cf2de1 --- /dev/null +++ b/input/data/config/example_field_with_storage_config.json @@ -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 +} \ No newline at end of file diff --git a/input/data/tasks/example_field_only_task.json b/input/data/tasks/example_field_only_task.json index 88fed6bba6..1af56a30fc 100644 --- a/input/data/tasks/example_field_only_task.json +++ b/input/data/tasks/example_field_only_task.json @@ -6,7 +6,7 @@ "tasks": [ { "task_type": "SIMULATION_SINGLE_RUN", - "metadata_file_path": "input/metadata/example_field_and_feed_metadata.json", + "metadata_file_path": "input/metadata/example_field_only_metadata.json", "output_prefix": "field_only", "log_verbosity": "errors", "random_seed": 42, diff --git a/input/data/tasks/example_field_with_storage_task.json b/input/data/tasks/example_field_with_storage_task.json new file mode 100644 index 0000000000..f647c79824 --- /dev/null +++ b/input/data/tasks/example_field_with_storage_task.json @@ -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": [] + } + ] +} \ No newline at end of file diff --git a/input/metadata/example_field_only_metadata.json b/input/metadata/example_field_only_metadata.json index e2a8ea97d5..85eabda25f 100644 --- a/input/metadata/example_field_only_metadata.json +++ b/input/metadata/example_field_only_metadata.json @@ -7,6 +7,41 @@ "type": "json", "properties": "config_properties" }, + "animal": { + "title": "Animal data", + "description": "Input data and configuration information for animal management decisions, herd attributes, and housing properties.", + "path": "input/data/animal/example_freestall_animal.json", + "type": "json", + "properties": "animal_properties" + }, + "animal_population": { + "title": "Animal population data", + "description": "Animal objects that herd initialization draws from during the simulation. A similar file can be generated using the -I and -s command line arguments simultaneously.", + "path": "input/data/animal/animal_population.json", + "type": "json", + "properties": "animal_population_properties" + }, + "animal_net_merit": { + "title": "Animal Net Merit data", + "description": "The net merit value for cows.", + "path": "input/data/animal/animal_genetics/NetMerit_HO.csv", + "type": "csv", + "properties": "animal_net_merit_properties" + }, + "animal_top_listing_semen": { + "title": "Animal Top Listing Semen data", + "description": "The top listing semen value for new born calves.", + "path": "input/data/animal/animal_genetics/TopListingSemen_HO.csv", + "type": "csv", + "properties": "animal_top_listing_semen_properties" + }, + "lactation": { + "title": "Lactation curve adjustment values", + "description": "Values for the adjustment of the three wood parameters based on farm specific data", + "path": "input/data/animal/lactation_curve_adjustment_inputs.json", + "type": "json", + "properties": "lactation_properties" + }, "economy": { "title": "Economy data", "description": "Energy prices used in the EEE module.", @@ -35,6 +70,41 @@ "type": "csv", "properties": "feed_emissions_properties" }, + "feed": { + "title": "Feed data", + "description": "Feeds available for each animal combination, purchased feeds and their prices, feed storage options, and user-defined ration percentages and associated parameters.", + "path": "input/data/feed/example_Midwest_feed.json", + "type": "json", + "properties": "feed_properties" + }, + "NRC_Comp": { + "title": "NRC Comp data", + "description": "Nutritional information for each feed, following NRC (2001) guidelines.", + "path": "input/data/feed/NRC_comp.csv", + "type": "csv", + "properties": "NRC_Comp_properties" + }, + "NASEM_Comp": { + "title": "NASEM Comp data", + "description": "Nutritional information for each feed, following NASEM (2021) guidelines.", + "path": "input/data/feed/NASEM_Comp_with_TDN_urea.csv", + "type": "csv", + "properties": "NASEM_Comp_properties" + }, + "manure_management": { + "title": "Manure Processor Configurations", + "description": "Configurations of manure processors (handlers, separators, digesters, and storages) used in Manure module.", + "path": "input/data/manure/example_freestall_processor_configs.json", + "type": "json", + "properties": "manure_management_properties" + }, + "manure_processor_connection": { + "title": "Manure Processor Connections", + "description": "The connection configs for all manure processors.", + "path": "input/data/manure/example_freestall_processor_connections.json", + "type": "json", + "properties": "manure_processor_connection_properties" + }, "crop_configurations": { "title": "Crop Configurations", "description": "Configurations for how crops grow and are managed.", diff --git a/input/metadata/example_field_with_storage_metadata.json b/input/metadata/example_field_with_storage_metadata.json new file mode 100644 index 0000000000..b7fbc05876 --- /dev/null +++ b/input/metadata/example_field_with_storage_metadata.json @@ -0,0 +1,254 @@ +{ + "files": { + "config": { + "title": "Config Data", + "description": "Configuration file for general simulation parameters.", + "path": "input/data/config/example_field_with_storage_config.json", + "type": "json", + "properties": "config_properties" + }, + "animal": { + "title": "Animal data", + "description": "Input data and configuration information for animal management decisions, herd attributes, and housing properties.", + "path": "input/data/animal/example_freestall_animal.json", + "type": "json", + "properties": "animal_properties" + }, + "animal_population": { + "title": "Animal population data", + "description": "Animal objects that herd initialization draws from during the simulation. A similar file can be generated using the -I and -s command line arguments simultaneously.", + "path": "input/data/animal/animal_population.json", + "type": "json", + "properties": "animal_population_properties" + }, + "animal_net_merit": { + "title": "Animal Net Merit data", + "description": "The net merit value for cows.", + "path": "input/data/animal/animal_genetics/NetMerit_HO.csv", + "type": "csv", + "properties": "animal_net_merit_properties" + }, + "animal_top_listing_semen": { + "title": "Animal Top Listing Semen data", + "description": "The top listing semen value for new born calves.", + "path": "input/data/animal/animal_genetics/TopListingSemen_HO.csv", + "type": "csv", + "properties": "animal_top_listing_semen_properties" + }, + "lactation": { + "title": "Lactation curve adjustment values", + "description": "Values for the adjustment of the three wood parameters based on farm specific data", + "path": "input/data/animal/lactation_curve_adjustment_inputs.json", + "type": "json", + "properties": "lactation_properties" + }, + "economy": { + "title": "Economy data", + "description": "Energy prices used in the EEE module.", + "path": "input/data/EEE/default_costs.csv", + "type": "csv", + "properties": "economic_properties" + }, + "emission": { + "title": "Emissions data", + "description": "General emission values used in the EEE module.", + "path": "input/data/EEE/default_emissions.csv", + "type": "csv", + "properties": "emissions_properties" + }, + "purchased_feeds_emissions": { + "title": "Emissions from purchased feeds", + "description": "Purchased feeds emission values used in the EEE module. Missing data interpolated following script in helpful_scripts/emissions_interpolation.", + "path": "input/data/EEE/full_feeds_emissions_July2024_interpolated_regional_average.csv", + "type": "csv", + "properties": "feed_emissions_properties" + }, + "purchased_feed_land_use_change_emissions": { + "title": "Land Use Change emissions from purchased feeds", + "description": "Purchased feeds land use change emission values used in the EEE module. Missing data interpolated following script in helpful_scripts/emissions_interpolation.", + "path": "input/data/EEE/full_feeds_land_use_change_emissions_July2024_interpolated_regional_average.csv", + "type": "csv", + "properties": "feed_emissions_properties" + }, + "feed": { + "title": "Feed data", + "description": "Feeds available for each animal combination, purchased feeds and their prices, feed storage options, and user-defined ration percentages and associated parameters.", + "path": "input/data/feed/example_Midwest_feed.json", + "type": "json", + "properties": "feed_properties" + }, + "NRC_Comp": { + "title": "NRC Comp data", + "description": "Nutritional information for each feed, following NRC (2001) guidelines.", + "path": "input/data/feed/NRC_comp.csv", + "type": "csv", + "properties": "NRC_Comp_properties" + }, + "NASEM_Comp": { + "title": "NASEM Comp data", + "description": "Nutritional information for each feed, following NASEM (2021) guidelines.", + "path": "input/data/feed/NASEM_Comp_with_TDN_urea.csv", + "type": "csv", + "properties": "NASEM_Comp_properties" + }, + "manure_management": { + "title": "Manure Processor Configurations", + "description": "Configurations of manure processors (handlers, separators, digesters, and storages) used in Manure module.", + "path": "input/data/manure/example_freestall_processor_configs.json", + "type": "json", + "properties": "manure_management_properties" + }, + "manure_processor_connection": { + "title": "Manure Processor Connections", + "description": "The connection configs for all manure processors.", + "path": "input/data/manure/example_freestall_processor_connections.json", + "type": "json", + "properties": "manure_processor_connection_properties" + }, + "crop_configurations": { + "title": "Crop Configurations", + "description": "Configurations for how crops grow and are managed.", + "path": "input/data/crop_configurations/default_crop_configs.json", + "type": "json", + "properties": "crop_configuration_properties" + }, + "field_1": { + "title": "Field specification", + "description": "Field characteristics and references to field management specifications.", + "path": "input/data/field/example_small_field_corn_alf_silage.json", + "type": "json", + "properties": "field_properties" + }, + "soil_1": { + "title": "Soil data", + "description": "Characteristic soil information, including composition, slope, and layer details.", + "path": "input/data/soil/example_soil.json", + "type": "json", + "properties": "soil_profile_properties" + }, + "Corn-Alf-Silage": { + "title": "Crop data", + "description": "Crop selection and detailed rotation schedules.", + "path": "input/data/crop/example_alf_corn_silage_rotation.json", + "type": "json", + "properties": "crop_schedule_properties" + }, + "fertilizer_schedule_1": { + "title": "Fertilizer schedule.", + "description": "Fertilizer types available, and their application schedule.", + "path": "input/data/fertilizer_schedule/example_sm_alf_corn_fertilizer.json", + "type": "json", + "properties": "fertilizer_schedule_properties" + }, + "manure_schedule_1": { + "title": "Manure schedule.", + "description": "Specifies manure applications to a field.", + "path": "input/data/manure_schedule/example_sm_corn_alf_manure_schedule.json", + "type": "json", + "properties": "manure_schedule_properties" + }, + "tillage_schedule_1": { + "title": "Tillage schedule.", + "description": "Schedule of tillage applications for a field.", + "path": "input/data/tillage_schedule/no_till.json", + "type": "json", + "properties": "tillage_schedule_properties" + }, + "field_2": { + "title": "Field specification", + "description": "Field characteristics and references to field management specifications.", + "path": "input/data/field/example_small_field_corn_grain_alf_hay.json", + "type": "json", + "properties": "field_properties" + }, + "soil_2": { + "title": "Soil data", + "description": "Characteristic soil information, including composition, slope, and layer details.", + "path": "input/data/soil/example_soil_2.json", + "type": "json", + "properties": "soil_profile_properties" + }, + "CornGrain-AlfHay": { + "title": "Crop data", + "description": "Crop selection and detailed rotation schedules.", + "path": "input/data/crop/example_alf_hay_corn_grain_rotation.json", + "type": "json", + "properties": "crop_schedule_properties" + }, + "fertilizer_schedule_2": { + "title": "Fertilizer schedule.", + "description": "Fertilizer types available, and their application schedule.", + "path": "input/data/fertilizer_schedule/example_sm_alf_corn_fertilizer.json", + "type": "json", + "properties": "fertilizer_schedule_properties" + }, + "manure_schedule_2": { + "title": "Manure schedule.", + "description": "Specifies manure applications to a field.", + "path": "input/data/manure_schedule/example_sm_corn_alf_manure_schedule.json", + "type": "json", + "properties": "manure_schedule_properties" + }, + "tillage_schedule_2": { + "title": "Tillage schedule.", + "description": "Schedule of tillage applications for a field.", + "path": "input/data/tillage_schedule/no_till.json", + "type": "json", + "properties": "tillage_schedule_properties" + }, + "weather": { + "title": "Weather data", + "description": "Weather data used during the simulation, including date, precipitation, temperature, etc.", + "path": "input/data/weather/example_temperate_weather.csv", + "type": "csv", + "properties": "weather_properties" + }, + "user_feeds": { + "title": "User Feed", + "description": "Summary of feeds available for use in the simulation, including their RuFaS ID, short descriptors, and which nutrient composition file they are included in.", + "path": "input/data/feed/user_feeds.csv", + "type": "csv", + "properties": "user_feeds_properties" + }, + "tractor_dataset": { + "title": "Tractor Dataset", + "description": "Details agricultural machinery operations for various crops, including tractor size, operations (planting, mowing, collection, etc.), implement details, operational parameters (depth, width, mass), and throughput metrics, spanning different crop types and soil management practices.", + "path": "input/data/EEE/tractor_dataset.csv", + "type": "csv", + "properties": "tractor_dataset_properties" + }, + "EEE_constants": { + "title": "EEE Constants", + "description": "The constants that are used in EEE module.", + "path": "input/data/EEE/constants.json", + "type": "json", + "properties": "EEE_constants_properties" + }, + "feed_management": { + "title": "Feed Management", + "description": "Configurations for feed storage units.", + "path": "input/data/feed_management/example_feed_storage_configs.json", + "type": "json", + "properties": "feed_storage_configurations" + }, + "feed_storage_configurations": { + "title": "Feed Management", + "description": "Configurations for feed storage units.", + "path": "input/data/feed_management/example_feed_storage_configs.json", + "type": "json", + "properties": "feed_storage_configurations" + }, + "feed_storage_instances": { + "title": "Feed Storages", + "description": "Names of feed storage configs used in the simulation.", + "path": "input/data/feed_management/example_freestall_feed_storages.json", + "type": "json", + "properties": "feed_storage_instances" + } + }, + "runtime_metadata": { + "EEE_econ": { + "path": "input/metadata/EEE/econ_metadata.json" + } + } +} \ No newline at end of file diff --git a/input/task_manager_metadata.json b/input/task_manager_metadata.json index bdcc1f0bc2..c6b065ad5c 100644 --- a/input/task_manager_metadata.json +++ b/input/task_manager_metadata.json @@ -3,7 +3,7 @@ "tasks": { "title": "Task manager data", "description": "Configuration file for general simulation parameters.", - "path": "input/data/tasks/example_field_only_task.json", + "path": "input/data/tasks/example_field_with_storage_task.json", "type": "json", "properties": "tasks_properties" } From c23078c2cf109c8c741d3e6641ac44aa32e1f875 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 4 Apr 2026 10:06:27 +0000 Subject: [PATCH 05/19] Apply Black Formatting --- RUFAS/simulation_engine.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/RUFAS/simulation_engine.py b/RUFAS/simulation_engine.py index 6ebf0a4966..b4fcb5a2ae 100644 --- a/RUFAS/simulation_engine.py +++ b/RUFAS/simulation_engine.py @@ -111,7 +111,7 @@ 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 + simulation_type.FIELD_ONLY: self._execute_field_only_simulation, } self._initialize_simulation() @@ -229,9 +229,7 @@ def _execute_full_farm_daily_simulation(self) -> None: def _execute_field_only_simulation(self) -> None: manure_applications: list[ManureEventNutrientRequestResults] = self.generate_daily_manure_applications() - self.field_manager.daily_update_routine( - self.weather, self.time, manure_applications - ) + self.field_manager.daily_update_routine(self.weather, self.time, manure_applications) self._report_daily_records() From 88522d41bbf2b7911a675dc47a79ff2b0be421c0 Mon Sep 17 00:00:00 2001 From: Matthew Liu Date: Sat, 4 Apr 2026 20:16:31 +0900 Subject: [PATCH 06/19] Updated metadata and added overwritten functions --- RUFAS/simulation_engine.py | 47 +++++++++++++++++++++----- input/metadata/properties/default.json | 2 +- tests/test_simulation_engine.py | 4 +-- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/RUFAS/simulation_engine.py b/RUFAS/simulation_engine.py index 6ebf0a4966..fa1e8f47a8 100644 --- a/RUFAS/simulation_engine.py +++ b/RUFAS/simulation_engine.py @@ -111,7 +111,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 + simulation_type.FIELD_ONLY: self._execute_field_only_simulation, + simulation_type.FIELD_WITH_STORAGE: self._execute_field_with_storage_simulation } self._initialize_simulation() @@ -211,7 +212,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) @@ -226,12 +227,33 @@ def _execute_full_farm_daily_simulation(self) -> None: self._advance_time() + def _execute_field_with_storage_simulation(self) -> None: + """ + Executes the daily simulation routines for a farm with only the field and storage modules. + + Daily Field and Feed Simulation Process: + 1. Field operations (manure applications, harvesting) + 4. Record keeping (time, weather, purchased feeds fed emissions) + 5. Advance simulation date + + """ + self._execute_daily_field_with_storage_operations() + + self._report_daily_records() + + self._advance_time() + def _execute_field_only_simulation(self) -> None: - manure_applications: list[ManureEventNutrientRequestResults] = self.generate_daily_manure_applications() + """ + Executes the daily simulation routines for a field only modules. - self.field_manager.daily_update_routine( - self.weather, self.time, manure_applications - ) + 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() @@ -250,7 +272,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) @@ -261,13 +283,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) diff --git a/input/metadata/properties/default.json b/input/metadata/properties/default.json index 01e7294c9c..4eaf50e8c7 100644 --- a/input/metadata/properties/default.json +++ b/input/metadata/properties/default.json @@ -17,7 +17,7 @@ "type": "string", "description": "The type of daily simulation to run. Will determine daily simulation function in SimulationEngine.", "default": "full_farm", - "pattern": "^(full_farm|field_and_feed)$" + "pattern": "^(full_farm|field_and_feed|field_only|field_with_storage)$" }, "nutrient_standard": { "type": "string", diff --git a/tests/test_simulation_engine.py b/tests/test_simulation_engine.py index 01f710d2b1..fa5991af0a 100644 --- a/tests/test_simulation_engine.py +++ b/tests/test_simulation_engine.py @@ -327,7 +327,7 @@ def test_execute_daily_field_operations( simulation_engine.feed_manager = MagicMock() # Act - result = simulation_engine._execute_daily_field_operations() + result = simulation_engine._execute_daily_field_with_storage_operations() # Assert mock_generate_daily_manure_applications.assert_called_once_with() @@ -371,7 +371,7 @@ def test_execute_daily_field_operations_no_harvested_crops( simulation_engine.feed_manager = MagicMock() # Act - result = simulation_engine._execute_daily_field_operations() + result = simulation_engine._execute_daily_field_with_storage_operations() # Assert mock_generate_daily_manure_applications.assert_called_once_with() From 3f81d929ff70aa5f4dff9f88b5c71654d33888da Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 4 Apr 2026 11:20:01 +0000 Subject: [PATCH 07/19] Apply Black Formatting --- RUFAS/simulation_engine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RUFAS/simulation_engine.py b/RUFAS/simulation_engine.py index fa1e8f47a8..7663d5de45 100644 --- a/RUFAS/simulation_engine.py +++ b/RUFAS/simulation_engine.py @@ -112,7 +112,7 @@ def __init__(self, simulation_type: SimulationType) -> None: 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, - simulation_type.FIELD_WITH_STORAGE: self._execute_field_with_storage_simulation + simulation_type.FIELD_WITH_STORAGE: self._execute_field_with_storage_simulation, } self._initialize_simulation() From f269a2c0fa11a0adc6ec056cdd5d3a80fc3ff7e5 Mon Sep 17 00:00:00 2001 From: matthew7838 Date: Sat, 4 Apr 2026 11:24:08 +0000 Subject: [PATCH 08/19] Update badges on README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 32553e061d..91a1c140d2 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-1198%20errors-red)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml) From 368935a4250ae2b9a5c345ccabd6b11350e4d4c7 Mon Sep 17 00:00:00 2001 From: Matthew Liu Date: Sat, 4 Apr 2026 20:26:09 +0900 Subject: [PATCH 09/19] Updated changelog.md --- RUFAS/simulation_engine.py | 6 +++--- changelog.md | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/RUFAS/simulation_engine.py b/RUFAS/simulation_engine.py index fa1e8f47a8..cac7123a81 100644 --- a/RUFAS/simulation_engine.py +++ b/RUFAS/simulation_engine.py @@ -231,10 +231,10 @@ def _execute_field_with_storage_simulation(self) -> None: """ Executes the daily simulation routines for a farm with only the field and storage modules. - Daily Field and Feed Simulation Process: + Daily Field With Storage Simulation Process: 1. Field operations (manure applications, harvesting) - 4. Record keeping (time, weather, purchased feeds fed emissions) - 5. Advance simulation date + 2. Record keeping (time, weather, purchased feeds fed emissions) + 3. Advance simulation date """ self._execute_daily_field_with_storage_operations() diff --git a/changelog.md b/changelog.md index 5f7c28eec3..50c18fb55c 100644 --- a/changelog.md +++ b/changelog.md @@ -57,6 +57,7 @@ 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. ### v1.0.0 From 6b93d7ba728ba78872b25b5a1d7612a55bed97ab Mon Sep 17 00:00:00 2001 From: Matthew Liu Date: Sat, 4 Apr 2026 20:31:48 +0900 Subject: [PATCH 10/19] Updated tests --- RUFAS/simulation_engine.py | 2 + tests/test_simulation_engine.py | 120 ++++++++++++++++++++++++++++++-- 2 files changed, 118 insertions(+), 4 deletions(-) diff --git a/RUFAS/simulation_engine.py b/RUFAS/simulation_engine.py index 12d205b28c..57c98d6e9d 100644 --- a/RUFAS/simulation_engine.py +++ b/RUFAS/simulation_engine.py @@ -48,6 +48,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 diff --git a/tests/test_simulation_engine.py b/tests/test_simulation_engine.py index fa5991af0a..ae0be9a5a5 100644 --- a/tests/test_simulation_engine.py +++ b/tests/test_simulation_engine.py @@ -41,6 +41,8 @@ def test_simulation_type_enum_values() -> None: """Unit test for SimulationType enum values.""" assert SimulationType.FULL_FARM.value == "full_farm" assert SimulationType.FIELD_AND_FEED.value == "field_and_feed" + assert SimulationType.FIELD_ONLY.value == "field_only" + assert SimulationType.FIELD_WITH_STORAGE.value == "field_with_storage" @pytest.mark.parametrize( @@ -48,6 +50,8 @@ def test_simulation_type_enum_values() -> None: [ (SimulationType.FULL_FARM, True), (SimulationType.FIELD_AND_FEED, False), + (SimulationType.FIELD_ONLY, False), + (SimulationType.FIELD_WITH_STORAGE, False), ], ) def test_simulate_animals( @@ -60,7 +64,11 @@ def test_simulate_animals( def test_non_animal_simulation_types() -> None: """Unit test for SimulationType._non_animal_simulation_types.""" - assert SimulationType._non_animal_simulation_types() == {SimulationType.FIELD_AND_FEED} + assert SimulationType._non_animal_simulation_types() == { + SimulationType.FIELD_AND_FEED, + SimulationType.FIELD_ONLY, + SimulationType.FIELD_WITH_STORAGE, + } @pytest.mark.parametrize( @@ -68,6 +76,8 @@ def test_non_animal_simulation_types() -> None: [ ("full_farm", SimulationType.FULL_FARM), ("field_and_feed", SimulationType.FIELD_AND_FEED), + ("field_only", SimulationType.FIELD_ONLY), + ("field_with_storage", SimulationType.FIELD_WITH_STORAGE), ], ) def test_get_simulation_type_valid( @@ -84,7 +94,10 @@ def test_get_simulation_type_invalid() -> None: with pytest.raises( ValueError, - match=("Unknown simulation type: not_a_real_simulation. " "Expected one of: full_farm, field_and_feed."), + match=( + "Unknown simulation type: not_a_real_simulation. " + "Expected one of: full_farm, field_and_feed, field_only, field_with_storage." + ), ): SimulationType.get_simulation_type(invalid_simulation_type) @@ -191,7 +204,7 @@ def test_execute_full_farm_daily_simulation( mock_execute_daily_field_operations = mocker.patch.object( simulation_engine, - "_execute_daily_field_operations", + "_execute_daily_field_with_storage_operations", return_value=daily_harvested_crops, ) mock_build_harvest_schedule = mocker.patch.object( @@ -243,7 +256,7 @@ def test_execute_field_and_feed_daily_simulation( parent.attach_mock( mocker.patch.object( simulation_engine, - "_execute_daily_field_operations", + "_execute_daily_field_with_storage_operations", return_value=daily_harvested_crops, ), "execute_daily_field_operations", @@ -384,6 +397,105 @@ def test_execute_daily_field_operations_no_harvested_crops( assert result == harvested_crops +def test_execute_daily_field_only_operations( + simulation_engine: SimulationEngine, + mocker: MockerFixture, +) -> None: + """ + Unit test for function _execute_daily_field_only_operations + in file RUFAS/simulation_engine.py + """ + manure_applications = [MagicMock(), MagicMock()] + harvested_crops = [MagicMock(), MagicMock()] + + mock_generate_daily_manure_applications = mocker.patch.object( + simulation_engine, + "generate_daily_manure_applications", + return_value=manure_applications, + ) + + simulation_engine.weather = MagicMock() + simulation_engine.time = MagicMock() + simulation_engine.field_manager = MagicMock() + simulation_engine.field_manager.daily_update_routine.return_value = harvested_crops + simulation_engine.feed_manager = MagicMock() + + result = simulation_engine._execute_daily_field_only_operations() + + mock_generate_daily_manure_applications.assert_called_once_with() + simulation_engine.field_manager.daily_update_routine.assert_called_once_with( + simulation_engine.weather, + simulation_engine.time, + manure_applications, + ) + simulation_engine.feed_manager.receive_crop.assert_not_called() + assert result == harvested_crops + + +def test_execute_field_only_simulation( + simulation_engine: SimulationEngine, + mocker: MockerFixture, +) -> None: + """ + Unit test for function _execute_field_only_simulation + in file RUFAS/simulation_engine.py + """ + parent = MagicMock() + + parent.attach_mock( + mocker.patch.object(simulation_engine, "_execute_daily_field_only_operations"), + "execute_daily_field_only_operations", + ) + parent.attach_mock( + mocker.patch.object(simulation_engine, "_report_daily_records"), + "report_daily_records", + ) + parent.attach_mock( + mocker.patch.object(simulation_engine, "_advance_time"), + "advance_time", + ) + + simulation_engine._execute_field_only_simulation() + + assert parent.mock_calls == [ + call.execute_daily_field_only_operations(), + call.report_daily_records(), + call.advance_time(), + ] + + +def test_execute_field_with_storage_simulation( + simulation_engine: SimulationEngine, + mocker: MockerFixture, +) -> None: + """ + Unit test for function _execute_field_with_storage_simulation + in file RUFAS/simulation_engine.py + """ + parent = MagicMock() + + parent.attach_mock( + mocker.patch.object(simulation_engine, "_execute_daily_field_with_storage_operations"), + "execute_daily_field_with_storage_operations", + ) + parent.attach_mock( + mocker.patch.object(simulation_engine, "_report_daily_records"), + "report_daily_records", + ) + parent.attach_mock( + mocker.patch.object(simulation_engine, "_advance_time"), + "advance_time", + ) + + simulation_engine._execute_field_with_storage_simulation() + + assert parent.mock_calls == [ + call.execute_daily_field_with_storage_operations(), + call.report_daily_records(), + call.advance_time(), + ] + + def test_build_harvest_schedule_no_feed_recalculation( simulation_engine: SimulationEngine, mocker: MockerFixture, From 3e9212d2957c0926b8232e790324c3f62678fbbf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 4 Apr 2026 11:33:32 +0000 Subject: [PATCH 11/19] Apply Black Formatting From cffe1a503d5aa47c46d2d41e6dad62400b93cd87 Mon Sep 17 00:00:00 2001 From: matthew7838 Date: Sat, 4 Apr 2026 11:37:57 +0000 Subject: [PATCH 12/19] Update badges on README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 91a1c140d2..32553e061d 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-1198%20errors-red)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml) From 114c46fd46e2f0826fd716b74df7d5e7f475a9c9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 6 Apr 2026 19:02:39 +0000 Subject: [PATCH 13/19] Apply Black Formatting From 27f0566f8d5a46917480e7d0d029d73be76d4a00 Mon Sep 17 00:00:00 2001 From: Matthew Liu Date: Tue, 7 Apr 2026 17:33:43 +0900 Subject: [PATCH 14/19] Addressed Niko's comments on improving unit test --- tests/test_simulation_engine.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_simulation_engine.py b/tests/test_simulation_engine.py index ae0be9a5a5..a88df4bd4d 100644 --- a/tests/test_simulation_engine.py +++ b/tests/test_simulation_engine.py @@ -91,12 +91,13 @@ def test_get_simulation_type_valid( def test_get_simulation_type_invalid() -> None: """Unit test for SimulationType.get_simulation_type with an invalid value.""" invalid_simulation_type = "not_a_real_simulation" + valid_simulation_types = ", ".join(simulation_type.value for simulation_type in SimulationType) with pytest.raises( ValueError, match=( - "Unknown simulation type: not_a_real_simulation. " - "Expected one of: full_farm, field_and_feed, field_only, field_with_storage." + f"Unknown simulation type: {invalid_simulation_type}. " + f"Expected one of: {valid_simulation_types}." ), ): SimulationType.get_simulation_type(invalid_simulation_type) From 9c9f991a2c0aff3caeb3c36c0468bcd1ece30e9b Mon Sep 17 00:00:00 2001 From: Matthew Liu Date: Tue, 7 Apr 2026 17:34:43 +0900 Subject: [PATCH 15/19] Revert changes for testing purpose --- input/task_manager_metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input/task_manager_metadata.json b/input/task_manager_metadata.json index c6b065ad5c..5674a5fcdc 100644 --- a/input/task_manager_metadata.json +++ b/input/task_manager_metadata.json @@ -3,7 +3,7 @@ "tasks": { "title": "Task manager data", "description": "Configuration file for general simulation parameters.", - "path": "input/data/tasks/example_field_with_storage_task.json", + "path": "input/data/tasks/example_freestall_task.json", "type": "json", "properties": "tasks_properties" } From 5915db402db89bfda8b7af6dab37be7ac41e0804 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 7 Apr 2026 08:34:45 +0000 Subject: [PATCH 16/19] Apply Black Formatting From f5f8ff1fc6d11895fe70f8ea9e978ca4060efeaf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 7 Apr 2026 08:36:46 +0000 Subject: [PATCH 17/19] Apply Black Formatting --- tests/test_simulation_engine.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_simulation_engine.py b/tests/test_simulation_engine.py index a88df4bd4d..9fa2c78a4b 100644 --- a/tests/test_simulation_engine.py +++ b/tests/test_simulation_engine.py @@ -95,10 +95,7 @@ def test_get_simulation_type_invalid() -> None: with pytest.raises( ValueError, - match=( - f"Unknown simulation type: {invalid_simulation_type}. " - f"Expected one of: {valid_simulation_types}." - ), + match=(f"Unknown simulation type: {invalid_simulation_type}. " f"Expected one of: {valid_simulation_types}."), ): SimulationType.get_simulation_type(invalid_simulation_type) From a31591fd012aba577d29968501067e437f48195d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 7 Apr 2026 14:45:02 +0000 Subject: [PATCH 18/19] Apply Black Formatting From 27d9be515b51d1de68460121f6f784ae2b4a1e3c Mon Sep 17 00:00:00 2001 From: Matthew Liu Date: Tue, 14 Apr 2026 22:58:29 +0900 Subject: [PATCH 19/19] Separate field manure supplier into data structures --- RUFAS/biophysical/manure/manure_manager.py | 2 +- .../manure => data_structures}/field_manure_supplier.py | 0 RUFAS/simulation_engine.py | 5 ++--- .../test_manure/test_field_manure_supplier.py | 2 +- .../test_manure/test_manure_manager/test_manure_manager.py | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) rename RUFAS/{biophysical/manure => data_structures}/field_manure_supplier.py (100%) diff --git a/RUFAS/biophysical/manure/manure_manager.py b/RUFAS/biophysical/manure/manure_manager.py index c511ddbbdb..384705b7de 100644 --- a/RUFAS/biophysical/manure/manure_manager.py +++ b/RUFAS/biophysical/manure/manure_manager.py @@ -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 diff --git a/RUFAS/biophysical/manure/field_manure_supplier.py b/RUFAS/data_structures/field_manure_supplier.py similarity index 100% rename from RUFAS/biophysical/manure/field_manure_supplier.py rename to RUFAS/data_structures/field_manure_supplier.py diff --git a/RUFAS/simulation_engine.py b/RUFAS/simulation_engine.py index 9466434412..ff4aa354e1 100644 --- a/RUFAS/simulation_engine.py +++ b/RUFAS/simulation_engine.py @@ -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 @@ -324,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 diff --git a/tests/test_biophysical/test_manure/test_field_manure_supplier.py b/tests/test_biophysical/test_manure/test_field_manure_supplier.py index 302e45ed21..3a41b8ccac 100644 --- a/tests/test_biophysical/test_manure/test_field_manure_supplier.py +++ b/tests/test_biophysical/test_manure/test_field_manure_supplier.py @@ -1,6 +1,6 @@ import pytest -from RUFAS.biophysical.manure.field_manure_supplier import FieldManureSupplier +from RUFAS.data_structures.field_manure_supplier import FieldManureSupplier from RUFAS.data_structures.manure_to_crop_soil_connection import NutrientRequest, NutrientRequestResults from RUFAS.data_structures.manure_types import ManureType diff --git a/tests/test_biophysical/test_manure/test_manure_manager/test_manure_manager.py b/tests/test_biophysical/test_manure/test_manure_manager/test_manure_manager.py index 041987a735..61190a9c98 100644 --- a/tests/test_biophysical/test_manure/test_manure_manager/test_manure_manager.py +++ b/tests/test_biophysical/test_manure/test_manure_manager/test_manure_manager.py @@ -6,7 +6,7 @@ from pytest_mock import MockerFixture, MockFixture from RUFAS.biophysical.manure.digester.digester import Digester -from RUFAS.biophysical.manure.field_manure_supplier import FieldManureSupplier +from RUFAS.data_structures.field_manure_supplier import FieldManureSupplier from RUFAS.biophysical.manure.manure_manager import STORAGE_CLASS_TO_TYPE, ManureManager from RUFAS.biophysical.manure.manure_nutrient_manager import ManureNutrientManager from RUFAS.biophysical.manure.processor import Processor