From edc409136230aa7ba92b9b6fbfff832500f2be57 Mon Sep 17 00:00:00 2001 From: Marco Berzborn Date: Mon, 4 May 2026 12:26:39 +0200 Subject: [PATCH 01/12] Re-add the DE auralization method --- app/services/simulation_service.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/services/simulation_service.py b/app/services/simulation_service.py index 17ca354..5601fbd 100644 --- a/app/services/simulation_service.py +++ b/app/services/simulation_service.py @@ -448,6 +448,13 @@ def run_solver(simulation_run_id: int, json_path: str): # the idea is to have one shared pipeline across all # methods. match simulation_method: + case "DE": + # TODO: This function is not a general auralization function and should be renamed + imp_tot, fs = auralization_calculation( + None, + json_path.replace(".json", "_pressure.csv"), + json_path.replace(".json", ".wav"), + ) case "DG": imp_tot, fs = auralization_calculation_DG( None, From cae8260db72e9a3bbfb9365fadff8b60f4bfa61b Mon Sep 17 00:00:00 2001 From: Marco Berzborn Date: Thu, 7 May 2026 18:25:31 +0200 Subject: [PATCH 02/12] CI: Mark outdated settings tests for skipping (#85) ### Proposed changes It seems they were not updated on code refactoring. I'm marking them to skip and create an issue to keep track of the issue. --- tests/unit/services/test_setting_service.py | 28 ++++++++++++--------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/unit/services/test_setting_service.py b/tests/unit/services/test_setting_service.py index 5e6bb64..c457861 100644 --- a/tests/unit/services/test_setting_service.py +++ b/tests/unit/services/test_setting_service.py @@ -7,6 +7,7 @@ from app.types.Task import TaskType from tests.unit import BaseTestCase +import pytest class UsersUnitTests(BaseTestCase): def setUp(self): @@ -15,6 +16,7 @@ def setUp(self): """ super().setUp() + @pytest.mark.skip(reason="It seems this test was not updated during refactoring.") def test_insert_initial_settings(self): """ Test that initial settings are correctly inserted into the database. @@ -25,6 +27,7 @@ def test_insert_initial_settings(self): self.assertTrue(len(settings) > 0) + @pytest.mark.skip(reason="It seems this test was not updated during refactoring.") def test_update_settings(self): with self.app.app_context(): setting_service.update_settings() @@ -32,17 +35,18 @@ def test_update_settings(self): self.assertTrue(len(settings) > 0) - # def test_get_setting_by_type(self): - # """ - # Test that setting is correctly retrieved by simulationType. - # """ - # with self.app.app_context(): - # setting_service.insert_initial_settings() + @pytest.mark.skip(reason="It seems this test was not updated during refactoring.") + def test_get_setting_by_type(self): + """ + Test that setting is correctly retrieved by simulationType. + """ + with self.app.app_context(): + setting_service.insert_initial_settings() - # for task_type in {"DE", "DG", "BOTH"}: - # if task_type in TaskType.__members__.keys(): - # setting = setting_service.get_setting_by_type(task_type) - # self.assertIsInstance(setting, Dict) - # self.assertTrue(len(setting) > 0) + for task_type in {"DE", "DG", "BOTH"}: + if task_type in TaskType.__members__.keys(): + setting = setting_service.get_setting_by_type(task_type) + self.assertIsInstance(setting, Dict) + self.assertTrue(len(setting) > 0) - # self.assertRaises(HTTPException, setting_service.get_setting_by_type, "SOMTHING_DOES_NOT_EXIST") + self.assertRaises(HTTPException, setting_service.get_setting_by_type, "SOMTHING_DOES_NOT_EXIST") From 4feabcfa7e9f974a4d2d87583a4f44c73b7b66d1 Mon Sep 17 00:00:00 2001 From: Marco Berzborn Date: Thu, 7 May 2026 18:33:53 +0200 Subject: [PATCH 03/12] CI: Ignore tests which require more elaborate setup (#84) The executor tests require setting up Mocks and Docker. The set-up for these is currently located in .github/workflows/engd2026.yml --- .github/workflows/pytest.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 7ffe1f7..be41c2c 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -26,4 +26,10 @@ jobs: run: python -m pip install -r requirements.txt - name: Run tests - run: python -m pytest -v tests/unit/services/ tests/unit/models + run: | + python -m pytest -v \ + tests/unit/services/ \ + tests/unit/models \ + --ignore=tests/unit/executors/ \ + --ignore=tests/unit/services/test_discovery_service.py \ + --ignore=tests/unit/services/test_run_solver.py From b42042d70d38d1585625671ff8d2abbf3ae2ef06 Mon Sep 17 00:00:00 2001 From: Marco Berzborn Date: Fri, 8 May 2026 17:53:57 +0200 Subject: [PATCH 04/12] DOC: Cleanup documentation of removed code (#88) ### Proposed changes - Remove the documentation entries for removed simulation backends - Remove obsolete entries for example gallery - Remove example gallery plugin --- docs/source/conf.py | 7 ------- docs/source/includes/api_documentation.rst | 4 ---- .../includes/api_documentation/DEinterface.rst | 7 ------- .../includes/api_documentation/DGinterface.rst | 7 ------- .../api_documentation/MyNewMethodinterface.rst | 7 ------- docs/source/index.rst | 12 ------------ requirements.txt | 1 - 7 files changed, 45 deletions(-) delete mode 100644 docs/source/includes/api_documentation/DEinterface.rst delete mode 100644 docs/source/includes/api_documentation/DGinterface.rst delete mode 100644 docs/source/includes/api_documentation/MyNewMethodinterface.rst diff --git a/docs/source/conf.py b/docs/source/conf.py index f9c7b29..1b6efcc 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,6 @@ "myst_parser", "sphinx_design", "sphinx_copybutton", - "sphinx_gallery.gen_gallery", ] source_suffix = [".rst", ".md"] @@ -59,9 +58,3 @@ html_context = { "default_mode": "light" } - -sphinx_gallery_conf = { - "examples_dirs": "../../simulation-backend/examples", # path to your example scripts - "gallery_dirs": "auto_examples", # path to where to save gallery generated output - "image_scrapers": ("matplotlib",), -} diff --git a/docs/source/includes/api_documentation.rst b/docs/source/includes/api_documentation.rst index 5282c39..7b4a1f6 100644 --- a/docs/source/includes/api_documentation.rst +++ b/docs/source/includes/api_documentation.rst @@ -4,7 +4,3 @@ API Documentation .. toctree:: :maxdepth: 1 :caption: Implemented Interfaces: - - api_documentation/MyNewMethodinterface - api_documentation/DEinterface - api_documentation/DGinterface diff --git a/docs/source/includes/api_documentation/DEinterface.rst b/docs/source/includes/api_documentation/DEinterface.rst deleted file mode 100644 index f625c72..0000000 --- a/docs/source/includes/api_documentation/DEinterface.rst +++ /dev/null @@ -1,7 +0,0 @@ -Acoustic Diffusion Equation Interface -===================================== - -.. automodule:: simulation_backend.DEinterface - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/includes/api_documentation/DGinterface.rst b/docs/source/includes/api_documentation/DGinterface.rst deleted file mode 100644 index 086ad08..0000000 --- a/docs/source/includes/api_documentation/DGinterface.rst +++ /dev/null @@ -1,7 +0,0 @@ -Discontinuous Galerkin (DG) Interface -===================================== - -.. automodule:: simulation_backend.DGinterface - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/includes/api_documentation/MyNewMethodinterface.rst b/docs/source/includes/api_documentation/MyNewMethodinterface.rst deleted file mode 100644 index e8bbbd5..0000000 --- a/docs/source/includes/api_documentation/MyNewMethodinterface.rst +++ /dev/null @@ -1,7 +0,0 @@ -Example Interface -================= - -.. automodule:: simulation_backend.MyNewMethodinterface - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/index.rst b/docs/source/index.rst index 7eca18b..3580fdb 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -21,15 +21,3 @@ Please follow the steps provided here to setup the CHORAS backend. :caption: Contribution Guidelines includes/contributing.rst - -.. toctree:: - :maxdepth: 1 - :caption: API Reference - - includes/api_documentation.rst - -.. toctree:: - :maxdepth: 1 - :caption: Example Gallery - - auto_examples/index diff --git a/requirements.txt b/requirements.txt index d1727ab..5d48649 100644 --- a/requirements.txt +++ b/requirements.txt @@ -147,7 +147,6 @@ pydata-sphinx-theme # Docs theme myst_parser # Docs, required for markdown support sphinx-design # Docs, required for tabs and other design elements sphinx-copybutton # Docs, required for copy buttons in code blocks -sphinx-gallery # Docs, required for example gallery matplotlib # Docs, required for plot directive pyroomacoustics docker From 9cba5088387289a2da286b33eb3d7aa5abc8308b Mon Sep 17 00:00:00 2001 From: Marco Berzborn Date: Fri, 8 May 2026 17:57:24 +0200 Subject: [PATCH 05/12] Add services to API documentation (some docstrings are not populated yet) --- docs/source/includes/api_documentation.rst | 16 +++++++++++++++- .../api_documentation/auralization_service.rst | 7 +++++++ .../api_documentation/discovery_service.rst | 7 +++++++ .../includes/api_documentation/executors.rst | 12 ++++++++++++ .../api_documentation/export_service.rst | 7 +++++++ .../includes/api_documentation/file_service.rst | 7 +++++++ .../api_documentation/geometry_service.rst | 7 +++++++ .../api_documentation/material_service.rst | 7 +++++++ .../includes/api_documentation/mesh_service.rst | 7 +++++++ .../includes/api_documentation/model_service.rst | 7 +++++++ .../api_documentation/project_service.rst | 7 +++++++ .../api_documentation/setting_service.rst | 7 +++++++ .../api_documentation/simulation_service.rst | 7 +++++++ docs/source/index.rst | 7 +++++++ 14 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 docs/source/includes/api_documentation/auralization_service.rst create mode 100644 docs/source/includes/api_documentation/discovery_service.rst create mode 100644 docs/source/includes/api_documentation/executors.rst create mode 100644 docs/source/includes/api_documentation/export_service.rst create mode 100644 docs/source/includes/api_documentation/file_service.rst create mode 100644 docs/source/includes/api_documentation/geometry_service.rst create mode 100644 docs/source/includes/api_documentation/material_service.rst create mode 100644 docs/source/includes/api_documentation/mesh_service.rst create mode 100644 docs/source/includes/api_documentation/model_service.rst create mode 100644 docs/source/includes/api_documentation/project_service.rst create mode 100644 docs/source/includes/api_documentation/setting_service.rst create mode 100644 docs/source/includes/api_documentation/simulation_service.rst diff --git a/docs/source/includes/api_documentation.rst b/docs/source/includes/api_documentation.rst index 7b4a1f6..a124d50 100644 --- a/docs/source/includes/api_documentation.rst +++ b/docs/source/includes/api_documentation.rst @@ -1,6 +1,20 @@ API Documentation ================= +Services +-------- + .. toctree:: :maxdepth: 1 - :caption: Implemented Interfaces: + + api_documentation/executors.rst + api_documentation/discovery_service.rst + api_documentation/export_service.rst + api_documentation/file_service.rst + api_documentation/geometry_service.rst + api_documentation/material_service.rst + api_documentation/mesh_service.rst + api_documentation/model_service.rst + api_documentation/project_service.rst + api_documentation/setting_service.rst + api_documentation/simulation_service.rst diff --git a/docs/source/includes/api_documentation/auralization_service.rst b/docs/source/includes/api_documentation/auralization_service.rst new file mode 100644 index 0000000..d434024 --- /dev/null +++ b/docs/source/includes/api_documentation/auralization_service.rst @@ -0,0 +1,7 @@ +Auralization Service +==================== + +.. automodule:: app.services.auralization_service + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/includes/api_documentation/discovery_service.rst b/docs/source/includes/api_documentation/discovery_service.rst new file mode 100644 index 0000000..958805f --- /dev/null +++ b/docs/source/includes/api_documentation/discovery_service.rst @@ -0,0 +1,7 @@ +Discovery Service +================= + +.. automodule:: app.services.discovery_service + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/includes/api_documentation/executors.rst b/docs/source/includes/api_documentation/executors.rst new file mode 100644 index 0000000..d37d7a2 --- /dev/null +++ b/docs/source/includes/api_documentation/executors.rst @@ -0,0 +1,12 @@ +Executors +========= + +.. automodule:: app.services.executors.local_executor + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: app.services.executors.cloud_executor + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/includes/api_documentation/export_service.rst b/docs/source/includes/api_documentation/export_service.rst new file mode 100644 index 0000000..ec94536 --- /dev/null +++ b/docs/source/includes/api_documentation/export_service.rst @@ -0,0 +1,7 @@ +Export Service +============== + +.. automodule:: app.services.export_service + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/includes/api_documentation/file_service.rst b/docs/source/includes/api_documentation/file_service.rst new file mode 100644 index 0000000..a657542 --- /dev/null +++ b/docs/source/includes/api_documentation/file_service.rst @@ -0,0 +1,7 @@ +File Service +============ + +.. automodule:: app.services.file_service + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/includes/api_documentation/geometry_service.rst b/docs/source/includes/api_documentation/geometry_service.rst new file mode 100644 index 0000000..b041409 --- /dev/null +++ b/docs/source/includes/api_documentation/geometry_service.rst @@ -0,0 +1,7 @@ +Geometry Service +================ + +.. automodule:: app.services.geometry_service + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/includes/api_documentation/material_service.rst b/docs/source/includes/api_documentation/material_service.rst new file mode 100644 index 0000000..4b99e0c --- /dev/null +++ b/docs/source/includes/api_documentation/material_service.rst @@ -0,0 +1,7 @@ +Material Service +================ + +.. automodule:: app.services.material_service + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/includes/api_documentation/mesh_service.rst b/docs/source/includes/api_documentation/mesh_service.rst new file mode 100644 index 0000000..67c8847 --- /dev/null +++ b/docs/source/includes/api_documentation/mesh_service.rst @@ -0,0 +1,7 @@ +Mesh Service +============ + +.. automodule:: app.services.mesh_service + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/includes/api_documentation/model_service.rst b/docs/source/includes/api_documentation/model_service.rst new file mode 100644 index 0000000..1a701a8 --- /dev/null +++ b/docs/source/includes/api_documentation/model_service.rst @@ -0,0 +1,7 @@ +Model Service +============= + +.. automodule:: app.services.model_service + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/includes/api_documentation/project_service.rst b/docs/source/includes/api_documentation/project_service.rst new file mode 100644 index 0000000..a2a9f77 --- /dev/null +++ b/docs/source/includes/api_documentation/project_service.rst @@ -0,0 +1,7 @@ +Project Service +=============== + +.. automodule:: app.services.project_service + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/includes/api_documentation/setting_service.rst b/docs/source/includes/api_documentation/setting_service.rst new file mode 100644 index 0000000..f1df6f6 --- /dev/null +++ b/docs/source/includes/api_documentation/setting_service.rst @@ -0,0 +1,7 @@ +Setting Service +=============== + +.. automodule:: app.services.setting_service + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/includes/api_documentation/simulation_service.rst b/docs/source/includes/api_documentation/simulation_service.rst new file mode 100644 index 0000000..6988c7b --- /dev/null +++ b/docs/source/includes/api_documentation/simulation_service.rst @@ -0,0 +1,7 @@ +Simulation Service +================== + +.. automodule:: app.services.simulation_service + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/index.rst b/docs/source/index.rst index 3580fdb..7983640 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -21,3 +21,10 @@ Please follow the steps provided here to setup the CHORAS backend. :caption: Contribution Guidelines includes/contributing.rst + + +.. toctree:: + :maxdepth: 1 + :caption: API References + + includes/api_documentation.rst From 5c986820aab228a83a4429d99c8049e566842600 Mon Sep 17 00:00:00 2001 From: Marco Berzborn Date: Mon, 4 May 2026 12:27:14 +0200 Subject: [PATCH 06/12] Add temporary general wav file writing functionality for all new methods returning a RIR --- app/services/simulation_service.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/app/services/simulation_service.py b/app/services/simulation_service.py index 5601fbd..e3dc1d9 100644 --- a/app/services/simulation_service.py +++ b/app/services/simulation_service.py @@ -463,12 +463,19 @@ def run_solver(simulation_run_id: int, json_path: str): ) # this should be the only thing getting executed case _: - imp_tot, fs = auralization_calculation( - None, - json_path.replace(".json", "_pressure.csv"), - json_path.replace(".json", ".wav"), - ) - + # TODO: instead of reading the rir from the _pressure.csv file, read it from the json file directly + import numpy as np + imp_tot = np.loadtxt(json_path.replace(".json", "_pressure.csv"), delimiter=",") + with open(json_path, "r") as json_file: + input_data = json.load(json_file) + fs = input_data["simulationSettings"]["sampling_rate"] + rir_wav_file_name = json_path.replace(".json", ".wav") + + import pyfar as pf + rir = pf.Signal(imp_tot, fs) + pf.io.write_audio(rir, rir_wav_file_name) + logger.info(f"Impulse response shape: {imp_tot.shape}, sampling rate: {fs}") + # auralization: save the impulse response to xlsx if not ExportHelper.write_data_to_xlsx_file( From e1470917f3a2ef3e4979b8354f47a6a8cb5422e1 Mon Sep 17 00:00:00 2001 From: Marco Berzborn Date: Mon, 4 May 2026 12:32:49 +0200 Subject: [PATCH 07/12] Add initial draft of the mono-aural auralization method --- app/services/auralization_service.py | 46 +++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/app/services/auralization_service.py b/app/services/auralization_service.py index 252128a..09e6705 100644 --- a/app/services/auralization_service.py +++ b/app/services/auralization_service.py @@ -288,15 +288,25 @@ def run_auralization(auralizationId: int) -> None: logger.debug("run auralization calculation") - #TODO: fix behavior for DG auralization, DG method output format - # should be changed. We want a single universal auralization method, - # without having to switch logic between them for each simulation method. match simulation.simulationMethod: case "DE": _, _ = auralization_calculation(signal_file_name, pressure_file_name, wav_output_file_name) case "DG": _, _ = auralization_calculation_DG(signal_file_name, pressure_file_name, wav_output_file_name) - + case _: + #TODO: We want a single universal auralization method, + # without having to switch logic between them for each simulation method. + # This will be implemented in the function mono_aural_auralization, which will be a + # general convolution-based auralization method using the RIR. + # This method does not rely on the pressure.csv file, but the wav file directly + pressure_file_name_wav = os.path.join( + DefaultConfig.UPLOAD_FOLDER_NAME, export.name.replace(".xlsx", ".wav") + ) + mono_aural_auralization( + signal_file_name, + pressure_file_name_wav, + wav_output_file_name + ) auralization.status = Status.Completed @@ -310,6 +320,34 @@ def run_auralization(auralizationId: int) -> None: abort(400, "Error running this auralization") +def mono_aural_auralization( + signal_file_name: str, + impulse_response_file_name_wav: str, + wav_output_file_name: str, + ) -> None: + """Create a mono-aural auralization by convolution. + + If the sampling rates do not match, the impulse response is resampled to + match the sampling rate of the dry input signal. + + Parameters + ---------- + signal_file_name : str + The dry input signal file name (wav format). + impulse_response_file_name_wav : str + The impulse response file name (wav format). + wav_output_file_name : str + The convolved output signal file name (wav format). + """ + + import pyfar as pf + dry_signal = pf.io.read_audio(signal_file_name) + rir = pf.io.read_audio(impulse_response_file_name_wav) + rir_resampled = pf.dsp.resample(rir, dry_signal.sampling_rate) + convolved_signal = pf.dsp.convolve(rir_resampled, dry_signal) + pf.io.write_audio(convolved_signal, wav_output_file_name) + + # TODO: too long code, refactor this function def auralization_calculation_DG( signal_file_name: Optional[str], impulse_response: str, wav_output_file_name: Optional[str] = None From b32e2931d2a37c969127d8b62fbfa2248a31a727 Mon Sep 17 00:00:00 2001 From: Marco Berzborn Date: Mon, 4 May 2026 12:34:16 +0200 Subject: [PATCH 08/12] Add pyfar as dependency (used in the newly added generalized auralization method and result export in the SimulationService and AuralizationService) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5d48649..6ec3c48 100644 --- a/requirements.txt +++ b/requirements.txt @@ -155,4 +155,4 @@ paramiko git+https://github.com/Building-acoustics-TU-Eindhoven/acousticDE.git@d32afb2498e27bd996fc7356d57dc4f1ed76aa44#egg=acousticDE # git+https://github.com/dtu-act/deeponet-acoustic-wave-prop.git@3d3fc5ee952756eedcd4fec3c3674ad829825c7e#egg=deeponet-acoustics git+https://github.com/Building-acoustics-TU-Eindhoven/edg-acoustics.git@08cac98da98ed14ba1366741b1c0644001503b82#egg=edg-acoustics - +pyfar From 264345651d06c71eb36f1b40c3277106f0ba497c Mon Sep 17 00:00:00 2001 From: SilvinWillemsen Date: Mon, 4 May 2026 17:27:47 +0200 Subject: [PATCH 09/12] WIP: general auralization. DG now gets auralized using the Pyfar approach. Also refactored a bit so that a not-working xlsx export will not break the auralization. --- app/services/simulation_service.py | 135 ++++++++++++++++------------- requirements.txt | 2 +- 2 files changed, 75 insertions(+), 62 deletions(-) diff --git a/app/services/simulation_service.py b/app/services/simulation_service.py index e3dc1d9..215f3b9 100644 --- a/app/services/simulation_service.py +++ b/app/services/simulation_service.py @@ -290,6 +290,7 @@ def start_solver_task(simulation_id): "geo_path": geo_path, "results": results_container, "task_id": -1, + "fs_auralization": 44100 }, indent=4, ) @@ -421,73 +422,85 @@ def run_solver(simulation_run_id: int, json_path: str): cancel_flag_path = Path(json_path).parent / f"{result_container['task_id']}.cancel" + # auralization: generate impulse response wav file + # TODO: fix DG method such that this auralization works, + # the idea is to have one shared pipeline across all + # methods. + match simulation_method: + case "DE": + # TODO: This function is not a general auralization function and should be renamed + imp_tot, fs = auralization_calculation( + None, + json_path.replace(".json", "_pressure.csv"), + json_path.replace(".json", ".wav"), + ) + + # this should be the only thing getting executed + case _: + import numpy as np + + with open(json_path, "r") as json_file: + result_container = json.load(json_file) + + imp_tot = np.array(result_container["results"][0]["responses"][0]["receiverResults"]) + + with open(json_path, "r") as json_file: + input_data = json.load(json_file) + if "sampling_rate" in input_data["simulationSettings"]: + fs = input_data["simulationSettings"]["sampling_rate"] + else: + fs = input_data["fs_auralization"] # 44100 by default + + rir_wav_file_name = json_path.replace(".json", ".wav") + + import pyfar as pf + if imp_tot is None or len(imp_tot) == 0: + logger.warning("Impulse response data is empty or missing") + imp_tot = np.zeros(44100) # 1 second of silence at 44.1 kHz + norm_rir = pf.Signal(imp_tot, fs) + else: + rir = pf.Signal(imp_tot, fs) + norm_rir = pf.dsp.normalize(rir) + + pf.io.write_audio(norm_rir, rir_wav_file_name) + logger.info(f"Impulse response shape: {imp_tot.shape}, sampling rate: {fs}") + # logs = container.logs().decode("utf-8") # logger.info(f"{simulation_method} container FULL logs:\n{logs}") if os.path.exists(cancel_flag_path): - logger.info("Cancelled: do not save to xlsx") + logger.info("Cancelled: Not saving to xlsx") else: - logger.info("Saving to xlsx...") - - # save the simulation result json to xlsx - if not ExportHelper.parse_json_file_to_xlsx_file( - json_path, json_path.replace(".json", ".xlsx") - ): - logger.error("Error saving the result to xlsx") - raise "Error saving the result to xlsx" - - # db - save the xlsx file path - export = Export( - name=Path(json_path).name.replace(".json", ".xlsx"), - simulationId=simulation.id, - ) - session.add(export) - - # auralization: generate impulse response wav file - # TODO: fix DG method such that this auralization works, - # the idea is to have one shared pipeline across all - # methods. - match simulation_method: - case "DE": - # TODO: This function is not a general auralization function and should be renamed - imp_tot, fs = auralization_calculation( - None, - json_path.replace(".json", "_pressure.csv"), - json_path.replace(".json", ".wav"), - ) - case "DG": - imp_tot, fs = auralization_calculation_DG( - None, - json_path.replace(".json", "_pressure.csv"), - json_path.replace(".json", ".wav"), - ) - # this should be the only thing getting executed - case _: - # TODO: instead of reading the rir from the _pressure.csv file, read it from the json file directly - import numpy as np - imp_tot = np.loadtxt(json_path.replace(".json", "_pressure.csv"), delimiter=",") - with open(json_path, "r") as json_file: - input_data = json.load(json_file) - fs = input_data["simulationSettings"]["sampling_rate"] - rir_wav_file_name = json_path.replace(".json", ".wav") - - import pyfar as pf - rir = pf.Signal(imp_tot, fs) - pf.io.write_audio(rir, rir_wav_file_name) - logger.info(f"Impulse response shape: {imp_tot.shape}, sampling rate: {fs}") - - - # auralization: save the impulse response to xlsx - if not ExportHelper.write_data_to_xlsx_file( - json_path.replace(".json", ".xlsx"), - CustomExportParametersConfig.impulse_response, - {f"{fs}Hz": imp_tot}, - ): - logger.error( - "Error saving the impulse response to xlsx" + try: + logger.info("Saving to xlsx...") + + # save the simulation result json to xlsx + if not ExportHelper.parse_json_file_to_xlsx_file( + json_path, json_path.replace(".json", ".xlsx") + ): + logger.error("Error saving the result to xlsx") + raise "Error saving the result to xlsx" + + # db - save the xlsx file path + export = Export( + name=Path(json_path).name.replace(".json", ".xlsx"), + simulationId=simulation.id, ) - raise "Error saving the impulse response to xlsx" - + session.add(export) + + # auralization: save the impulse response to xlsx + if not ExportHelper.write_data_to_xlsx_file( + json_path.replace(".json", ".xlsx"), + CustomExportParametersConfig.impulse_response, + {f"{fs}Hz": imp_tot}, + ): + logger.error( + "Error saving the impulse response to xlsx" + ) + raise "Error saving the impulse response to xlsx" + except Exception as ex: + logger.error(f"Error during saving results: {ex}") + raise Exception(f"Error during saving results: {ex}") result_container = {} if json_path is not None: diff --git a/requirements.txt b/requirements.txt index 6ec3c48..0b49fb5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -155,4 +155,4 @@ paramiko git+https://github.com/Building-acoustics-TU-Eindhoven/acousticDE.git@d32afb2498e27bd996fc7356d57dc4f1ed76aa44#egg=acousticDE # git+https://github.com/dtu-act/deeponet-acoustic-wave-prop.git@3d3fc5ee952756eedcd4fec3c3674ad829825c7e#egg=deeponet-acoustics git+https://github.com/Building-acoustics-TU-Eindhoven/edg-acoustics.git@08cac98da98ed14ba1366741b1c0644001503b82#egg=edg-acoustics -pyfar +pyfar \ No newline at end of file From b8abed16a52ccdaef2947dc06ef88924506ab5e4 Mon Sep 17 00:00:00 2001 From: SilvinWillemsen Date: Wed, 6 May 2026 09:55:37 +0200 Subject: [PATCH 10/12] Updated todo --- app/services/simulation_service.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/services/simulation_service.py b/app/services/simulation_service.py index 215f3b9..7b7e98c 100644 --- a/app/services/simulation_service.py +++ b/app/services/simulation_service.py @@ -423,9 +423,9 @@ def run_solver(simulation_run_id: int, json_path: str): cancel_flag_path = Path(json_path).parent / f"{result_container['task_id']}.cancel" # auralization: generate impulse response wav file - # TODO: fix DG method such that this auralization works, - # the idea is to have one shared pipeline across all - # methods. + # TODO: move the auralization calculation to DE and write that + # to the JSON so that everything can be handled by the current + # default case and we can get rid of the match case. match simulation_method: case "DE": # TODO: This function is not a general auralization function and should be renamed From 3d829d29f1882d6571a01aff4a46e3746bd3cf6d Mon Sep 17 00:00:00 2001 From: SilvinWillemsen Date: Wed, 6 May 2026 14:28:15 +0200 Subject: [PATCH 11/12] Added comments describing the normalisation procedure and why we don't normalise a signal filled with 0s. --- app/services/simulation_service.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/services/simulation_service.py b/app/services/simulation_service.py index 7b7e98c..c791dba 100644 --- a/app/services/simulation_service.py +++ b/app/services/simulation_service.py @@ -457,9 +457,10 @@ def run_solver(simulation_run_id: int, json_path: str): if imp_tot is None or len(imp_tot) == 0: logger.warning("Impulse response data is empty or missing") imp_tot = np.zeros(44100) # 1 second of silence at 44.1 kHz - norm_rir = pf.Signal(imp_tot, fs) + norm_rir = pf.Signal(imp_tot, fs) # don't use the pf.dsp.normalize function on an empty signal, as it returns NaN values. else: rir = pf.Signal(imp_tot, fs) + # Normalise the rir. Some methods return pressure values that are too high, which causes issues when writing to wav. norm_rir = pf.dsp.normalize(rir) pf.io.write_audio(norm_rir, rir_wav_file_name) From 189ed6005786e60f995162b7d5e33e8feb64d93d Mon Sep 17 00:00:00 2001 From: Marco Berzborn Date: Sun, 10 May 2026 20:15:23 +0200 Subject: [PATCH 12/12] Explictly raise RuntimeError instead of a string (no longer supported in Python 3) --- app/services/simulation_service.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/services/simulation_service.py b/app/services/simulation_service.py index c791dba..ea3d8c5 100644 --- a/app/services/simulation_service.py +++ b/app/services/simulation_service.py @@ -480,7 +480,7 @@ def run_solver(simulation_run_id: int, json_path: str): json_path, json_path.replace(".json", ".xlsx") ): logger.error("Error saving the result to xlsx") - raise "Error saving the result to xlsx" + raise RuntimeError("Error saving the result to xlsx") # db - save the xlsx file path export = Export( @@ -498,10 +498,10 @@ def run_solver(simulation_run_id: int, json_path: str): logger.error( "Error saving the impulse response to xlsx" ) - raise "Error saving the impulse response to xlsx" + raise RuntimeError("Error saving the impulse response to xlsx") except Exception as ex: logger.error(f"Error during saving results: {ex}") - raise Exception(f"Error during saving results: {ex}") + raise RuntimeError(f"Error during saving results: {ex}") result_container = {} if json_path is not None: