From bbdc41e4278a1d8820549b420d040f4139b4f16a Mon Sep 17 00:00:00 2001 From: Hary Dhimas Date: Mon, 15 Jun 2026 13:50:58 +0200 Subject: [PATCH 01/11] feat: geometry compatibility settings for CHORAS backend --- de_method/de_geometry_compatibility.json | 13 ++++ dg_method/dg_geometry_compatibility.json | 17 +++++ .../baseline_geometry_compatibility.json | 74 +++++++++++++++++++ methods-config.json | 13 ++++ ...yroomacoustics_geometry_compatibility.json | 23 ++++++ 5 files changed, 140 insertions(+) create mode 100644 de_method/de_geometry_compatibility.json create mode 100644 dg_method/dg_geometry_compatibility.json create mode 100644 example_settings/baseline_geometry_compatibility.json create mode 100644 pyroomacoustics_method/pyroomacoustics_geometry_compatibility.json diff --git a/de_method/de_geometry_compatibility.json b/de_method/de_geometry_compatibility.json new file mode 100644 index 0000000..bb0aa19 --- /dev/null +++ b/de_method/de_geometry_compatibility.json @@ -0,0 +1,13 @@ +{ + "extends": "choras-baseline", + "method": "DE", + "notes": "The Diffusion Equation solver meshes an enclosed volume, so the surface must be watertight, but it is tolerant of non-planar faces because they are retriangulated during volume meshing.", + "issues": { + "non_planar_face": { + "compatibility": "compatible" + }, + "small_face": { + "compatibility": "warning" + } + } +} diff --git a/dg_method/dg_geometry_compatibility.json b/dg_method/dg_geometry_compatibility.json new file mode 100644 index 0000000..e3e072e --- /dev/null +++ b/dg_method/dg_geometry_compatibility.json @@ -0,0 +1,17 @@ +{ + "extends": "choras-baseline", + "method": "DG", + "notes": "Discontinuous Galerkin is a wave-based solver requiring a watertight, manifold, planar-faceted PLC. It copies the CHORAS baseline and tightens mesh-quality issues that the baseline only warns about.", + "issues": { + "non_planar_face": { + "compatibility": "incompatible" + }, + "degenerate_face": { + "compatibility": "incompatible" + }, + "small_face": { + "compatibility": "incompatible", + "description": "Faces below the element-size threshold destabilise the DG mesh and must be remeshed." + } + } +} diff --git a/example_settings/baseline_geometry_compatibility.json b/example_settings/baseline_geometry_compatibility.json new file mode 100644 index 0000000..949c1b4 --- /dev/null +++ b/example_settings/baseline_geometry_compatibility.json @@ -0,0 +1,74 @@ +{ + "version": 1, + "description": "CHORAS default geometry-issue compatibility baseline. Every simulation method inherits this list. A method may ship its own override file (referenced by 'geometryCompatibility' in methods-config.json) that copies these entries and overrides only the fields that differ. The override is merged on top of this baseline per issue kind.", + "compatibilityLevels": { + "compatible": "The issue does not affect this simulation method; the geometry can be used as-is.", + "warning": "The issue is tolerated but may reduce accuracy or require review.", + "incompatible": "The issue breaks this simulation method and must be repaired before running." + }, + "issues": { + "duplicate_vertex": { + "label": "Duplicate vertex", + "description": "Two or more vertices share the same coordinates within tolerance.", + "defaultSeverity": "warn", + "autoRepairable": true, + "compatibility": "warning" + }, + "degenerate_face": { + "label": "Degenerate face", + "description": "A face has zero or near-zero area (collapsed/sliver triangle).", + "defaultSeverity": "warn", + "autoRepairable": true, + "compatibility": "warning" + }, + "non_planar_face": { + "label": "Non-planar face", + "description": "A polygon face whose vertices do not lie on a single plane.", + "defaultSeverity": "warn", + "autoRepairable": false, + "compatibility": "warning" + }, + "t_junction": { + "label": "T-junction", + "description": "A vertex lies on the interior of another face's edge, breaking edge connectivity.", + "defaultSeverity": "fatal", + "autoRepairable": true, + "compatibility": "incompatible" + }, + "intersection": { + "label": "Self-intersection", + "description": "Faces intersect each other, violating a piecewise-linear-complex (PLC).", + "defaultSeverity": "fatal", + "autoRepairable": true, + "compatibility": "incompatible" + }, + "boundary_edge": { + "label": "Boundary edge", + "description": "An edge bordered by a single face, indicating a non-watertight surface.", + "defaultSeverity": "fatal", + "autoRepairable": false, + "compatibility": "incompatible" + }, + "possible_hole": { + "label": "Possible hole", + "description": "A closed loop of boundary edges suggesting a gap in the surface.", + "defaultSeverity": "fatal", + "autoRepairable": false, + "compatibility": "incompatible" + }, + "inverted_normal": { + "label": "Inverted normal", + "description": "A face is oriented inward instead of outward.", + "defaultSeverity": "warn", + "autoRepairable": true, + "compatibility": "warning" + }, + "small_face": { + "label": "Small face", + "description": "A face whose area is below the recommended size threshold.", + "defaultSeverity": "warn", + "autoRepairable": false, + "compatibility": "warning" + } + } +} diff --git a/methods-config.json b/methods-config.json index 1caecf0..a6c8b55 100644 --- a/methods-config.json +++ b/methods-config.json @@ -5,6 +5,7 @@ "envVars": {"CUDA_VISIBLE_DEVICES": "0"}, "label": "Discontinuous Galerkin method", "settings":"dg_setting.json", + "geometryCompatibility":"dg_method/dg_geometry_compatibility.json", "entryFile":"DGinterface.py", "repositoryURL":"https://github.com/Building-acoustics-TU-Eindhoven/edg-acoustics/", "documentationURL":"https://dg-roomacoustics.readthedocs.io/en/latest/" @@ -15,16 +16,28 @@ "envVars": {}, "label": "Diffusion Equation method", "settings":"de_setting.json", + "geometryCompatibility":"de_method/de_geometry_compatibility.json", "entryFile":"DEinterface.py", "repositoryURL":"https://github.com/Building-acoustics-TU-Eindhoven/acousticDE/", "documentationURL":"https://building-acoustics-tu-eindhoven.github.io/acousticDE/index.html" }, + { + "simulationType": "MyNewMethod", + "containerImage": "mynewmethod_image:latest", + "envVars": {}, + "label": "My New Method", + "entryFile":"MyNewMethodInterface.py", + "settings":"my_new_setting.json", + "repositoryURL":"", + "documentationURL":"" + }, { "simulationType": "Pyroomacoustics", "containerImage": "pyroomacoustics_image:latest", "envVars": {}, "label": "Pyroomacoustics", "settings": "pyroomacoustics_setting.json", + "geometryCompatibility": "pyroomacoustics_method/pyroomacoustics_geometry_compatibility.json", "entryFile": "__main__.py", "repositoryURL": "https://github.com/LCAV/pyroomacoustics", "documentationURL": "https://pyroomacoustics.readthedocs.io" diff --git a/pyroomacoustics_method/pyroomacoustics_geometry_compatibility.json b/pyroomacoustics_method/pyroomacoustics_geometry_compatibility.json new file mode 100644 index 0000000..1ccfa5d --- /dev/null +++ b/pyroomacoustics_method/pyroomacoustics_geometry_compatibility.json @@ -0,0 +1,23 @@ +{ + "extends": "choras-baseline", + "method": "Pyroomacoustics", + "notes": "Geometric-acoustics (image-source / ray tracing) is far more tolerant of surface defects than the wave-based solvers. Topology imperfections that only matter for volume meshing are downgraded here.", + "issues": { + "t_junction": { + "compatibility": "warning" + }, + "intersection": { + "compatibility": "warning" + }, + "non_planar_face": { + "compatibility": "warning" + }, + "small_face": { + "compatibility": "compatible" + }, + "boundary_edge": { + "compatibility": "warning", + "description": "Open edges reduce energy conservation but do not stop a ray-tracing run." + } + } +} From 5d2e81cf84a49f251eb3ee5a3a4e9b119b2ee518 Mon Sep 17 00:00:00 2001 From: Hary Dhimas Date: Thu, 25 Jun 2026 23:31:48 +0200 Subject: [PATCH 02/11] feat: add geometry compatibility files for DE, DG, and Pyroomacoustics methods --- ...ility.json => geometry_compatibility.json} | 0 ...ility.json => geometry_compatibility.json} | 0 .../baseline_geometry_compatibility.json | 28 ++++--------------- methods-config.json | 6 ++-- .../geometry_compatibility.json | 13 +++++++++ ...yroomacoustics_geometry_compatibility.json | 23 --------------- 6 files changed, 21 insertions(+), 49 deletions(-) rename de_method/{de_geometry_compatibility.json => geometry_compatibility.json} (100%) rename dg_method/{dg_geometry_compatibility.json => geometry_compatibility.json} (100%) create mode 100644 pyroomacoustics_method/geometry_compatibility.json delete mode 100644 pyroomacoustics_method/pyroomacoustics_geometry_compatibility.json diff --git a/de_method/de_geometry_compatibility.json b/de_method/geometry_compatibility.json similarity index 100% rename from de_method/de_geometry_compatibility.json rename to de_method/geometry_compatibility.json diff --git a/dg_method/dg_geometry_compatibility.json b/dg_method/geometry_compatibility.json similarity index 100% rename from dg_method/dg_geometry_compatibility.json rename to dg_method/geometry_compatibility.json diff --git a/example_settings/baseline_geometry_compatibility.json b/example_settings/baseline_geometry_compatibility.json index 949c1b4..deff106 100644 --- a/example_settings/baseline_geometry_compatibility.json +++ b/example_settings/baseline_geometry_compatibility.json @@ -10,65 +10,47 @@ "duplicate_vertex": { "label": "Duplicate vertex", "description": "Two or more vertices share the same coordinates within tolerance.", - "defaultSeverity": "warn", - "autoRepairable": true, "compatibility": "warning" }, "degenerate_face": { "label": "Degenerate face", "description": "A face has zero or near-zero area (collapsed/sliver triangle).", - "defaultSeverity": "warn", - "autoRepairable": true, "compatibility": "warning" }, "non_planar_face": { "label": "Non-planar face", "description": "A polygon face whose vertices do not lie on a single plane.", - "defaultSeverity": "warn", - "autoRepairable": false, "compatibility": "warning" }, "t_junction": { "label": "T-junction", "description": "A vertex lies on the interior of another face's edge, breaking edge connectivity.", - "defaultSeverity": "fatal", - "autoRepairable": true, "compatibility": "incompatible" }, "intersection": { "label": "Self-intersection", "description": "Faces intersect each other, violating a piecewise-linear-complex (PLC).", - "defaultSeverity": "fatal", - "autoRepairable": true, "compatibility": "incompatible" }, "boundary_edge": { "label": "Boundary edge", "description": "An edge bordered by a single face, indicating a non-watertight surface.", - "defaultSeverity": "fatal", - "autoRepairable": false, "compatibility": "incompatible" }, "possible_hole": { "label": "Possible hole", "description": "A closed loop of boundary edges suggesting a gap in the surface.", - "defaultSeverity": "fatal", - "autoRepairable": false, - "compatibility": "incompatible" - }, - "inverted_normal": { - "label": "Inverted normal", - "description": "A face is oriented inward instead of outward.", - "defaultSeverity": "warn", - "autoRepairable": true, "compatibility": "warning" }, "small_face": { "label": "Small face", "description": "A face whose area is below the recommended size threshold.", - "defaultSeverity": "warn", - "autoRepairable": false, "compatibility": "warning" + }, + "overlapping_face": { + "label": "Overlapping faces", + "description": "Two or more faces occupy the same space, causing potential simulation issues.", + "compatibility": "incompatible" } } } diff --git a/methods-config.json b/methods-config.json index a6c8b55..178414a 100644 --- a/methods-config.json +++ b/methods-config.json @@ -5,7 +5,7 @@ "envVars": {"CUDA_VISIBLE_DEVICES": "0"}, "label": "Discontinuous Galerkin method", "settings":"dg_setting.json", - "geometryCompatibility":"dg_method/dg_geometry_compatibility.json", + "geometryCompatibility":"dg_method/geometry_compatibility.json", "entryFile":"DGinterface.py", "repositoryURL":"https://github.com/Building-acoustics-TU-Eindhoven/edg-acoustics/", "documentationURL":"https://dg-roomacoustics.readthedocs.io/en/latest/" @@ -16,7 +16,7 @@ "envVars": {}, "label": "Diffusion Equation method", "settings":"de_setting.json", - "geometryCompatibility":"de_method/de_geometry_compatibility.json", + "geometryCompatibility":"de_method/geometry_compatibility.json", "entryFile":"DEinterface.py", "repositoryURL":"https://github.com/Building-acoustics-TU-Eindhoven/acousticDE/", "documentationURL":"https://building-acoustics-tu-eindhoven.github.io/acousticDE/index.html" @@ -37,7 +37,7 @@ "envVars": {}, "label": "Pyroomacoustics", "settings": "pyroomacoustics_setting.json", - "geometryCompatibility": "pyroomacoustics_method/pyroomacoustics_geometry_compatibility.json", + "geometryCompatibility": "pyroomacoustics_method/geometry_compatibility.json", "entryFile": "__main__.py", "repositoryURL": "https://github.com/LCAV/pyroomacoustics", "documentationURL": "https://pyroomacoustics.readthedocs.io" diff --git a/pyroomacoustics_method/geometry_compatibility.json b/pyroomacoustics_method/geometry_compatibility.json new file mode 100644 index 0000000..6fd6011 --- /dev/null +++ b/pyroomacoustics_method/geometry_compatibility.json @@ -0,0 +1,13 @@ +{ + "extends": "choras-baseline", + "method": "Pyroomacoustics", + "notes": "Example notes", + "issues": { + "non_planar_face": { + "compatibility": "compatible" + }, + "small_face": { + "compatibility": "warning" + } + } +} diff --git a/pyroomacoustics_method/pyroomacoustics_geometry_compatibility.json b/pyroomacoustics_method/pyroomacoustics_geometry_compatibility.json deleted file mode 100644 index 1ccfa5d..0000000 --- a/pyroomacoustics_method/pyroomacoustics_geometry_compatibility.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "extends": "choras-baseline", - "method": "Pyroomacoustics", - "notes": "Geometric-acoustics (image-source / ray tracing) is far more tolerant of surface defects than the wave-based solvers. Topology imperfections that only matter for volume meshing are downgraded here.", - "issues": { - "t_junction": { - "compatibility": "warning" - }, - "intersection": { - "compatibility": "warning" - }, - "non_planar_face": { - "compatibility": "warning" - }, - "small_face": { - "compatibility": "compatible" - }, - "boundary_edge": { - "compatibility": "warning", - "description": "Open edges reduce energy conservation but do not stop a ray-tracing run." - } - } -} From 380390197d26d4e4595b2c878f3a1d6cac84fba5 Mon Sep 17 00:00:00 2001 From: Hary Dhimas Date: Fri, 10 Jul 2026 20:43:11 +0200 Subject: [PATCH 03/11] feat: update geometry compatibility settings for DE, DG, and Pyroomacoustics methods --- de_method/geometry_compatibility.json | 8 ++++---- dg_method/geometry_compatibility.json | 10 +++------- example_settings/baseline_geometry_compatibility.json | 5 +++++ pyroomacoustics_method/geometry_compatibility.json | 8 ++++---- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/de_method/geometry_compatibility.json b/de_method/geometry_compatibility.json index bb0aa19..c92431e 100644 --- a/de_method/geometry_compatibility.json +++ b/de_method/geometry_compatibility.json @@ -3,11 +3,11 @@ "method": "DE", "notes": "The Diffusion Equation solver meshes an enclosed volume, so the surface must be watertight, but it is tolerant of non-planar faces because they are retriangulated during volume meshing.", "issues": { - "non_planar_face": { - "compatibility": "compatible" - }, - "small_face": { + "boundary_edge": { "compatibility": "warning" + }, + "possible_hole": { + "compatibility": "incompatible" } } } diff --git a/dg_method/geometry_compatibility.json b/dg_method/geometry_compatibility.json index e3e072e..00b2bd4 100644 --- a/dg_method/geometry_compatibility.json +++ b/dg_method/geometry_compatibility.json @@ -3,15 +3,11 @@ "method": "DG", "notes": "Discontinuous Galerkin is a wave-based solver requiring a watertight, manifold, planar-faceted PLC. It copies the CHORAS baseline and tightens mesh-quality issues that the baseline only warns about.", "issues": { - "non_planar_face": { - "compatibility": "incompatible" + "boundary_edge": { + "compatibility": "warning" }, - "degenerate_face": { + "possible_hole": { "compatibility": "incompatible" - }, - "small_face": { - "compatibility": "incompatible", - "description": "Faces below the element-size threshold destabilise the DG mesh and must be remeshed." } } } diff --git a/example_settings/baseline_geometry_compatibility.json b/example_settings/baseline_geometry_compatibility.json index deff106..b5f12ca 100644 --- a/example_settings/baseline_geometry_compatibility.json +++ b/example_settings/baseline_geometry_compatibility.json @@ -47,6 +47,11 @@ "description": "A face whose area is below the recommended size threshold.", "compatibility": "warning" }, + "collinear_face": { + "label": "Collinear face", + "description": "A face whose vertices are collinear or nearly collinear (collapsed to a line).", + "compatibility": "warning" + }, "overlapping_face": { "label": "Overlapping faces", "description": "Two or more faces occupy the same space, causing potential simulation issues.", diff --git a/pyroomacoustics_method/geometry_compatibility.json b/pyroomacoustics_method/geometry_compatibility.json index 6fd6011..30f3391 100644 --- a/pyroomacoustics_method/geometry_compatibility.json +++ b/pyroomacoustics_method/geometry_compatibility.json @@ -3,11 +3,11 @@ "method": "Pyroomacoustics", "notes": "Example notes", "issues": { - "non_planar_face": { - "compatibility": "compatible" - }, - "small_face": { + "boundary_edge": { "compatibility": "warning" + }, + "possible_hole": { + "compatibility": "incompatible" } } } From 113be994090a82d3237e261b69866ed50510e7e8 Mon Sep 17 00:00:00 2001 From: Hary Dhimas Date: Thu, 16 Jul 2026 20:07:02 +0200 Subject: [PATCH 04/11] feat: Add method-specific validation modules (Option A) - Add de_method/de_interface/validation.py with run_method_validation() - Add dg_method/dg_interface/validation.py with run_method_validation() (incompatible sample) - Update methods-config.json to reference validation modules for DE and DG methods --- de_method/de_interface/validation.py | 24 ++++++++++++++++++++++++ dg_method/dg_interface/validation.py | 26 ++++++++++++++++++++++++++ methods-config.json | 2 ++ 3 files changed, 52 insertions(+) create mode 100644 de_method/de_interface/validation.py create mode 100644 dg_method/dg_interface/validation.py diff --git a/de_method/de_interface/validation.py b/de_method/de_interface/validation.py new file mode 100644 index 0000000..e51622d --- /dev/null +++ b/de_method/de_interface/validation.py @@ -0,0 +1,24 @@ +"""Method-specific geometry validation for the DE method (lightweight). + +This module provides geometry validation checks specific to the Diffusion Equation +solver. It is imported dynamically by the backend (no heavy deps on acousticDE). +""" + + +def run_method_validation(input_file: str) -> dict: + """Validate geometry for DE method. + + Args: + input_file: Path to the geometry file (e.g., .obj) to validate. + + Returns: + dict with keys: + - compatible (bool): True if geometry is suitable, False otherwise + - reason (str): Human-readable explanation + """ + # TODO: real checks (watertightness, closed volume, etc.). + # Hardcoded result for now: + return { + "compatible": True, + "reason": "Geometry forms a closed volume suitable for the DE solver.", + } diff --git a/dg_method/dg_interface/validation.py b/dg_method/dg_interface/validation.py new file mode 100644 index 0000000..2c1bb37 --- /dev/null +++ b/dg_method/dg_interface/validation.py @@ -0,0 +1,26 @@ +"""Method-specific geometry validation for the DG method (lightweight). + +This module provides geometry validation checks specific to the Discontinuous +Galerkin solver. It is imported dynamically by the backend (no heavy deps on +edg-acoustics). +""" + + +def run_method_validation(input_file: str) -> dict: + """Validate geometry for DG method. + + Args: + input_file: Path to the geometry file (e.g., .obj) to validate. + + Returns: + dict with keys: + - compatible (bool): True if geometry is suitable, False otherwise + - reason (str): Human-readable explanation + """ + # TODO: real checks (watertightness, manifold, planar-faceted, etc.). + # Hardcoded result for now (incompatible sample to test override): + return { + "compatible": False, + "reason": "DG requires a watertight, manifold, planar-faceted mesh; " + "non-planar faces were detected.", + } diff --git a/methods-config.json b/methods-config.json index 178414a..a507d1a 100644 --- a/methods-config.json +++ b/methods-config.json @@ -6,6 +6,7 @@ "label": "Discontinuous Galerkin method", "settings":"dg_setting.json", "geometryCompatibility":"dg_method/geometry_compatibility.json", + "methodValidation":"dg_method/dg_interface/validation.py", "entryFile":"DGinterface.py", "repositoryURL":"https://github.com/Building-acoustics-TU-Eindhoven/edg-acoustics/", "documentationURL":"https://dg-roomacoustics.readthedocs.io/en/latest/" @@ -17,6 +18,7 @@ "label": "Diffusion Equation method", "settings":"de_setting.json", "geometryCompatibility":"de_method/geometry_compatibility.json", + "methodValidation":"de_method/de_interface/validation.py", "entryFile":"DEinterface.py", "repositoryURL":"https://github.com/Building-acoustics-TU-Eindhoven/acousticDE/", "documentationURL":"https://building-acoustics-tu-eindhoven.github.io/acousticDE/index.html" From 070a5b12b216c1d1e48adc326d57f3c251633e94 Mon Sep 17 00:00:00 2001 From: Hary Dhimas Date: Tue, 21 Jul 2026 14:59:37 +0200 Subject: [PATCH 05/11] feat: Enhance geometry validation for DE and DG methods with file existence and format checks --- de_method/de_interface/validation.py | 49 ++++++++++++++++++++++++---- dg_method/dg_interface/validation.py | 3 +- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/de_method/de_interface/validation.py b/de_method/de_interface/validation.py index e51622d..d7434e0 100644 --- a/de_method/de_interface/validation.py +++ b/de_method/de_interface/validation.py @@ -4,6 +4,8 @@ solver. It is imported dynamically by the backend (no heavy deps on acousticDE). """ +import os + def run_method_validation(input_file: str) -> dict: """Validate geometry for DE method. @@ -16,9 +18,44 @@ def run_method_validation(input_file: str) -> dict: - compatible (bool): True if geometry is suitable, False otherwise - reason (str): Human-readable explanation """ - # TODO: real checks (watertightness, closed volume, etc.). - # Hardcoded result for now: - return { - "compatible": True, - "reason": "Geometry forms a closed volume suitable for the DE solver.", - } + # Step 1: Check if file exists + if not os.path.exists(input_file): + return { + "compatible": False, + "reason": f"File does not exist: {input_file}", + } + + # Step 2: Check if it's OBJ format + if not input_file.lower().endswith('.obj'): + return { + "compatible": False, + "reason": f"File must be in OBJ format, got: {os.path.splitext(input_file)[1]}", + } + + # Step 3: Parse OBJ file and count faces (surfaces) + try: + face_count = 0 + with open(input_file, 'r') as f: + for line in f: + line = line.strip() + # OBJ face lines start with 'f' + if line.startswith('f '): + face_count += 1 + + # Step 4: Check if face count exceeds threshold + if face_count > 6: + return { + "compatible": False, + "reason": f"Geometry has {face_count} faces, but DE method supports maximum 6 faces. Simplify the mesh.", + } + + return { + "compatible": True, + "reason": f"Geometry is valid for DE method ({face_count} faces detected).", + } + + except Exception as e: + return { + "compatible": False, + "reason": f"Error parsing OBJ file: {str(e)}", + } diff --git a/dg_method/dg_interface/validation.py b/dg_method/dg_interface/validation.py index 2c1bb37..5a28556 100644 --- a/dg_method/dg_interface/validation.py +++ b/dg_method/dg_interface/validation.py @@ -17,8 +17,7 @@ def run_method_validation(input_file: str) -> dict: - compatible (bool): True if geometry is suitable, False otherwise - reason (str): Human-readable explanation """ - # TODO: real checks (watertightness, manifold, planar-faceted, etc.). - # Hardcoded result for now (incompatible sample to test override): + # TODO: real checks (also consider limitation of current DG implementation) return { "compatible": False, "reason": "DG requires a watertight, manifold, planar-faceted mesh; " From d85a2afdebad3b9a6f38c8ec19fb5115f74c6dda Mon Sep 17 00:00:00 2001 From: Hary Dhimas Date: Tue, 28 Jul 2026 13:44:11 +0200 Subject: [PATCH 06/11] feat: Rename 'degenerate_face' to 'zero_area_face' for clarity in geometry compatibility issues --- example_settings/baseline_geometry_compatibility.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example_settings/baseline_geometry_compatibility.json b/example_settings/baseline_geometry_compatibility.json index b5f12ca..ebddc46 100644 --- a/example_settings/baseline_geometry_compatibility.json +++ b/example_settings/baseline_geometry_compatibility.json @@ -12,8 +12,8 @@ "description": "Two or more vertices share the same coordinates within tolerance.", "compatibility": "warning" }, - "degenerate_face": { - "label": "Degenerate face", + "zero_area_face": { + "label": "Zero area face", "description": "A face has zero or near-zero area (collapsed/sliver triangle).", "compatibility": "warning" }, From c6ec91af799fec3ea629b8d601fc233d3a9e5ab6 Mon Sep 17 00:00:00 2001 From: Hary Dhimas Date: Tue, 4 Aug 2026 14:51:38 +0200 Subject: [PATCH 07/11] feat: Update geometry validation for DE and DG methods to return default valid state --- de_method/de_interface/validation.py | 70 +++++++++++++++------------- dg_method/dg_interface/validation.py | 5 +- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/de_method/de_interface/validation.py b/de_method/de_interface/validation.py index d7434e0..ed14465 100644 --- a/de_method/de_interface/validation.py +++ b/de_method/de_interface/validation.py @@ -8,6 +8,10 @@ def run_method_validation(input_file: str) -> dict: + return { + "compatible": True, + "reason": "Geometry is valid for DE method (default validation).", + } """Validate geometry for DE method. Args: @@ -19,43 +23,43 @@ def run_method_validation(input_file: str) -> dict: - reason (str): Human-readable explanation """ # Step 1: Check if file exists - if not os.path.exists(input_file): - return { - "compatible": False, - "reason": f"File does not exist: {input_file}", - } + # if not os.path.exists(input_file): + # return { + # "compatible": False, + # "reason": f"File does not exist: {input_file}", + # } # Step 2: Check if it's OBJ format - if not input_file.lower().endswith('.obj'): - return { - "compatible": False, - "reason": f"File must be in OBJ format, got: {os.path.splitext(input_file)[1]}", - } + # if not input_file.lower().endswith('.obj'): + # return { + # "compatible": False, + # "reason": f"File must be in OBJ format, got: {os.path.splitext(input_file)[1]}", + # } # Step 3: Parse OBJ file and count faces (surfaces) - try: - face_count = 0 - with open(input_file, 'r') as f: - for line in f: - line = line.strip() - # OBJ face lines start with 'f' - if line.startswith('f '): - face_count += 1 + # try: + # face_count = 0 + # with open(input_file, 'r') as f: + # for line in f: + # line = line.strip() + # # OBJ face lines start with 'f' + # if line.startswith('f '): + # face_count += 1 - # Step 4: Check if face count exceeds threshold - if face_count > 6: - return { - "compatible": False, - "reason": f"Geometry has {face_count} faces, but DE method supports maximum 6 faces. Simplify the mesh.", - } + # # Step 4: Check if face count exceeds threshold + # if face_count > 6: + # return { + # "compatible": False, + # "reason": f"Geometry has {face_count} faces, but DE method supports maximum 6 faces. Simplify the mesh.", + # } - return { - "compatible": True, - "reason": f"Geometry is valid for DE method ({face_count} faces detected).", - } + # return { + # "compatible": True, + # "reason": f"Geometry is valid for DE method ({face_count} faces detected).", + # } - except Exception as e: - return { - "compatible": False, - "reason": f"Error parsing OBJ file: {str(e)}", - } + # except Exception as e: + # return { + # "compatible": False, + # "reason": f"Error parsing OBJ file: {str(e)}", + # } diff --git a/dg_method/dg_interface/validation.py b/dg_method/dg_interface/validation.py index 5a28556..273b396 100644 --- a/dg_method/dg_interface/validation.py +++ b/dg_method/dg_interface/validation.py @@ -19,7 +19,6 @@ def run_method_validation(input_file: str) -> dict: """ # TODO: real checks (also consider limitation of current DG implementation) return { - "compatible": False, - "reason": "DG requires a watertight, manifold, planar-faceted mesh; " - "non-planar faces were detected.", + "compatible": True, + "reason": "Geometry is valid for DG method (default validation).", } From 6368f5ebf84bc1bbf680c67562e617e9c1565fc8 Mon Sep 17 00:00:00 2001 From: Hary Dhimas Date: Thu, 13 Aug 2026 10:07:20 +0200 Subject: [PATCH 08/11] feat: Update compatibility levels for boundary_edge and possible_hole in baseline geometry settings --- GEOMETRY_COMPATIBILITY_AND_VALIDATION.md | 355 ++++++++++++++++++ .../baseline_geometry_compatibility.json | 4 +- 2 files changed, 357 insertions(+), 2 deletions(-) create mode 100644 GEOMETRY_COMPATIBILITY_AND_VALIDATION.md diff --git a/GEOMETRY_COMPATIBILITY_AND_VALIDATION.md b/GEOMETRY_COMPATIBILITY_AND_VALIDATION.md new file mode 100644 index 0000000..e74d329 --- /dev/null +++ b/GEOMETRY_COMPATIBILITY_AND_VALIDATION.md @@ -0,0 +1,355 @@ +# Geometry Compatibility & Custom Validation — Integration Guide + +This guide is for developers integrating a new simulation method into CHORAS. +It explains the two mechanisms that decide **whether an uploaded geometry can be +run with your method**: + +1. **Baseline geometry compatibility** — a declarative JSON rule set that maps + each geometry *issue kind* to a compatibility level, which your method + overrides where it differs. +2. **Custom validation** (`validation.py`) — an optional Python hook + (`run_method_validation`) that runs your own programmatic checks on the + geometry file at request time. + +Both feed the same endpoint the frontend calls to show, e.g., +*"compatible with 3 of 3 simulation methods"* and the per-issue badges on the +geometry-repair page. + +--- + +## How this fits into CHORAS + +CHORAS is split across three repositories that cooperate here: + +```mermaid +flowchart LR + A[geometry-pipeline
detects issues] -->|issue report JSON
keyed by IssueKind| B + B[backend
geometry_compatibility_service.py] -->|merged compatibility
+ validation verdict| C + C[frontend
geometry-repair page] + D[simulation-backend
baseline + per-method overrides
+ validation.py] --> B +``` + +1. **geometry-pipeline** analyzes the uploaded model and writes an **issue + report** — a JSON object keyed by *issue kind* (e.g. `duplicate_vertex`, + `boundary_edge`), each mapping to a list of detected occurrences. +2. **backend** (`app/services/geometry_compatibility_service.py`) loads the + **baseline** from this repo, merges each method's **override** on top, then + compares the merged rules against the issue report to produce a per-method + verdict. If a method ships a `validation.py`, its verdict is combined in. +3. **frontend** renders the verdict: overall compatibility per method, a badge + per issue, and enables/disables the *Use Initial / Use Repaired Model* + buttons. + + +> **Key rule:** the *issue kind* strings you use in your override JSON must +> exactly match the `IssueKind` values emitted by the geometry-pipeline. The +> current set is listed in the baseline (see below). + +--- + +## 1. Baseline Geometry Compatibility + +### 1.1 What the baseline file is + +`example_settings/baseline_geometry_compatibility.json` is the **single source +of truth** for the default compatibility of every geometry issue. Every method +inherits it, and only overrides the entries that differ. + +It has three parts: + +| Field | Purpose | +|-------|---------| +| `version` | Schema version of the baseline. | +| `compatibilityLevels` | The three allowed levels and what each means. | +| `issues` | One entry per issue kind: `label`, `description`, and default `compatibility`. | + +The three compatibility levels (worst-case ordering matters — see §1.5): + +| Level | Meaning | Severity rank | +|-------|---------|:-------------:| +| `compatible` | The issue does not affect this method; geometry usable as-is. | 0 | +| `warning` | Tolerated but may reduce accuracy or need review. | 1 | +| `incompatible` | Breaks this method; must be repaired before running. | 2 | + +The baseline currently defines these **issue kinds** (these are the exact keys +you may override): + +`duplicate_vertex`, `zero_area_face`, `non_planar_face`, `t_junction`, +`intersection`, `boundary_edge`, `possible_hole`, `small_face`, +`collinear_face`, `overlapping_face`. + +Every issue entry carries a `description` explaining what the defect is, so the +baseline file doubles as a reference: open +[`example_settings/baseline_geometry_compatibility.json`](example_settings/baseline_geometry_compatibility.json), +read the `description` of each kind, and use its baseline `compatibility` as the +starting point you decide whether to keep or override for your solver. + +Example baseline entry — a self-intersection, which the baseline marks as +`incompatible` because it violates a valid piecewise-linear complex: + +```json +"intersection": { + "label": "Self-intersection", + "description": "Faces intersect each other, violating a piecewise-linear-complex (PLC).", + "compatibility": "incompatible" +} +``` + +### 1.2 How your method overrides the baseline + +Your method ships a small override file, referenced by the +`geometryCompatibility` key in [`methods-config.json`](methods-config.json). +You only list the issue kinds whose fields differ from the baseline — everything +else is inherited unchanged. + +Real example — [`de_method/geometry_compatibility.json`](de_method/geometry_compatibility.json): + +```json +{ + "extends": "choras-baseline", + "method": "DE", + "notes": "The Diffusion Equation solver meshes an enclosed volume, so the surface must be watertight, but it is tolerant of non-planar faces because they are retriangulated during volume meshing.", + "issues": { + "boundary_edge": { + "compatibility": "warning" + }, + "possible_hole": { + "compatibility": "incompatible" + } + } +} +``` + +| Field | Required | Purpose | +|-------|:--------:|---------| +| `extends` | convention | Always `"choras-baseline"` — documents intent (the merge always uses the baseline). | +| `method` | convention | Human-readable method identifier for traceability. | +| `notes` | optional | Surfaced by the backend and shown to the user; explain *why* your rules differ. | +| `issues` | optional | Per-kind field overrides. Any kind you omit inherits the baseline entirely. | + +### 1.3 The merge rules (exactly what happens) + +The backend (`_merge_method_override`) does a **per-issue-kind field merge**: + +- It deep-copies the baseline `issues`. +- For every kind in your override's `issues`, it **updates only the fields you + provide** (so overriding just `compatibility` keeps the baseline `label` and + `description`). +- Any kind **not mentioned** in your override is inherited unchanged. +- A kind you add that is **not** in the baseline is added as-is (use this only + if the pipeline actually emits that kind). + +So the DE example above yields: all 10 baseline issues, with `boundary_edge` +downgraded to `warning` and `possible_hole` raised to `incompatible`; the other +eight keep their baseline values. + +### 1.4 Steps to add compatibility rules for a new method + +1. Create `your_method/geometry_compatibility.json` using the template in §1.2. +2. Set `method` and a helpful `notes` string. +3. Under `issues`, add **only** the kinds whose `compatibility` differs from the + baseline. Use the exact kind keys from §1.1. +4. Register it in [`methods-config.json`](methods-config.json) by adding a + `geometryCompatibility` entry pointing at the file (path relative to this + repo root): + + ```json + { + "simulationType": "YourMethod", + "label": "Your Method", + "geometryCompatibility": "your_method/geometry_compatibility.json", + "methodValidation": "your_method/your_interface/validation.py", + "entryFile": "YourInterface.py", + "settings": "your_setting.json" + } + ``` + +5. That's it — the backend discovers the method and merges automatically. No + backend code changes are needed. + +> If you omit `geometryCompatibility`, your method inherits the **full baseline +> unchanged** — every issue keeps its baseline compatibility level. + +#### What if I want a *different* compatibility than the baseline? + +**Do not edit `baseline_geometry_compatibility.json` to change it for your +method.** The baseline is shared by *every* method, so editing it changes the +default for all solvers at once. Instead, express the difference as an +**override** in your own `your_method/geometry_compatibility.json`: + +- To make an issue **stricter or more lenient for your method**, add that issue + kind under `issues` with the `compatibility` you want (as the DE example does + for `boundary_edge` and `possible_hole`). This affects only your method. +- Only edit the baseline file itself when the change is a genuine + **project-wide default** that should apply to *all* methods (for example, + fixing a wrong description, or changing the CHORAS-wide default for a brand-new + issue kind emitted by the pipeline). Such a change should be reviewed as a + shared-config change, not a per-method tweak. + +### 1.5 How a verdict is computed from your rules + +For a given uploaded model, the backend (`_method_result`) resolves a single +`compatible` value per method: + +- It looks at which issue kinds are actually **present** (non-empty list) in the + pipeline's report. +- The method verdict is the **worst-case** (highest severity rank) among the + present issues' compatibility levels. +- If no rule-relevant issue is present → `compatible`. +- If your method declares a kind the report has **no information** about (the + kind isn't a key in the report at all) and nothing else is present → + `unknown`. + +--- + +## 2. Custom Validation (`validation.py`) + +The baseline/override system is **declarative** — it only reasons about the +issue kinds the pipeline reports. **Custom validation** lets your method run +**arbitrary Python checks** against the actual geometry file (for example: face +count limits, bounding-box size, format constraints, watertightness heuristics +specific to your solver). + +**Custom validation is entirely optional.** If your method does not need any +programmatic checks beyond the baseline/override rules, simply **do not create a +`validation.py`** and **omit the `methodValidation` key** from your entry in +`methods-config.json`. The backend then decides compatibility from the +baseline/override rules alone — this is exactly what Pyroomacoustics does. You +only add validation when you need logic the declarative rules cannot express. + +### 2.1 The contract + +Your validation module must expose a single function: + +```python +def run_method_validation(input_file: str) -> dict: + ... +``` + +| Aspect | Specification | +|--------|---------------| +| **Function name** | Must be exactly `run_method_validation` (the backend looks it up by name). | +| **Parameter** | `input_file: str` — an absolute filesystem path to the geometry file (e.g. an `.obj`) resolved inside the backend's uploads folder. | +| **Return value** | A `dict` with two keys: `compatible` (bool) and `reason` (str). | +| **`compatible`** | `True` if the geometry is suitable for your method, `False` otherwise. | +| **`reason`** | Human-readable explanation, surfaced to the user (shown even when compatible). | + +Minimal valid example — [`dg_method/dg_interface/validation.py`](dg_method/dg_interface/validation.py): + +```python +def run_method_validation(input_file: str) -> dict: + """Validate geometry for DG method. + + Args: + input_file: Path to the geometry file (e.g., .obj) to validate. + + Returns: + dict with keys: + - compatible (bool): True if geometry is suitable, False otherwise + - reason (str): Human-readable explanation + """ + return { + "compatible": True, + "reason": "Geometry is valid for DG method (default validation).", + } +``` + +A richer example — counting faces and rejecting oversized meshes: + +```python +import os + + +def run_method_validation(input_file: str) -> dict: + if not os.path.exists(input_file): + return {"compatible": False, "reason": f"File does not exist: {input_file}"} + + if not input_file.lower().endswith(".obj"): + return {"compatible": False, "reason": "File must be in OBJ format."} + + face_count = 0 + with open(input_file, "r") as f: + for line in f: + if line.startswith("f "): + face_count += 1 + + if face_count > 6: + return { + "compatible": False, + "reason": f"Geometry has {face_count} faces; this method supports max 6.", + } + + return { + "compatible": True, + "reason": f"Geometry is valid ({face_count} faces detected).", + } +``` + +### 2.2 How the backend loads and runs it + +- The module is referenced by the `methodValidation` key in + [`methods-config.json`](methods-config.json) (path relative to this repo root). +- The backend imports it **dynamically** with `importlib` at request time and + calls `run_method_validation(input_file)` + (`geometry_compatibility_service.py`). +- Validation is **best-effort and optional**: + - If the file is missing, the function is absent, it raises, or it returns a + malformed dict → validation is **skipped** (treated as no verdict), and only + the baseline/override rules apply. + - Only a dict containing a `compatible` key is accepted; `reason` defaults to + an empty string if omitted. + +> **Keep it lightweight.** The module is imported **inside the backend process**, +> not your solver container. Do **not** import heavy solver dependencies +> (e.g. `acousticDE`, `edg-acoustics`) here — the existing modules deliberately +> note they are "lightweight … no heavy deps". Stick to the standard library and +> simple file parsing. + +### 2.3 How the validation verdict combines with the rules + +When both a validation module and an input file are available, the backend +combines the two verdicts using **worst-case wins** logic: + +- Your `compatible: True` → verdict `compatible`; `compatible: False` → + `incompatible`. +- This is compared against the declarative verdict from §1.5, and the **more + severe** of the two is used as the method's final `compatible` value. +- Your `reason` string is attached to the method result and shown to the user. + +So validation can only ever make a method **stricter**, never more permissive — +it cannot turn a rule-based `incompatible` into `compatible`. + +### 2.4 Steps to add custom validation for a new method + +1. Create `your_method/your_interface/validation.py` exposing + `run_method_validation(input_file: str) -> dict`. +2. Implement your checks using only lightweight dependencies. +3. Always return `{"compatible": bool, "reason": str}` on **every** path, + including error paths. +4. Register it via the `methodValidation` key in + [`methods-config.json`](methods-config.json) (see the snippet in §1.4). +5. Test locally by uploading a model that should pass and one that should fail, + and confirm the frontend shows the expected verdict and your `reason`. + +> Custom validation is **optional**. If your method omits `methodValidation` +> (as Pyroomacoustics does), only the baseline/override rules decide +> compatibility. + +--- + +## Quick reference + +| Concern | File you edit | Key in `methods-config.json` | Backend consumer | +|---------|---------------|------------------------------|------------------| +| Default rules for all methods | `example_settings/baseline_geometry_compatibility.json` | — | `geometry_compatibility_service.py` | +| Your method's rule overrides | `your_method/geometry_compatibility.json` | `geometryCompatibility` | `_merge_method_override` | +| Your method's custom checks | `your_method/your_interface/validation.py` | `methodValidation` | `_run_method_validation` | + +| Contract | Requirement | +|----------|-------------| +| Issue kind keys | Must match the pipeline's `IssueKind` values (§1.1). | +| Compatibility level | One of `compatible`, `warning`, `incompatible`. | +| Validation function | Named `run_method_validation(input_file: str) -> dict`. | +| Validation return | `{"compatible": bool, "reason": str}` on every path. | +| Validation weight | Lightweight imports only; runs in the backend process. | +| Validation effect | Can only make a method stricter (worst-case wins). | diff --git a/example_settings/baseline_geometry_compatibility.json b/example_settings/baseline_geometry_compatibility.json index ebddc46..f39a4fd 100644 --- a/example_settings/baseline_geometry_compatibility.json +++ b/example_settings/baseline_geometry_compatibility.json @@ -35,12 +35,12 @@ "boundary_edge": { "label": "Boundary edge", "description": "An edge bordered by a single face, indicating a non-watertight surface.", - "compatibility": "incompatible" + "compatibility": "warning" }, "possible_hole": { "label": "Possible hole", "description": "A closed loop of boundary edges suggesting a gap in the surface.", - "compatibility": "warning" + "compatibility": "incompatible" }, "small_face": { "label": "Small face", From a2d98bc7ddc8f24aeb5f5549a847f46ad8edb3b9 Mon Sep 17 00:00:00 2001 From: Hary Dhimas Date: Tue, 1 Sep 2026 10:37:36 +0200 Subject: [PATCH 09/11] feat: Update geometry compatibility default settings --- de_method/geometry_compatibility.json | 12 +++++++++--- dg_method/geometry_compatibility.json | 12 +++++++++--- .../baseline_geometry_compatibility.json | 8 ++++---- pyroomacoustics_method/geometry_compatibility.json | 12 +++++++++--- 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/de_method/geometry_compatibility.json b/de_method/geometry_compatibility.json index c92431e..64dfa71 100644 --- a/de_method/geometry_compatibility.json +++ b/de_method/geometry_compatibility.json @@ -3,11 +3,17 @@ "method": "DE", "notes": "The Diffusion Equation solver meshes an enclosed volume, so the surface must be watertight, but it is tolerant of non-planar faces because they are retriangulated during volume meshing.", "issues": { - "boundary_edge": { - "compatibility": "warning" - }, "possible_hole": { "compatibility": "incompatible" + }, + "t_junction": { + "compatibility": "incompatible" + }, + "intersection": { + "compatibility": "incompatible" + }, + "overlapping_face": { + "compatibility": "incompatible" } } } diff --git a/dg_method/geometry_compatibility.json b/dg_method/geometry_compatibility.json index 00b2bd4..99facf0 100644 --- a/dg_method/geometry_compatibility.json +++ b/dg_method/geometry_compatibility.json @@ -3,11 +3,17 @@ "method": "DG", "notes": "Discontinuous Galerkin is a wave-based solver requiring a watertight, manifold, planar-faceted PLC. It copies the CHORAS baseline and tightens mesh-quality issues that the baseline only warns about.", "issues": { - "boundary_edge": { - "compatibility": "warning" - }, "possible_hole": { "compatibility": "incompatible" + }, + "t_junction": { + "compatibility": "incompatible" + }, + "intersection": { + "compatibility": "incompatible" + }, + "overlapping_face": { + "compatibility": "incompatible" } } } diff --git a/example_settings/baseline_geometry_compatibility.json b/example_settings/baseline_geometry_compatibility.json index f39a4fd..473c658 100644 --- a/example_settings/baseline_geometry_compatibility.json +++ b/example_settings/baseline_geometry_compatibility.json @@ -25,12 +25,12 @@ "t_junction": { "label": "T-junction", "description": "A vertex lies on the interior of another face's edge, breaking edge connectivity.", - "compatibility": "incompatible" + "compatibility": "warning" }, "intersection": { "label": "Self-intersection", "description": "Faces intersect each other, violating a piecewise-linear-complex (PLC).", - "compatibility": "incompatible" + "compatibility": "warning" }, "boundary_edge": { "label": "Boundary edge", @@ -40,7 +40,7 @@ "possible_hole": { "label": "Possible hole", "description": "A closed loop of boundary edges suggesting a gap in the surface.", - "compatibility": "incompatible" + "compatibility": "warning" }, "small_face": { "label": "Small face", @@ -55,7 +55,7 @@ "overlapping_face": { "label": "Overlapping faces", "description": "Two or more faces occupy the same space, causing potential simulation issues.", - "compatibility": "incompatible" + "compatibility": "warning" } } } diff --git a/pyroomacoustics_method/geometry_compatibility.json b/pyroomacoustics_method/geometry_compatibility.json index 30f3391..96c7ccd 100644 --- a/pyroomacoustics_method/geometry_compatibility.json +++ b/pyroomacoustics_method/geometry_compatibility.json @@ -3,11 +3,17 @@ "method": "Pyroomacoustics", "notes": "Example notes", "issues": { - "boundary_edge": { - "compatibility": "warning" - }, "possible_hole": { "compatibility": "incompatible" + }, + "t_junction": { + "compatibility": "incompatible" + }, + "intersection": { + "compatibility": "incompatible" + }, + "overlapping_face": { + "compatibility": "incompatible" } } } From b0dc6bfdea2d7fa4e256ed2847c08a09c27374ee Mon Sep 17 00:00:00 2001 From: Hary Dhimas Date: Tue, 1 Sep 2026 10:42:32 +0200 Subject: [PATCH 10/11] fix: Update section references in geometry compatibility documentation for clarity --- GEOMETRY_COMPATIBILITY_AND_VALIDATION.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/GEOMETRY_COMPATIBILITY_AND_VALIDATION.md b/GEOMETRY_COMPATIBILITY_AND_VALIDATION.md index e74d329..1aac9e9 100644 --- a/GEOMETRY_COMPATIBILITY_AND_VALIDATION.md +++ b/GEOMETRY_COMPATIBILITY_AND_VALIDATION.md @@ -63,7 +63,7 @@ It has three parts: | `compatibilityLevels` | The three allowed levels and what each means. | | `issues` | One entry per issue kind: `label`, `description`, and default `compatibility`. | -The three compatibility levels (worst-case ordering matters — see §1.5): +The three compatibility levels (worst-case ordering matters — see 1.5): | Level | Meaning | Severity rank | |-------|---------|:-------------:| @@ -145,10 +145,10 @@ eight keep their baseline values. ### 1.4 Steps to add compatibility rules for a new method -1. Create `your_method/geometry_compatibility.json` using the template in §1.2. +1. Create `your_method/geometry_compatibility.json` using the template in 1.2. 2. Set `method` and a helpful `notes` string. 3. Under `issues`, add **only** the kinds whose `compatibility` differs from the - baseline. Use the exact kind keys from §1.1. + baseline. Use the exact kind keys from 1.1. 4. Register it in [`methods-config.json`](methods-config.json) by adding a `geometryCompatibility` entry pointing at the file (path relative to this repo root): @@ -312,7 +312,7 @@ combines the two verdicts using **worst-case wins** logic: - Your `compatible: True` → verdict `compatible`; `compatible: False` → `incompatible`. -- This is compared against the declarative verdict from §1.5, and the **more +- This is compared against the declarative verdict from 1.5, and the **more severe** of the two is used as the method's final `compatible` value. - Your `reason` string is attached to the method result and shown to the user. @@ -327,7 +327,7 @@ it cannot turn a rule-based `incompatible` into `compatible`. 3. Always return `{"compatible": bool, "reason": str}` on **every** path, including error paths. 4. Register it via the `methodValidation` key in - [`methods-config.json`](methods-config.json) (see the snippet in §1.4). + [`methods-config.json`](methods-config.json) (see the snippet in 1.4). 5. Test locally by uploading a model that should pass and one that should fail, and confirm the frontend shows the expected verdict and your `reason`. @@ -347,7 +347,7 @@ it cannot turn a rule-based `incompatible` into `compatible`. | Contract | Requirement | |----------|-------------| -| Issue kind keys | Must match the pipeline's `IssueKind` values (§1.1). | +| Issue kind keys | Must match the pipeline's `IssueKind` values (1.1). | | Compatibility level | One of `compatible`, `warning`, `incompatible`. | | Validation function | Named `run_method_validation(input_file: str) -> dict`. | | Validation return | `{"compatible": bool, "reason": str}` on every path. | From b3f0105705682b5c3f9420b0b80479926e4c6eed Mon Sep 17 00:00:00 2001 From: Hary Dhimas Date: Tue, 1 Sep 2026 11:21:11 +0200 Subject: [PATCH 11/11] feat: Add geometry compatibility files for Misuka and Sparrowpy methods, and update Pyroomacoustics notes --- methods-config.json | 2 ++ misuka_method/geometry_compatibility.json | 7 +++++++ pyroomacoustics_method/geometry_compatibility.json | 2 +- sparrowpy_method/geometry_compatibility.json | 7 +++++++ 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 misuka_method/geometry_compatibility.json create mode 100644 sparrowpy_method/geometry_compatibility.json diff --git a/methods-config.json b/methods-config.json index 000ba83..bebd120 100644 --- a/methods-config.json +++ b/methods-config.json @@ -51,6 +51,7 @@ "label": "misuka", "entryFile":"misukaInterface.py", "settings":"misuka_setting.json", + "geometryCompatibility":"misuka_method/geometry_compatibility.json", "repositoryURL":"https://github.com/misuka-renderer/misuka.git", "documentationURL":"https://www.tu.berlin/en/ak/research/projects/differentiable-acoustic-path-tracing" }, @@ -60,6 +61,7 @@ "envVars": {}, "label": "sparrowpy", "settings":"sparrowpy_setting.json", + "geometryCompatibility":"sparrowpy_method/geometry_compatibility.json", "entryFile":"sparrowpy_interface.py", "repositoryURL":"https://github.com/sparrow-acoustics/sparrowpy", "documentationURL":"https://sparrowpy.readthedocs.io/en/stable" diff --git a/misuka_method/geometry_compatibility.json b/misuka_method/geometry_compatibility.json new file mode 100644 index 0000000..f43f1b5 --- /dev/null +++ b/misuka_method/geometry_compatibility.json @@ -0,0 +1,7 @@ +{ + "extends": "choras-baseline", + "method": "Misuka", + "notes": "Misuka is a research-focused room acoustic renderer for forward and inverse sound transport simulation, developed in collaboration between the Audio Communication Group and the Computer Graphics Group at TU Berlin.", + "issues": { + } +} diff --git a/pyroomacoustics_method/geometry_compatibility.json b/pyroomacoustics_method/geometry_compatibility.json index 96c7ccd..3ce0a84 100644 --- a/pyroomacoustics_method/geometry_compatibility.json +++ b/pyroomacoustics_method/geometry_compatibility.json @@ -1,7 +1,7 @@ { "extends": "choras-baseline", "method": "Pyroomacoustics", - "notes": "Example notes", + "notes": "Pyroomacoustics is a software package aimed at the rapid development and testing of audio array processing algorithms.", "issues": { "possible_hole": { "compatibility": "incompatible" diff --git a/sparrowpy_method/geometry_compatibility.json b/sparrowpy_method/geometry_compatibility.json new file mode 100644 index 0000000..67dd30f --- /dev/null +++ b/sparrowpy_method/geometry_compatibility.json @@ -0,0 +1,7 @@ +{ + "extends": "choras-baseline", + "method": "Sparrowpy", + "notes": "Sound Propagation with Acoustic Radiosity for Realistic Outdoor Worlds.", + "issues": { + } +}