From 3a95417ace474e5177acb9c008d7e93b2b7121e8 Mon Sep 17 00:00:00 2001 From: Savio Cardoso Date: Sat, 15 Aug 2026 05:36:04 +0200 Subject: [PATCH] feat: add parallel parameter sweep automation --- configs/thickness_sweep.yaml | 13 ++ docs/figures/thickness_vs_stress.png | Bin 0 -> 80001 bytes docs/validation/thickness_sweep.csv | 7 + scripts/plot_thickness_sweep.py | 32 +++++ src/bodysimpy/analysis/parameter_sweep.py | 149 +++++++++++++++++++++ src/bodysimpy/cli.py | 26 ++++ src/bodysimpy/config/loader.py | 24 +++- src/bodysimpy/config/models.py | 22 +++ src/bodysimpy/workflows/parameter_sweep.py | 39 ++++++ tests/integration/test_parameter_sweep.py | 69 ++++++++++ tests/unit/test_parameter_sweep_output.py | 40 ++++++ tests/unit/test_sweep_config.py | 69 ++++++++++ 12 files changed, 489 insertions(+), 1 deletion(-) create mode 100644 configs/thickness_sweep.yaml create mode 100644 docs/figures/thickness_vs_stress.png create mode 100644 docs/validation/thickness_sweep.csv create mode 100644 scripts/plot_thickness_sweep.py create mode 100644 src/bodysimpy/analysis/parameter_sweep.py create mode 100644 src/bodysimpy/workflows/parameter_sweep.py create mode 100644 tests/integration/test_parameter_sweep.py create mode 100644 tests/unit/test_parameter_sweep_output.py create mode 100644 tests/unit/test_sweep_config.py diff --git a/configs/thickness_sweep.yaml b/configs/thickness_sweep.yaml new file mode 100644 index 0000000..b7cabc2 --- /dev/null +++ b/configs/thickness_sweep.yaml @@ -0,0 +1,13 @@ +base_config: configs/baseline_crossmember.yaml + +thickness_values_m: + - 0.0010 + - 0.0012 + - 0.0014 + - 0.0016 + - 0.0018 + - 0.0020 + +output_csv: docs/validation/thickness_sweep.csv + +max_workers: 4 \ No newline at end of file diff --git a/docs/figures/thickness_vs_stress.png b/docs/figures/thickness_vs_stress.png new file mode 100644 index 0000000000000000000000000000000000000000..2e8638dc57cc4a0400a976c7dfe65f999e998110 GIT binary patch literal 80001 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 z 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 ezqc}v%{+Rz+bJnE*!iF;4|{(k_MPVTS( literal 0 HcmV?d00001 diff --git a/docs/validation/thickness_sweep.csv b/docs/validation/thickness_sweep.csv new file mode 100644 index 0000000..18c6bb1 --- /dev/null +++ b/docs/validation/thickness_sweep.csv @@ -0,0 +1,7 @@ +thickness_mm,max_stress_mpa,deflection_mm,mode1_hz,mass_kg +1.0,258.099,22.537209999999998,50.008,1.8526000000000007 +1.2,217.937,19.029770000000003,49.76519,2.215584 +1.4,189.294,16.528290000000002,49.52232,2.5760559999999972 +1.6,167.852,14.655619999999999,49.27939,2.9340159999999993 +1.8,151.21,13.20223,49.03639,3.2894640000000024 +2.0,137.93,12.04239,48.79333,3.6424 diff --git a/scripts/plot_thickness_sweep.py b/scripts/plot_thickness_sweep.py new file mode 100644 index 0000000..1224209 --- /dev/null +++ b/scripts/plot_thickness_sweep.py @@ -0,0 +1,32 @@ +import matplotlib.pyplot as plt +import pandas as pd + + +def main() -> None: + dataframe = pd.read_csv("docs/validation/thickness_sweep.csv") + + plt.figure(figsize=(8, 5)) + + plt.plot( + dataframe["thickness_mm"], + dataframe["max_stress_mpa"], + marker="o", + ) + + plt.xlabel("Wall thickness [mm]") + plt.ylabel("Maximum axial stress [MPa]") + plt.title("BodySimPy Thickness Sweep — Stress") + + plt.grid(True) + plt.tight_layout() + + plt.savefig( + "docs/figures/thickness_vs_stress.png", + dpi=200, + ) + + plt.close() + + +if __name__ == "__main__": + main() diff --git a/src/bodysimpy/analysis/parameter_sweep.py b/src/bodysimpy/analysis/parameter_sweep.py new file mode 100644 index 0000000..821713e --- /dev/null +++ b/src/bodysimpy/analysis/parameter_sweep.py @@ -0,0 +1,149 @@ +import csv +from concurrent.futures import ThreadPoolExecutor +from dataclasses import dataclass, replace +from pathlib import Path + +from bodysimpy.domain.structural_model import ( + StructuralModel, +) +from bodysimpy.solvers.calculix import ( + CalculiXSolver, +) + + +@dataclass(frozen=True, slots=True) +class SweepPoint: + """Results from one wall-thickness design point.""" + + thickness_m: float + max_stress_pa: float + tip_deflection_m: float + mode_1_frequency_hz: float + mass_kg: float + + +def _evaluate_thickness( + model: StructuralModel, + thickness_m: float, +) -> SweepPoint: + if thickness_m <= 0.0: + raise ValueError("Sweep thickness must be positive.") + + section = replace( + model.section, + thickness_m=thickness_m, + ) + + thickness_mm = thickness_m * 1000.0 + + thickness_tag = f"{thickness_mm:.3f}".replace(".", "p") + + variant = replace( + model, + name=(f"{model.name}_thickness_{thickness_tag}mm"), + section=section, + ) + + solver = CalculiXSolver() + + static_result = solver.run(variant) + + modal_result = solver.run_modal( + variant, + modes=1, + ) + + if static_result.max_axial_stress_pa is None: + raise ValueError("Static solver returned no axial stress.") + + mass_kg = variant.material.density_kg_m3 * variant.section.area_m2 * variant.length_m + + return SweepPoint( + thickness_m=thickness_m, + max_stress_pa=(static_result.max_axial_stress_pa), + tip_deflection_m=(static_result.tip_deflection_m), + mode_1_frequency_hz=(modal_result.natural_frequencies_hz[0]), + mass_kg=mass_kg, + ) + + +def run_thickness_sweep( + model: StructuralModel, + *, + thickness_values_m: tuple[float, ...], + max_workers: int = 1, +) -> tuple[SweepPoint, ...]: + """Evaluate a wall-thickness parameter sweep.""" + + if not thickness_values_m: + raise ValueError("Thickness sweep must contain at least one value.") + + if max_workers <= 0: + raise ValueError("Maximum worker count must be positive.") + + if max_workers == 1: + return tuple( + _evaluate_thickness( + model, + thickness, + ) + for thickness in thickness_values_m + ) + + def evaluate( + thickness_m: float, + ) -> SweepPoint: + return _evaluate_thickness( + model, + thickness_m, + ) + + with ThreadPoolExecutor(max_workers=max_workers) as executor: + return tuple( + executor.map( + evaluate, + thickness_values_m, + ) + ) + + +def write_sweep_csv( + points: tuple[SweepPoint, ...], + path: str | Path, +) -> None: + """Write parameter-sweep results to CSV.""" + + output_path = Path(path) + + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + with output_path.open( + "w", + encoding="utf-8", + newline="", + ) as stream: + writer = csv.writer(stream) + + writer.writerow( + [ + "thickness_mm", + "max_stress_mpa", + "deflection_mm", + "mode1_hz", + "mass_kg", + ] + ) + + for point in points: + writer.writerow( + [ + point.thickness_m * 1000.0, + point.max_stress_pa / 1e6, + point.tip_deflection_m * 1000.0, + point.mode_1_frequency_hz, + point.mass_kg, + ] + ) diff --git a/src/bodysimpy/cli.py b/src/bodysimpy/cli.py index 1ce6c8a..111bd9d 100644 --- a/src/bodysimpy/cli.py +++ b/src/bodysimpy/cli.py @@ -1,5 +1,11 @@ +from pathlib import Path + import typer +from bodysimpy.workflows.parameter_sweep import ( + run_thickness_sweep_from_config, +) + app = typer.Typer( name="bodysim", help="Structural CAE workflow automation for automotive body structures.", @@ -14,5 +20,25 @@ def version() -> None: typer.echo("BodySimPy 0.1.0") +@app.command() +def sweep( + config_path: Path, +) -> None: + """Run a configured parameter sweep.""" + + points = run_thickness_sweep_from_config(config_path) + + typer.echo(f"Completed {len(points)} sweep points.") + + for point in points: + typer.echo( + f"{point.thickness_m * 1000:.1f} mm | " + f"{point.max_stress_pa / 1e6:.3f} MPa | " + f"{point.tip_deflection_m * 1000:.4f} mm | " + f"{point.mode_1_frequency_hz:.4f} Hz | " + f"{point.mass_kg:.4f} kg" + ) + + if __name__ == "__main__": app() diff --git a/src/bodysimpy/config/loader.py b/src/bodysimpy/config/loader.py index 9543be1..35a74c5 100644 --- a/src/bodysimpy/config/loader.py +++ b/src/bodysimpy/config/loader.py @@ -2,7 +2,10 @@ import yaml -from bodysimpy.config.models import SimulationConfig +from bodysimpy.config.models import ( + SimulationConfig, + ThicknessSweepConfig, +) def load_config(path: str | Path) -> SimulationConfig: @@ -17,3 +20,22 @@ def load_config(path: str | Path) -> SimulationConfig: raise TypeError("Configuration root must be a YAML mapping.") return SimulationConfig.model_validate(raw_config) + + +def load_thickness_sweep_config( + path: str | Path, +) -> ThicknessSweepConfig: + """Load and validate a thickness-sweep YAML configuration.""" + + config_path = Path(path) + + with config_path.open( + "r", + encoding="utf-8", + ) as stream: + raw_config: object = yaml.safe_load(stream) + + if not isinstance(raw_config, dict): + raise TypeError("Sweep configuration root must be a YAML mapping.") + + return ThicknessSweepConfig.model_validate(raw_config) diff --git a/src/bodysimpy/config/models.py b/src/bodysimpy/config/models.py index 3406329..5d7e623 100644 --- a/src/bodysimpy/config/models.py +++ b/src/bodysimpy/config/models.py @@ -76,3 +76,25 @@ class SimulationConfig(StrictModel): analysis: AnalysisConfig mesh: MeshConfig stochastic: StochasticConfig | None = None + + +class ThicknessSweepConfig(StrictModel): + """Configuration for a wall-thickness parameter sweep.""" + + base_config: str = Field(min_length=1) + thickness_values_m: tuple[float, ...] + output_csv: str = Field(min_length=1) + max_workers: int = Field(default=1, gt=0) + + @model_validator(mode="after") + def validate_thickness_values(self) -> Self: + if not self.thickness_values_m: + raise ValueError("At least one thickness value is required.") + + if any(thickness <= 0.0 for thickness in self.thickness_values_m): + raise ValueError("All thickness values must be positive.") + + if len(set(self.thickness_values_m)) != len(self.thickness_values_m): + raise ValueError("Thickness sweep values must be unique.") + + return self diff --git a/src/bodysimpy/workflows/parameter_sweep.py b/src/bodysimpy/workflows/parameter_sweep.py new file mode 100644 index 0000000..8d2cac9 --- /dev/null +++ b/src/bodysimpy/workflows/parameter_sweep.py @@ -0,0 +1,39 @@ +from pathlib import Path + +from bodysimpy.analysis.parameter_sweep import ( + SweepPoint, + run_thickness_sweep, + write_sweep_csv, +) +from bodysimpy.config.loader import ( + load_config, + load_thickness_sweep_config, +) +from bodysimpy.modeling.crossmember import ( + build_crossmember_model, +) + + +def run_thickness_sweep_from_config( + path: str | Path, +) -> tuple[SweepPoint, ...]: + """Run a configured wall-thickness parameter sweep.""" + + sweep_config = load_thickness_sweep_config(path) + + base_config = load_config(sweep_config.base_config) + + model = build_crossmember_model(base_config) + + points = run_thickness_sweep( + model, + thickness_values_m=(sweep_config.thickness_values_m), + max_workers=sweep_config.max_workers, + ) + + write_sweep_csv( + points, + sweep_config.output_csv, + ) + + return points diff --git a/tests/integration/test_parameter_sweep.py b/tests/integration/test_parameter_sweep.py new file mode 100644 index 0000000..090a66a --- /dev/null +++ b/tests/integration/test_parameter_sweep.py @@ -0,0 +1,69 @@ +import shutil + +import pytest + +from bodysimpy.analysis.parameter_sweep import ( + run_thickness_sweep, +) +from bodysimpy.config.loader import load_config +from bodysimpy.modeling.crossmember import ( + build_crossmember_model, +) + + +@pytest.mark.skipif( + shutil.which("ccx") is None, + reason="CalculiX is not installed.", +) +def test_thickness_sweep_runs_real_solver() -> None: + config = load_config("configs/baseline_crossmember.yaml") + + model = build_crossmember_model(config) + + points = run_thickness_sweep( + model, + thickness_values_m=( + 0.0010, + 0.0020, + ), + max_workers=1, + ) + + assert len(points) == 2 + + assert points[0].thickness_m == pytest.approx(0.0010) + + assert points[1].thickness_m == pytest.approx(0.0020) + + for point in points: + assert point.max_stress_pa > 0.0 + assert point.tip_deflection_m > 0.0 + assert point.mode_1_frequency_hz > 0.0 + assert point.mass_kg > 0.0 + + assert points[1].mass_kg > points[0].mass_kg + + +@pytest.mark.skipif( + shutil.which("ccx") is None, + reason="CalculiX is not installed.", +) +def test_parallel_thickness_sweep() -> None: + config = load_config("configs/baseline_crossmember.yaml") + + model = build_crossmember_model(config) + + points = run_thickness_sweep( + model, + thickness_values_m=( + 0.0010, + 0.0015, + ), + max_workers=2, + ) + + assert len(points) == 2 + + assert points[0].thickness_m == pytest.approx(0.0010) + + assert points[1].thickness_m == pytest.approx(0.0015) diff --git a/tests/unit/test_parameter_sweep_output.py b/tests/unit/test_parameter_sweep_output.py new file mode 100644 index 0000000..f5b957e --- /dev/null +++ b/tests/unit/test_parameter_sweep_output.py @@ -0,0 +1,40 @@ +from pathlib import Path + +from bodysimpy.analysis.parameter_sweep import ( + SweepPoint, + write_sweep_csv, +) + + +def test_write_sweep_csv( + tmp_path: Path, +) -> None: + output_path = tmp_path / "sweep.csv" + + points = ( + SweepPoint( + thickness_m=0.001, + max_stress_pa=200e6, + tip_deflection_m=0.010, + mode_1_frequency_hz=45.0, + mass_kg=2.0, + ), + SweepPoint( + thickness_m=0.002, + max_stress_pa=120e6, + tip_deflection_m=0.006, + mode_1_frequency_hz=55.0, + mass_kg=3.5, + ), + ) + + write_sweep_csv( + points, + output_path, + ) + + content = output_path.read_text(encoding="utf-8") + + assert "thickness_mm,max_stress_mpa,deflection_mm,mode1_hz,mass_kg" in content + + assert "1.0,200.0,10.0,45.0,2.0" in content diff --git a/tests/unit/test_sweep_config.py b/tests/unit/test_sweep_config.py new file mode 100644 index 0000000..86f5f91 --- /dev/null +++ b/tests/unit/test_sweep_config.py @@ -0,0 +1,69 @@ +import pytest +from pydantic import ValidationError + +from bodysimpy.config.loader import load_thickness_sweep_config +from bodysimpy.config.models import ThicknessSweepConfig + + +def test_valid_thickness_sweep_configuration() -> None: + config = ThicknessSweepConfig.model_validate( + { + "base_config": "configs/baseline_crossmember.yaml", + "thickness_values_m": [ + 0.0010, + 0.0012, + 0.0014, + ], + "output_csv": "docs/validation/thickness_sweep.csv", + "max_workers": 1, + } + ) + + assert config.thickness_values_m == pytest.approx( + ( + 0.0010, + 0.0012, + 0.0014, + ) + ) + + assert config.max_workers == 1 + + +def test_sweep_rejects_non_positive_thickness() -> None: + with pytest.raises(ValidationError): + ThicknessSweepConfig.model_validate( + { + "base_config": "configs/baseline_crossmember.yaml", + "thickness_values_m": [ + 0.0010, + -0.0012, + ], + "output_csv": "docs/validation/thickness_sweep.csv", + "max_workers": 1, + } + ) + + +def test_sweep_rejects_duplicate_thickness_values() -> None: + with pytest.raises(ValidationError): + ThicknessSweepConfig.model_validate( + { + "base_config": "configs/baseline_crossmember.yaml", + "thickness_values_m": [ + 0.0010, + 0.0010, + ], + "output_csv": "docs/validation/thickness_sweep.csv", + "max_workers": 1, + } + ) + + +def test_load_thickness_sweep_yaml() -> None: + config = load_thickness_sweep_config("configs/thickness_sweep.yaml") + + assert len(config.thickness_values_m) == 6 + assert config.thickness_values_m[0] == pytest.approx(0.0010) + assert config.thickness_values_m[-1] == pytest.approx(0.0020) + assert config.max_workers == 4