From 19bd1cbbabbcd2e081223274903691a23da11952 Mon Sep 17 00:00:00 2001 From: PranjalManhgaye Date: Mon, 9 Mar 2026 00:22:32 +0530 Subject: [PATCH 01/94] Add requirements-reference.txt for reproducible Python deps (fixes #610) - Add tools/tests/requirements-reference.txt with pinned versions - Add update_requirements_reference.py to regenerate from reference_versions.yaml - Add validate_requirements_reference.py to check pyprecice matches - Add GitHub Action check-requirements-reference.yml - Document in tools/tests/README.md and release PR template --- .github/pull_request_template.md | 4 +- .../check-requirements-reference.yml | 28 +++++ changelog-entries/610.md | 1 + tools/tests/README.md | 19 +++ tools/tests/requirements-reference.txt | 11 ++ tools/tests/update_requirements_reference.py | 116 ++++++++++++++++++ .../tests/validate_requirements_reference.py | 53 ++++++++ 7 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/check-requirements-reference.yml create mode 100644 changelog-entries/610.md create mode 100644 tools/tests/requirements-reference.txt create mode 100644 tools/tests/update_requirements_reference.py create mode 100644 tools/tests/validate_requirements_reference.py diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 4750058f0..486b3b18e 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -10,7 +10,9 @@ TODO - Add a [sidebar entry](https://github.com/precice/precice.github.io/blob/master/_data/sidebars/tutorial_sidebar.yml) - Add it to the [overview](https://github.com/precice/precice.github.io/blob/master/content/tutorials/tutorials.md) +For **release PRs** (new distribution): update `tools/tests/requirements-reference.txt` if `reference_versions.yaml` changed (`python3 tools/tests/update_requirements_reference.py`). + ## Resources - [Contributing tutorials](https://precice.org/community-contribute-to-precice.html#contributing-tutorials) -- [System tests documentation](https://precice.org/dev-docs-system-tests.html) \ No newline at end of file +- [System tests documentation](https://precice.org/dev-docs-system-tests.html) diff --git a/.github/workflows/check-requirements-reference.yml b/.github/workflows/check-requirements-reference.yml new file mode 100644 index 000000000..15874dcda --- /dev/null +++ b/.github/workflows/check-requirements-reference.yml @@ -0,0 +1,28 @@ +name: Check requirements-reference +on: + push: + branches: + - master + - develop + paths: + - tools/tests/requirements-reference.txt + - tools/tests/reference_versions.yaml + - tools/tests/update_requirements_reference.py + pull_request: + branches: + - master + - develop + paths: + - tools/tests/requirements-reference.txt + - tools/tests/reference_versions.yaml + - tools/tests/update_requirements_reference.py +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.10" + - name: Validate requirements-reference + run: python3 tools/tests/validate_requirements_reference.py diff --git a/changelog-entries/610.md b/changelog-entries/610.md new file mode 100644 index 000000000..dc7b62a95 --- /dev/null +++ b/changelog-entries/610.md @@ -0,0 +1 @@ +- Add `requirements-reference.txt` (lockfile-style pinned Python versions) and `update_requirements_reference.py` for reproducible dependency versions per distribution (fixes [#610](https://github.com/precice/tutorials/issues/610)). Includes GitHub Action to validate pyprecice version matches `reference_versions.yaml`; release PR template reminder to update the reference file. diff --git a/tools/tests/README.md b/tools/tests/README.md index bd935965b..0d9180e62 100644 --- a/tools/tests/README.md +++ b/tools/tests/README.md @@ -185,6 +185,25 @@ User-facing tools: - `print_case_combinations.py`: Prints all possible combinations of tutorial cases, using the `metadata.yaml` files. - `build_docker_images.py`: Build the Docker images for each test - `generate_reference_results.py`: Executes the system tests with the versions defined in `reference_versions.yaml` and generates the reference data archives, with the names described in `tests.yaml`. (should only be used by the CI Pipeline) + - `update_requirements_reference.py`: Regenerates `requirements-reference.txt` with pinned Python versions from `reference_versions.yaml` (for reproducibility, see issue #610). + - `validate_requirements_reference.py`: Validates that `requirements-reference.txt` exists and pyprecice version matches `reference_versions.yaml`. + +### requirements-reference.txt + +A lockfile-style list of pinned Python dependency versions (pyprecice, numpy, matplotlib, nutils, setuptools) for reproducible builds and distributions (see issue [#610](https://github.com/precice/tutorials/issues/610)). This file is a **reference manifest only**: tutorial `run.sh` scripts keep using their own `requirements.txt` (with loose constraints) to avoid merge back-and-forth; system tests use the Docker image’s venv. The reference file records “versions known to work” for a distribution. + +**Update at each release.** For best accuracy (match what CI uses), capture from the systemtest Docker image: + +```bash +docker run --rm pip freeze | python3 update_requirements_reference.py --from-freeze +``` + +Or regenerate from `reference_versions.yaml` only (pyprecice from PYTHON_BINDINGS_REF; others from script defaults): + +```bash +cd tools/tests +python3 update_requirements_reference.py +``` Implementation scripts: diff --git a/tools/tests/requirements-reference.txt b/tools/tests/requirements-reference.txt new file mode 100644 index 000000000..9094a61c1 --- /dev/null +++ b/tools/tests/requirements-reference.txt @@ -0,0 +1,11 @@ +# Pinned Python dependency versions for reproducible system tests and distributions. +# Generated from reference_versions.yaml (PYTHON_BINDINGS_REF). Update at each release. +# Run: python3 update_requirements_reference.py +# +# See tools/tests/README.md for how to update this file. + +matplotlib==3.9.0 +numpy==1.26.4 +nutils==7.2 +pyprecice==3.2.0 +setuptools>=69.0.0 diff --git a/tools/tests/update_requirements_reference.py b/tools/tests/update_requirements_reference.py new file mode 100644 index 000000000..e6c6a2ae0 --- /dev/null +++ b/tools/tests/update_requirements_reference.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +""" +Update requirements-reference.txt with pinned versions from reference_versions.yaml. + +This script reads PYTHON_BINDINGS_REF from reference_versions.yaml and generates +a requirements-reference.txt with pyprecice pinned to the corresponding version. +Other common packages (numpy, matplotlib, setuptools) use fixed versions known +to work with the tutorials. + +Run from tools/tests/: + python3 update_requirements_reference.py + +Or to regenerate from a pip freeze (e.g. from Docker): + pip freeze | python3 update_requirements_reference.py --from-freeze +""" +import argparse +import re +import sys +from pathlib import Path + +REFERENCE_VERSIONS = Path(__file__).parent / "reference_versions.yaml" +REQUIREMENTS_REF = Path(__file__).parent / "requirements-reference.txt" + +# Default pinned versions for common packages (fallback when not using --from-freeze) +DEFAULTS = { + "matplotlib": "3.9.0", + "numpy": "1.26.4", + "nutils": "7.2", + "pyprecice": None, # From reference_versions.yaml + "setuptools": ">=69.0.0", +} + +# Packages to include (in order) +PACKAGES = ["matplotlib", "numpy", "nutils", "pyprecice", "setuptools"] + + +def get_pyprecice_version_from_ref(ref: str) -> str: + """Convert PYTHON_BINDINGS_REF (e.g. v3.2.0) to pyprecice version (3.2.0).""" + return ref.lstrip("v").strip() + + +def load_reference_versions() -> str: + """Load PYTHON_BINDINGS_REF from reference_versions.yaml.""" + text = REFERENCE_VERSIONS.read_text() + for line in text.splitlines(): + if "PYTHON_BINDINGS_REF" in line and ":" in line: + match = re.search(r'["\']([^"\']+)["\']', line) + if match: + return match.group(1) + raise ValueError("PYTHON_BINDINGS_REF not found in reference_versions.yaml") + + +def parse_freezed_packages(freezed: str) -> dict[str, str]: + """Parse pip freeze output into {package: version}.""" + result = {} + for line in freezed.strip().splitlines(): + line = line.strip() + if not line or line.startswith("#"): + continue + if "==" in line: + pkg, ver = line.split("==", 1) + result[pkg.lower()] = f"=={ver.strip()}" + elif "===" in line: + pkg, ver = line.split("===", 1) + result[pkg.lower()] = f"=={ver.strip()}" + return result + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Update requirements-reference.txt from reference_versions.yaml" + ) + parser.add_argument( + "--from-freeze", + action="store_true", + help="Read pip freeze from stdin and use those versions for known packages", + ) + args = parser.parse_args() + + pyprecice_ref = load_reference_versions() + pyprecice_ver = get_pyprecice_version_from_ref(pyprecice_ref) + + if args.from_freeze: + freezed = parse_freezed_packages(sys.stdin.read()) + versions = {} + for pkg in PACKAGES: + if pkg.lower() in freezed: + versions[pkg] = freezed[pkg.lower()] + elif pkg == "pyprecice": + versions[pkg] = f"=={pyprecice_ver}" + elif DEFAULTS.get(pkg): + versions[pkg] = ( + DEFAULTS[pkg] if DEFAULTS[pkg].startswith(("==", ">=", "~=")) else f"=={DEFAULTS[pkg]}" + ) + else: + DEFAULTS["pyprecice"] = pyprecice_ver + versions = { + pkg: f"=={ver}" if ver and not ver.startswith(("==", ">=", "~=")) else (ver or "") + for pkg, ver in DEFAULTS.items() + } + versions["pyprecice"] = f"=={pyprecice_ver}" + + header = """# Pinned Python dependency versions for reproducible system tests and distributions. +# Generated from reference_versions.yaml (PYTHON_BINDINGS_REF). Update at each release. +# Run: python3 update_requirements_reference.py +# +# See tools/tests/README.md for how to update this file. + +""" + lines = [f"{pkg}{versions.get(pkg, '')}\n" for pkg in PACKAGES if pkg in versions] + REQUIREMENTS_REF.write_text(header + "".join(lines)) + print(f"Wrote {REQUIREMENTS_REF} (pyprecice from {pyprecice_ref})") + + +if __name__ == "__main__": + main() diff --git a/tools/tests/validate_requirements_reference.py b/tools/tests/validate_requirements_reference.py new file mode 100644 index 000000000..870f53b8c --- /dev/null +++ b/tools/tests/validate_requirements_reference.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +""" +Validate that requirements-reference.txt exists and pyprecice version +matches PYTHON_BINDINGS_REF in reference_versions.yaml. + +Exit 0 on success, 1 on failure. +""" +import re +import sys +from pathlib import Path + +TOOLS_TESTS = Path(__file__).parent +REFERENCE_VERSIONS = TOOLS_TESTS / "reference_versions.yaml" +REQUIREMENTS_REF = TOOLS_TESTS / "requirements-reference.txt" + + +def main() -> int: + if not REQUIREMENTS_REF.exists(): + print(f"ERROR: {REQUIREMENTS_REF} not found. Run update_requirements_reference.py.", file=sys.stderr) + return 1 + + # Load PYTHON_BINDINGS_REF + ref_text = REFERENCE_VERSIONS.read_text() + ref_match = re.search(r'PYTHON_BINDINGS_REF:\s*["\']([^"\']+)["\']', ref_text) + if not ref_match: + print("ERROR: PYTHON_BINDINGS_REF not found in reference_versions.yaml", file=sys.stderr) + return 1 + + expected_ver = ref_match.group(1).lstrip("v").strip() + + # Parse pyprecice from requirements-reference.txt + req_text = REQUIREMENTS_REF.read_text() + precice_match = re.search(r"pyprecice\s*==\s*([\w.]+)", req_text) + if not precice_match: + print("ERROR: pyprecice not found in requirements-reference.txt", file=sys.stderr) + return 1 + + actual_ver = precice_match.group(1).strip() + if actual_ver != expected_ver: + print( + f"ERROR: pyprecice version mismatch: requirements-reference.txt has {actual_ver}, " + f"reference_versions.yaml PYTHON_BINDINGS_REF has {ref_match.group(1)}. " + "Run: python3 update_requirements_reference.py", + file=sys.stderr, + ) + return 1 + + print(f"OK: requirements-reference.txt pyprecice=={actual_ver} matches reference_versions.yaml") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From 2f63d615f0af4ff7e31d74d49c5e56c24c3c36b1 Mon Sep 17 00:00:00 2001 From: Marin Lauber <48692086+marinlauber@users.noreply.github.com> Date: Mon, 25 May 2026 22:22:54 +0200 Subject: [PATCH 02/94] Add julia example in resonant circuit (#658) --- .gitignore | 4 + changelog-entries/658.md | 1 + resonant-circuit/README.md | 43 +++++----- resonant-circuit/capacitor-julia/capacitor.jl | 82 ++++++++++++++++++ resonant-circuit/capacitor-julia/clean.sh | 7 ++ resonant-circuit/capacitor-julia/run.sh | 10 +++ resonant-circuit/capacitor-matlab/clean.sh | 3 +- resonant-circuit/capacitor-python/clean.sh | 7 ++ resonant-circuit/coil-julia/clean.sh | 7 ++ resonant-circuit/coil-julia/coil.jl | 85 +++++++++++++++++++ resonant-circuit/coil-julia/run.sh | 10 +++ resonant-circuit/coil-python/clean.sh | 7 ++ resonant-circuit/precice-config.xml | 7 ++ 13 files changed, 249 insertions(+), 24 deletions(-) create mode 100644 changelog-entries/658.md create mode 100644 resonant-circuit/capacitor-julia/capacitor.jl create mode 100755 resonant-circuit/capacitor-julia/clean.sh create mode 100755 resonant-circuit/capacitor-julia/run.sh create mode 100755 resonant-circuit/capacitor-python/clean.sh create mode 100755 resonant-circuit/coil-julia/clean.sh create mode 100644 resonant-circuit/coil-julia/coil.jl create mode 100755 resonant-circuit/coil-julia/run.sh create mode 100755 resonant-circuit/coil-python/clean.sh diff --git a/.gitignore b/.gitignore index 7f2055b46..4b7dafe69 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,10 @@ __pycache__/ Cargo.lock target/ +# Julia +Manifest.toml +Project.toml + # OpenFOAM 0.*/ [1-9]*/ diff --git a/changelog-entries/658.md b/changelog-entries/658.md new file mode 100644 index 000000000..cf9e728c7 --- /dev/null +++ b/changelog-entries/658.md @@ -0,0 +1 @@ +- Added Julia-based participants to the resonant-circuit tutorial [#658](https://github.com/precice/tutorials/pull/658) \ No newline at end of file diff --git a/resonant-circuit/README.md b/resonant-circuit/README.md index fbbcbc4e9..4f81fa45b 100644 --- a/resonant-circuit/README.md +++ b/resonant-circuit/README.md @@ -1,14 +1,14 @@ --- -title: Resonant Circuit +title: Resonant circuit permalink: tutorials-resonant-circuit.html -keywords: MATLAB, Python +keywords: MATLAB, Python, Julia summary: We simulate a two-element LC circuit (one inductor and one capacitor). --- ## Setup -The purpose of this tutorial is to illustrate the usage of preCICE to couple MATLAB code. Two different MATLAB solvers will be coupled to simulate a two-element LC circuit. This type of circuit consists of a very simple system with one inductor and one capacitor: +Two different solvers are coupled to simulate a two-element LC circuit. This type of circuit consists of a simple system with one inductor and one capacitor: ![LC circuit diagram [1]](images/tutorials-resonant-circuit-diagram.svg) @@ -20,7 +20,7 @@ $I(t) = -C \frac{\text{d}U}{\text{d}t}$ where $I$ is the current and $U$ the voltage of the circuit. -Each of these equations is going to be solved by a different MATLAB solver. Note that, as only one scalar is solved per equation, this is a 0+1 dimensional problem. +Each of these equations is solved by a different solver. Note that, as only one scalar is solved per equation, this is a 0+1 dimensional problem. ## Configuration @@ -31,43 +31,42 @@ preCICE configuration (image generated using the [precice-config-visualizer](htt ## Available solvers * *MATLAB* A solver using the [MATLAB bindings](https://precice.org/installation-bindings-matlab.html). - Before running this tutorial, follow the [instructions](https://precice.org/installation-bindings-matlab.html) to correctly install the MATLAB bindings. * *Python* A solver using the preCICE [Python bindings](https://precice.org/installation-bindings-python.html). +* *Julia* A solver using the preCICE [Julia bindings](https://precice.org/installation-bindings-julia.html). ## Running the simulation -### MATLAB +All listed solvers can be used to run the simulation. Open two separate terminals and start the desired capacitor and coil participants by calling the respective run script. For example: -For running this example, first get into one of the solver folders and open a MATLAB instance. -Afterward, do the same for the second solver. -After adding the MATLAB bindings to the MATLAB path (in both instances), run the following commands: - -In the first MATLAB instance, one can run the solver for the current: - -```MATLAB -coil +```bash +cd capacitor-python +./run.sh ``` -And in the second MATLAB instance, the solver for the voltage: +and -```MATLAB -capacitor +```bash +cd coil-julia +./run.sh ``` -The preCICE configuration file is hard-coded as `precice-config.xml` in the solvers. +### Running the MATLAB participants + +For running this example in the MATLAB GUI, first get into each of the solver folders and open a MATLAB instance for each. +After adding the MATLAB bindings to the MATLAB path (in both instances), run the `coil` and `capacitor` commands in the two windows. -#### Running from terminal +The path to the preCICE configuration file is hard-coded as `precice-config.xml` in the solvers. -If you prefer to not open the MATLAB GUIs, you can alternatively use two shells instead. +If you prefer not to use the MATLAB GUI, you can alternatively use two shells instead. For that, modify the path in the file `matlab-bindings-path.sh` found in the base directory of this tutorial to the path to your MATLAB bindings. By doing that, you can now open two shells and switch into the directories `capacitor-matlab` and `coil-matlab` and execute the `run.sh` scripts. ## Post-processing -As we defined a watchpoint on the 'Capacitor' participant (see `precice-config.xml`), we can plot it with gnuplot using the script `plot-solution.sh.` You need to specify the directory of the selected solid participant as a command line argument, so that the script can pick-up the desired watchpoint file, e.g. `./plot-solution.sh capacitor-python`. The resulting graph shows the voltage and current exchanged between the two participants. +As we defined a watchpoint on the 'Capacitor' participant (see `precice-config.xml`), we can plot it with gnuplot using the script `plot-solution.sh`. You need to specify the directory of the selected solid participant as a command line argument, so that the script can pick-up the desired watchpoint file, e.g., `./plot-solution.sh capacitor-python`. The resulting graph shows the voltage and current exchanged between the two participants. -Additionally, the MATLAB participant `capacitor-matlab` records the current and voltage over time. At the end of the simulation it creates a plot with the computed waveforms of current and voltage, as well as the analytical solution. +Additionally, the `capacitor-matlab` case records the current and voltage over time. At the end of the simulation, it creates a plot with the computed waveforms of current and voltage, as well as the analytical solution. After successfully running the coupling, one can find the curves in the folder `capacitor-matlab` as `Curves.png`. diff --git a/resonant-circuit/capacitor-julia/capacitor.jl b/resonant-circuit/capacitor-julia/capacitor.jl new file mode 100644 index 000000000..9219c5cae --- /dev/null +++ b/resonant-circuit/capacitor-julia/capacitor.jl @@ -0,0 +1,82 @@ +using PreCICE +using DifferentialEquations + +# Initialize and configure preCICE +participant = PreCICE.createParticipant("Capacitor", "../precice-config.xml", 0, 1) + +# Geometry IDs. As it is a 0-D simulation, only one vertex is necessary. +mesh_name = "Capacitor-Mesh" + +dimensions = PreCICE.getMeshDimensions(mesh_name) + +vertex_ids = PreCICE.setMeshVertices(mesh_name, zeros((1,dimensions))) + +let + # Data IDs + read_data_name = "Current" + write_data_name = "Voltage" + + # Simulation parameters and initial condition + C = 2 # Capacitance of the capacitor + L = 1 # Inductance of the coil + t0 = 0 # Initial simulation time + t_max = 10 # End simulation time + Io = 1 # Initial current + phi = 0 # Phase of the signal + + w0 = 1 / sqrt(L * C) # Resonant frequency + U0 = -w0 * L * Io * sin(phi) # Initial condition for U + + function f_U(u, p, t) + (dt, C, mesh_name, read_data_name, vertex_ids) = p + I = PreCICE.readData(mesh_name, read_data_name, vertex_ids, dt) + return -I[1] / C # Time derivative of U + end + + # Initialize simulation + if PreCICE.requiresInitialData() + PreCICE.writeData(mesh_name, write_data_name, vertex_ids, I0) + end + PreCICE.initialize() + + solver_dt = PreCICE.getMaxTimeStepSize() + + # Start simulation + t = t0 + U0_checkpoint = U0 + t_checkpoint = t + while PreCICE.isCouplingOngoing() + + # Record checkpoint if necessary + if PreCICE.requiresWritingCheckpoint() + U0_checkpoint = U0 + t_checkpoint = t + end + + # Make Simulation Step + precice_dt = PreCICE.getMaxTimeStepSize() + dt = min(precice_dt, solver_dt) + t_span = (t, t + dt) + params = (dt, C, mesh_name, read_data_name, vertex_ids) + prob = ODEProblem(f_U, U0, t_span, params) + # https://docs.sciml.ai/DiffEqDocs/stable/basics/common_solver_opts + sol = solve(prob, Tsit5(), reltol=1e-12, abstol=1e-12) + + # Exchange data + evals = max(length(sol.t), 3) # at least do 3 substeps to allow cubic interpolation + for i in range(1,evals) + U0 = sol(t_span[1] + i * dt / evals) + PreCICE.writeData(mesh_name, write_data_name, vertex_ids, fill(U0, (1,1))) + PreCICE.advance(dt / evals) + end + t = t + dt + + # Recover checkpoint if not converged + if PreCICE.requiresReadingCheckpoint() + U0 = U0_checkpoint + t = t_checkpoint + end + end + # Stop coupling + PreCICE.finalize() +end \ No newline at end of file diff --git a/resonant-circuit/capacitor-julia/clean.sh b/resonant-circuit/capacitor-julia/clean.sh new file mode 100755 index 000000000..8c3dba08a --- /dev/null +++ b/resonant-circuit/capacitor-julia/clean.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -e -u + +. ../../tools/cleaning-tools.sh + +clean_precice_logs . +clean_case_logs . \ No newline at end of file diff --git a/resonant-circuit/capacitor-julia/run.sh b/resonant-circuit/capacitor-julia/run.sh new file mode 100755 index 000000000..a61d1fa67 --- /dev/null +++ b/resonant-circuit/capacitor-julia/run.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -e -u + +. ../../tools/log.sh +exec > >(tee --append "$LOGFILE") 2>&1 + +julia --project=Project.toml -e 'using Pkg; Pkg.add("DifferentialEquations"); Pkg.add("PreCICE"); Pkg.instantiate();' +julia --project=Project.toml capacitor.jl + +close_log \ No newline at end of file diff --git a/resonant-circuit/capacitor-matlab/clean.sh b/resonant-circuit/capacitor-matlab/clean.sh index 6a77c6d6c..8b1311d03 100755 --- a/resonant-circuit/capacitor-matlab/clean.sh +++ b/resonant-circuit/capacitor-matlab/clean.sh @@ -1,8 +1,7 @@ #!/bin/sh set -e -u -rm ./*.png ./*.mat - . ../../tools/cleaning-tools.sh clean_matlab . +rm -f ./*.png ./*.mat diff --git a/resonant-circuit/capacitor-python/clean.sh b/resonant-circuit/capacitor-python/clean.sh new file mode 100755 index 000000000..8c3dba08a --- /dev/null +++ b/resonant-circuit/capacitor-python/clean.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -e -u + +. ../../tools/cleaning-tools.sh + +clean_precice_logs . +clean_case_logs . \ No newline at end of file diff --git a/resonant-circuit/coil-julia/clean.sh b/resonant-circuit/coil-julia/clean.sh new file mode 100755 index 000000000..8c3dba08a --- /dev/null +++ b/resonant-circuit/coil-julia/clean.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -e -u + +. ../../tools/cleaning-tools.sh + +clean_precice_logs . +clean_case_logs . \ No newline at end of file diff --git a/resonant-circuit/coil-julia/coil.jl b/resonant-circuit/coil-julia/coil.jl new file mode 100644 index 000000000..6cd949ebb --- /dev/null +++ b/resonant-circuit/coil-julia/coil.jl @@ -0,0 +1,85 @@ +using PreCICE +using DifferentialEquations + +# Initialize and configure preCICE +participant = PreCICE.createParticipant("Coil", "../precice-config.xml", 0, 1) + +# Geometry IDs. As it is a 0-D simulation, only one vertex is necessary. +mesh_name = "Coil-Mesh" + +dimensions = PreCICE.getMeshDimensions(mesh_name) + +vertex_ids = PreCICE.setMeshVertices(mesh_name, zeros((1,dimensions))) + +let + # Data IDs + read_data_name = "Voltage" + write_data_name = "Current" + + # Simulation parameters and initial condition + C = 2 # Capacitance of the capacitor + L = 1 # Inductance of the coil + t0 = 0 # Initial simulation time + Io = 1 # Initial current + phi = 0 # Phase of the signal + + w0 = 1 / sqrt(L * C) # Resonant frequency + I0 = Io * cos(phi) # Initial condition for I + + # to estimate cost + f_evals = 0 + + function f_I(u, p, t) + f_evals += 1 + (dt, L, mesh_name, read_data_name, vertex_ids) = p + U = PreCICE.readData(mesh_name, read_data_name, vertex_ids, dt) + return U[1] / L # Time derivative of I; ODE determining capacitor + end + + # Initialize simulation + if PreCICE.requiresInitialData() + PreCICE.writeData(mesh_name, write_data_name, vertex_ids, I0) + end + PreCICE.initialize() + + solver_dt = PreCICE.getMaxTimeStepSize() + + # Start simulation + t = t0 + I0_checkpoint = I0 + t_checkpoint = t + while PreCICE.isCouplingOngoing() + + # Record checkpoint if necessary + if PreCICE.requiresWritingCheckpoint() + I0_checkpoint = I0 + t_checkpoint = t + end + + # Make Simulation Step + precice_dt = PreCICE.getMaxTimeStepSize() + dt = min(precice_dt, solver_dt) + t_span = (t, t + dt) + params = (dt, L, mesh_name, read_data_name, vertex_ids) + prob = ODEProblem(f_I, I0, t_span, params) + # https://docs.sciml.ai/DiffEqDocs/stable/basics/common_solver_opts + sol = solve(prob, Tsit5(), reltol=1e-12, abstol=1e-12) + + # Exchange data + evals = max(length(sol.t), 3) # at least do 3 substeps to allow cubic interpolation + for i in range(1,evals) + I0 = sol(t_span[1] + i * dt / evals) + PreCICE.writeData(mesh_name, write_data_name, vertex_ids, fill(I0, (1,1))) + PreCICE.advance(dt / evals) + end + t = t + dt + + # Recover checkpoint if not converged + if PreCICE.requiresReadingCheckpoint() + I0 = I0_checkpoint + t = t_checkpoint + end + end + # Stop coupling + PreCICE.finalize() +end \ No newline at end of file diff --git a/resonant-circuit/coil-julia/run.sh b/resonant-circuit/coil-julia/run.sh new file mode 100755 index 000000000..6340410ae --- /dev/null +++ b/resonant-circuit/coil-julia/run.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -e -u + +. ../../tools/log.sh +exec > >(tee --append "$LOGFILE") 2>&1 + +julia --project=Project.toml -e 'using Pkg; Pkg.add("DifferentialEquations"); Pkg.add("PreCICE"); Pkg.instantiate();' +julia --project=Project.toml coil.jl + +close_log \ No newline at end of file diff --git a/resonant-circuit/coil-python/clean.sh b/resonant-circuit/coil-python/clean.sh new file mode 100755 index 000000000..8c3dba08a --- /dev/null +++ b/resonant-circuit/coil-python/clean.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -e -u + +. ../../tools/cleaning-tools.sh + +clean_precice_logs . +clean_case_logs . \ No newline at end of file diff --git a/resonant-circuit/precice-config.xml b/resonant-circuit/precice-config.xml index cda70ee19..fb72e5772 100644 --- a/resonant-circuit/precice-config.xml +++ b/resonant-circuit/precice-config.xml @@ -1,5 +1,12 @@ + + + + From 6b0d0822c3904ff4a9dc926741192efe2e78cbb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20H=C3=B6hn?= Date: Mon, 25 May 2026 22:38:02 +0200 Subject: [PATCH 03/94] Make the Quickstart compatible with foam-extend (#654) --- changelog-entries/654.md | 1 + quickstart/README.md | 6 +++ quickstart/fluid-openfoam/0/U | 3 ++ .../fluid-openfoam/constant/dynamicMeshDict | 2 + quickstart/fluid-openfoam/system/controlDict | 18 ++++++- quickstart/fluid-openfoam/system/fvSchemes | 1 + quickstart/fluid-openfoam/system/fvSolution | 18 +++++++ quickstart/fluid-openfoam/system/preciceDict | 1 + tools/run-foam-extend.sh | 47 +++++++++++++++++++ 9 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 changelog-entries/654.md create mode 100755 tools/run-foam-extend.sh diff --git a/changelog-entries/654.md b/changelog-entries/654.md new file mode 100644 index 000000000..12b0bee1a --- /dev/null +++ b/changelog-entries/654.md @@ -0,0 +1 @@ +- Added tools/run-foam-extend.sh to modify OpenFOAM tutorials for foam-extend and made the Quickstart tutorial compatible with foam-extend [#654](https://github.com/precice/tutorials/pull/654). \ No newline at end of file diff --git a/quickstart/README.md b/quickstart/README.md index b3561e0a7..491b50637 100644 --- a/quickstart/README.md +++ b/quickstart/README.md @@ -120,6 +120,12 @@ You can also run OpenFOAM in parallel: `./run.sh -parallel`. In serial, the simulation should take less than a minute to compute (simulated time: 2.5s). +{% note %} +While we recommend starting with the latest OpenFOAM version from openfoam.com, +this case can alternatively be executed with foam-extend. +For that, replace `run-openfoam.sh` with `run-foam-extend.sh` in the `run.sh` script. +{% endnote %} + ## Visualizing the results You can visualize the simulation results of the `Fluid` participant using ParaView and loading the (empty) file `fluid-openfoam/fluid-openfoam.foam`. The rigid body does not generate any readable output files, but the OpenFOAM data should be enough for now: click "play" in ParaView, the flap should already be moving! 🎉 diff --git a/quickstart/fluid-openfoam/0/U b/quickstart/fluid-openfoam/0/U index ebf7eb904..7d1ead131 100644 --- a/quickstart/fluid-openfoam/0/U +++ b/quickstart/fluid-openfoam/0/U @@ -22,11 +22,14 @@ boundaryField top { type noSlip; + value uniform (0 0 0); + // Note: Values in noSlip are only needed for foam-extend } bottom { type noSlip; + value uniform (0 0 0); } inlet diff --git a/quickstart/fluid-openfoam/constant/dynamicMeshDict b/quickstart/fluid-openfoam/constant/dynamicMeshDict index ec8a77f10..eb04dd676 100644 --- a/quickstart/fluid-openfoam/constant/dynamicMeshDict +++ b/quickstart/fluid-openfoam/constant/dynamicMeshDict @@ -14,6 +14,8 @@ motionSolverLibs ("libfvMotionSolvers.so"); solver displacementLaplacian; // OpenFOAM9 or newer: rename "solver" to "motionSolver" +diffusivity uniform; // Only relevant to foam-extend + displacementLaplacianCoeffs { diffusivity quadratic inverseDistance (flap); } diff --git a/quickstart/fluid-openfoam/system/controlDict b/quickstart/fluid-openfoam/system/controlDict index a527b5781..0237a4453 100644 --- a/quickstart/fluid-openfoam/system/controlDict +++ b/quickstart/fluid-openfoam/system/controlDict @@ -3,13 +3,17 @@ FoamFile version 2.0; format ascii; class dictionary; + location "system"; object controlDict; } +// Note: This file is prepared to run with the latest supported version of OpenFOAM.com. +// The tutorials/tools/run-foam-extend.sh script can modify this and other files to run with foam-extend. +// The format of the file (e.g., linked libraries in one line each) is at parts important to the script. + application pimpleFoam; // latest OpenFOAM // application pimpleDyMFoam; // OpenFOAM v1712, OpenFOAM 5.x, or older - startFrom startTime; startTime 0; @@ -36,7 +40,10 @@ timeFormat general; timePrecision 8; -libs ("libpreciceAdapterFunctionObject.so"); +libs +( + "libpreciceAdapterFunctionObject.so" +); functions { forces @@ -47,6 +54,13 @@ functions rho rhoInf; log true; rhoInf 10; + // The following entries are only relevant to foam-extend + functionObjectLibs ( "libforces.so" ); + outputControl timeStep; + outputInterval 1; + pName p; + UName U; + rhoName rhoInf; CofR (0 0 0); } diff --git a/quickstart/fluid-openfoam/system/fvSchemes b/quickstart/fluid-openfoam/system/fvSchemes index 2035cd05c..02b56e6fc 100644 --- a/quickstart/fluid-openfoam/system/fvSchemes +++ b/quickstart/fluid-openfoam/system/fvSchemes @@ -21,6 +21,7 @@ divSchemes { default none; div(phi,U) Gauss linearUpwind grad(U); + div((nuEff*dev(T(grad(U))))) Gauss linear; // Only relevant to foam-extend div((nuEff*dev2(T(grad(U))))) Gauss linear; } diff --git a/quickstart/fluid-openfoam/system/fvSolution b/quickstart/fluid-openfoam/system/fvSolution index 813288c04..9db2e2313 100644 --- a/quickstart/fluid-openfoam/system/fvSolution +++ b/quickstart/fluid-openfoam/system/fvSolution @@ -14,6 +14,10 @@ solvers tolerance 1e-6; relTol 1e-4; smoother DICGaussSeidel; + // The following entries are only relevant to foam-extend + agglomerator faceAreaPair; + nCellsInCoarsestLevel 10; + mergeLevels 1; } pFinal @@ -78,3 +82,17 @@ potentialFlow { nNonOrthogonalCorrectors 1; } + +// The relaxationFactors and fieldBounds are only relevant to foam-extend +relaxationFactors +{ + U 0.7; + UFinal 1; +} + +fieldBounds +{ + p -1e5 1e5; + U 100; +} + diff --git a/quickstart/fluid-openfoam/system/preciceDict b/quickstart/fluid-openfoam/system/preciceDict index c67f623a0..69e4bfdcc 100644 --- a/quickstart/fluid-openfoam/system/preciceDict +++ b/quickstart/fluid-openfoam/system/preciceDict @@ -36,4 +36,5 @@ interfaces FSI { rho rho [1 -3 0 0 0 0 0] 1000; + nu nu [0 2 -1 0 0 0 0] 0.001; // Only relevant to foam-extend } diff --git a/tools/run-foam-extend.sh b/tools/run-foam-extend.sh new file mode 100755 index 000000000..9e0aa0e95 --- /dev/null +++ b/tools/run-foam-extend.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env sh +set -e # Not setting -u as it gets triggered by the OpenFOAM RunFunctions + +# Prepare an (intentionally empty) .foam file for the ParaView OpenFOAM reader +CASENAME="$(pwd | xargs basename)" +touch "$CASENAME.foam" + +# Keep a backup of the files to modify +echo "backing up the original files (copies: 0/U.orig, system/controlDict.orig, constant/dynamicMeshDict.orig)" +cp 0/U 0/U.orig +cp system/controlDict system/controlDict.orig +cp constant/dynamicMeshDict constant/dynamicMeshDict.orig + +# Modify code for foam-extend +echo "modifying everything now" +sed -i "s/noSlip;/noSlipWall;/g" 0/U +sed -i "s,application pimpleFoam;,//application pimpleFoam;,g" system/controlDict +sed -i "s,// application pimpleDyMFoam;,application pimpleDyMFoam;,g" system/controlDict +sed -i '41i\ \ \ \ "liblduSolvers.so"' system/controlDict +sed -i '41i\ \ \ \ "libforces.so"' system/controlDict +sed -i "s,writeCompression off,writeCompression uncompressed,g" system/controlDict + +sed -i "s/libfvMotionSolvers\./libfvMotionSolver\./g" constant/dynamicMeshDict + +# OpenFOAM run functions: getApplication, getNumberOfProcessors +# shellcheck disable=SC1090 # This is an OpenFOAM file which we don't need to check +. "${WM_PROJECT_DIR}/bin/tools/RunFunctions" +solver=$(getApplication | cut -f 1 -d " " | sed '\~//~d') +if [ "${1:-}" = "-parallel" ]; then + procs=$(getNumberOfProcessors) + decomposePar -force + mpirun -np "${procs}" "${solver}" -parallel + reconstructPar +else + ${solver} +fi + +# Reverse code for OpenFOAM +#rm -rf constant/polyMesh +#sed -i "s/noSlipWall;/noSlip;/g" 0/U +#sed -i "s,application pimpleDyMFoam;,// application pimpleDyMFoam;,g" system/controlDict +#sed -i "s,//application pimpleFoam;,application pimpleFoam;,g" system/controlDict +#sed -i '/ "liblduSolvers.so"/d' system/controlDict +#sed -i '/ "libforces.so/d' system/controlDict +#sed -i "s,writeCompression uncompressed,writeCompression off,g" system/controlDict +# +#sed -i "s/libfvMotionSolver\./libfvMotionSolvers\./g" constant/dynamicMeshDict From e545b8c3be493a3598d9cc084c92ff7802cb2437 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Mon, 25 May 2026 23:26:18 +0200 Subject: [PATCH 04/94] Add flow-over-heated-plate-two-meshes test case (#708) --- changelog-entries/708.md | 1 + .../metadata.yaml | 20 +++++++++++++ tools/tests/tests.yaml | 28 +++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 changelog-entries/708.md create mode 100644 flow-over-heated-plate-two-meshes/metadata.yaml diff --git a/changelog-entries/708.md b/changelog-entries/708.md new file mode 100644 index 000000000..26fc5b820 --- /dev/null +++ b/changelog-entries/708.md @@ -0,0 +1 @@ +- Added the flow-over-heated-plate-two-meshes case to the system tests [#708](https://github.com/precice/tutorials/pull/708) \ No newline at end of file diff --git a/flow-over-heated-plate-two-meshes/metadata.yaml b/flow-over-heated-plate-two-meshes/metadata.yaml new file mode 100644 index 000000000..bf0d2f2f0 --- /dev/null +++ b/flow-over-heated-plate-two-meshes/metadata.yaml @@ -0,0 +1,20 @@ +name: Flow over heated plate with two meshes +path: flow-over-heated-plate-two-meshes # relative to git repo +url: https://precice.org/tutorials-flow-over-heated-plate-two-meshes.html + +participants: + - Fluid + - Solid + +cases: + fluid-openfoam: + participant: Fluid + directory: ./fluid-openfoam + run: ./run.sh + component: openfoam-adapter + + solid-calculix: + participant: Solid + directory: ./solid-calculix + run: ./run.sh + component: calculix-adapter diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index 1cac21867..07abe7b7e 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -1,4 +1,13 @@ test_suites: + flow-over-heated-plate-two-meshes: + tutorials: + - &flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix + path: flow-over-heated-plate-two-meshes + case_combination: + - fluid-openfoam + - solid-calculix + reference_result: ./flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz + quickstart_test: tutorials: - &quickstart @@ -30,6 +39,7 @@ test_suites: - fluid-openfoam - solid-openfoam reference_result: ./flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz + - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix openfoam_adapter_release: tutorials: - path: flow-over-heated-plate @@ -37,6 +47,22 @@ test_suites: - fluid-openfoam - solid-openfoam reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz + - path: perpendicular-flap + case_combination: + - fluid-openfoam + - solid-calculix + reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz + - path: perpendicular-flap + case_combination: + - fluid-openfoam + - solid-openfoam + reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz + - path: flow-over-heated-plate-nearest-projection + case_combination: + - fluid-openfoam + - solid-openfoam + reference_result: ./flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz + - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix fenics_test: tutorials: - path: flow-over-heated-plate @@ -63,6 +89,7 @@ test_suites: - fluid-openfoam - solid-calculix reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz + - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix dumux_test: tutorials: - path: two-scale-heat-conduction @@ -176,6 +203,7 @@ test_suites: - fluid-openfoam - solid-openfoam reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz + - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix - path: perpendicular-flap case_combination: - fluid-openfoam From 94c979c9b6a2773f34b227a3712388dd94e442c7 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Mon, 25 May 2026 23:40:30 +0200 Subject: [PATCH 05/94] Remove Nutils version from the tutorial descriptions It is installed automatically and we do not specify such versions for other solvers. --- channel-transport-particles/README.md | 2 +- channel-transport/README.md | 4 ++-- partitioned-heat-conduction-direct/README.md | 8 +++++++- partitioned-heat-conduction/README.md | 4 ++-- perpendicular-flap/README.md | 4 ++-- turek-hron-fsi3/README.md | 4 ++-- volume-coupled-flow/README.md | 2 +- 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/channel-transport-particles/README.md b/channel-transport-particles/README.md index d6725e66c..24e090e9d 100644 --- a/channel-transport-particles/README.md +++ b/channel-transport-particles/README.md @@ -28,7 +28,7 @@ preCICE configuration (image generated using the [precice-config-visualizer](htt Fluid participant: -* Nutils. For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). This Nutils solver requires at least Nutils v7.0. +* Nutils. For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). * OpenFOAM (pimpleFoam). For more information, have a look at the [OpenFOAM adapter documentation](https://precice.org/adapter-openfoam-overview.html). diff --git a/channel-transport/README.md b/channel-transport/README.md index a929c4acb..efa81fa12 100644 --- a/channel-transport/README.md +++ b/channel-transport/README.md @@ -37,13 +37,13 @@ preCICE configuration (image generated using the [precice-config-visualizer](htt Fluid participant: -* Nutils. For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). This Nutils solver requires at least Nutils v7.0. +* Nutils. For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). * OpenFOAM (pimpleFoam). For more information, have a look at the [OpenFOAM adapter documentation](https://precice.org/adapter-openfoam-overview.html). Transport participant: -* Nutils with support for [adaptive mesh refinement](https://precice.org/couple-your-code-moving-or-changing-meshes.html#pseudo-reference-domain). For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). This Nutils solver requires at least Nutils v7.0 and a preCICE release with [remeshing support](couple-your-code-moving-or-changing-meshes.html#remeshing-using-precice). +* Nutils with support for [adaptive mesh refinement](https://precice.org/couple-your-code-moving-or-changing-meshes.html#pseudo-reference-domain). For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). This solver requires a preCICE release with [remeshing support](couple-your-code-moving-or-changing-meshes.html#remeshing-using-precice). ## Running the simulation diff --git a/partitioned-heat-conduction-direct/README.md b/partitioned-heat-conduction-direct/README.md index 80faa5fca..3826c1397 100644 --- a/partitioned-heat-conduction-direct/README.md +++ b/partitioned-heat-conduction-direct/README.md @@ -23,9 +23,15 @@ preCICE configuration (image generated using the [precice-config-visualizer](htt ![preCICE configuration visualization](images/tutorials-partitioned-heat-conduction-direct-precice-config.png) +The data mapping is computed by directly sampling the FEM function representation at the inquired locations. + ## Available solvers -Currently only `nutils` is provided as a solver. The data mapping is computed by directly sampling the FEM function representation at the inquired locations. +You can either couple a solver with itself or different solvers with each other. Available solvers: + +- Nutils. For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). + +- G+Smo. Install the [G+Smo adapter](https://precice.org/adapter-gismo.html). ## Running the simulation diff --git a/partitioned-heat-conduction/README.md b/partitioned-heat-conduction/README.md index dabdfc66a..8b390f7f9 100644 --- a/partitioned-heat-conduction/README.md +++ b/partitioned-heat-conduction/README.md @@ -29,13 +29,13 @@ preCICE configuration (image generated using the [precice-config-visualizer](htt ## Available solvers and dependencies -You can either couple a solver with itself or different solvers with each other. In any case you will need to have preCICE and the python bindings installed on your system. +You can either couple a solver with itself or different solvers with each other. Available solvers: * FEniCS. Install [FEniCS](https://fenicsproject.org/download/archive/) and the [FEniCS-adapter](https://github.com/precice/fenics-adapter). The code is largely based on this [fenics-tutorial](https://github.com/hplgit/fenics-tutorial/blob/master/pub/python/vol1/ft03_heat.py) from [1]. * FEniCSx. Install [FEniCSx](https://fenicsproject.org/download/) and the [FEniCSx-adapter](https://github.com/precice/fenicsx-adapter). The code is largely based on this [fenics-tutorial](https://github.com/hplgit/fenics-tutorial/blob/master/pub/python/vol1/ft03_heat.py) from [1]. -* Nutils. Install [Nutils](https://nutils.org/install-nutils.html). +* Nutils. For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). * OpenFOAM. This case uses the custom [heatTransfer](https://github.com/precice/tutorials/blob/master/partitioned-heat-conduction/solver-openfoam/heatTransfer.C) solver (find it in `solver-openfoam` and build with `wmake`). Read more details in the [OpenFOAM adapter](https://precice.org/adapter-openfoam-overview.html). diff --git a/perpendicular-flap/README.md b/perpendicular-flap/README.md index e50562681..022150d3a 100644 --- a/perpendicular-flap/README.md +++ b/perpendicular-flap/README.md @@ -31,7 +31,7 @@ Fluid participant: * SU2. As opposed to the other two fluid codes, SU2 is in particular specialized for compressible flow. Therefore the default simulation parameters have been adjusted in order to pull the setup into the compressible flow regime. For more information, have a look at the [SU2 adapter documentation](https://precice.org/adapter-su2-overview.html). -* Nutils. For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). This Nutils solver requires at least Nutils v6.0. This case currently takes orders of magnitude longer than the OpenFOAM and SU2 cases, see [related issue](https://github.com/precice/tutorials/issues/506). +* Nutils. For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). This case currently takes orders of magnitude longer than the OpenFOAM and SU2 cases, see [related issue](https://github.com/precice/tutorials/issues/506). * Fake. A simple python script that acts as a fake solver and provides an arbitrary force, linearly-increasing per length of the flap. This solver can be used for debugging of the solid participant and its adapter. It also technically works with implicit coupling, thus no changes to the preCICE configuration are necessary. Note that [ASTE's replay mode](https://precice.org/tooling-aste.html#replay-mode) has a similar use case and could also feed artificial or previously recorded real data, replacing an actual solver. @@ -45,7 +45,7 @@ Solid participant: * DUNE. For more information, have a look at the [experimental DUNE adapter](https://github.com/precice/dune-adapter) and send us your feedback. -* Nutils. The structural model is currently limited to linear elasticity. For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). This Nutils solver requires at least Nutils v8.0. +* Nutils. The structural model is currently limited to linear elasticity. For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). * solids4foam. Like for CalculiX, the geometrically non-linear solver is used by default. For more information, see the [solids4foam documentation](https://solids4foam.github.io/documentation/overview.html) and a [related tutorial](https://www.solids4foam.com/tutorials/more-tutorials/fluid-solid-interaction/flexibleOversetCylinder.html). This case works with solids4foam v2.0, which is compatible with up to OpenFOAM v2012 and OpenFOAM 9 (as well as foam-extend, with which the OpenFOAM-preCICE adapter is not compatible), as well as the OpenFOAM-preCICE adapter v1.2.0 or later. diff --git a/turek-hron-fsi3/README.md b/turek-hron-fsi3/README.md index 726d83cfe..35da47df4 100644 --- a/turek-hron-fsi3/README.md +++ b/turek-hron-fsi3/README.md @@ -28,12 +28,12 @@ preCICE configuration (image generated using the [precice-config-visualizer](htt Fluid participant: * OpenFOAM (pimpleFoam). In case you are using a very old OpenFOAM version, you will need to adjust the solver to `pimpleDyMFoam` in the `Fluid/system/controlDict` file. For more information, have a look at the [OpenFOAM adapter documentation](https://precice.org/adapter-openfoam-overview.html). -* Nutils. For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). This Nutils solver requires at least Nutils v9.0. This case takes significantly longer to run than the OpenFOAM case, see [related issue](https://github.com/precice/tutorials/issues/506). +* Nutils. For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). This case takes significantly longer to run than the OpenFOAM case, see [related issue](https://github.com/precice/tutorials/issues/506). Solid participant: * deal.II. For more information, have a look at the [deal.II adapter documentation](https://precice.org/adapter-dealii-overview.html). This tutorial requires the nonlinear solid solver. Please copy the nonlinear solver executable to the `solid-dealii` folder or make it discoverable at runtime and update the `solid-dealii/run.sh` script. -* Nutils. For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). This Nutils solver requires at least Nutils v9.0. +* Nutils. For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). ## Running the Simulation diff --git a/volume-coupled-flow/README.md b/volume-coupled-flow/README.md index b1cdf8992..bef36f747 100644 --- a/volume-coupled-flow/README.md +++ b/volume-coupled-flow/README.md @@ -33,7 +33,7 @@ Fluid participant: Source participant: -* Nutils. For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). This Nutils solver requires at least Nutils v7.0. +* Nutils. For more information, have a look at the [Nutils adapter documentation](https://precice.org/adapter-nutils.html). ## Running the simulation From c76e4cb36a5f396f5719d6cd6f28d3dcf2ebdf31 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Tue, 26 May 2026 00:05:35 +0200 Subject: [PATCH 06/94] Add reference results for flow-over-heated-plate-two-meshes and improve error message (#794) Co-authored-by: preCICE Tests VM --- .../fluid-openfoam_solid-openfoam.tar.gz | 3 + .../reference_results.metadata | 71 +++++++++++++++++++ tools/tests/reference_versions.yaml | 2 +- tools/tests/systemtests/Systemtest.py | 3 +- 4 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz create mode 100644 flow-over-heated-plate-two-meshes/reference-results/reference_results.metadata diff --git a/flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz b/flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz new file mode 100644 index 000000000..ada37eda9 --- /dev/null +++ b/flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1dc187254ac813e6d85fa172d1c36dac7d6f679e8abb6a24d61776e4eeaa94f4 +size 2194408 diff --git a/flow-over-heated-plate-two-meshes/reference-results/reference_results.metadata b/flow-over-heated-plate-two-meshes/reference-results/reference_results.metadata new file mode 100644 index 000000000..3bc6fb7d7 --- /dev/null +++ b/flow-over-heated-plate-two-meshes/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-25 23:52:12 | 1dc187254ac813e6d85fa172d1c36dac7d6f679e8abb6a24d61776e4eeaa94f4 | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PRECICE_REF | d199bb3 | +| PRECICE_PRESET | production-audit | +| OPENFOAM_EXECUTABLE | openfoam2312 | +| OPENFOAM_ADAPTER_REF | v1.3.1 | +| PYTHON_BINDINGS_REF | v3.2.0 | +| FENICS_ADAPTER_REF | v2.2.0 | +| TUTORIALS_REF | 94c979c | +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| SU2_VERSION | 7.5.1 | +| SU2_ADAPTER_REF | 64d4aff | +| DEALII_ADAPTER_REF | 02c5d18 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| DUMUX_ADAPTER_REF | 0e914bb | +| MICRO_MANAGER_REF | v0.8.0 | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index 55e2b0396..c37dc5e1c 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -4,7 +4,7 @@ OPENFOAM_EXECUTABLE: "openfoam2312" OPENFOAM_ADAPTER_REF: "v1.3.1" PYTHON_BINDINGS_REF: "v3.2.0" FENICS_ADAPTER_REF: "v2.2.0" -TUTORIALS_REF: "b18b3ef" # May 10, 2026, https://github.com/precice/tutorials/pull/769 +TUTORIALS_REF: "94c979c" # May 25, 2026 PLATFORM: "ubuntu_2404" CALCULIX_VERSION: "2.20" CALCULIX_ADAPTER_REF: "v2.20.1" diff --git a/tools/tests/systemtests/Systemtest.py b/tools/tests/systemtests/Systemtest.py index 19d48ae7f..f438eac03 100644 --- a/tools/tests/systemtests/Systemtest.py +++ b/tools/tests/systemtests/Systemtest.py @@ -282,7 +282,8 @@ def _fetch_ref(self, repository: Path, ref: str): raise RuntimeError(f"git command returned code {result.returncode}") except Exception as e: - raise RuntimeError(f"An error occurred while fetching origin '{ref}': {e}") + raise RuntimeError( + f"An error occurred while fetching origin '{ref}': {e}. Do the values in reference_versions.yaml point to (still) valid Git refs?") def _checkout_ref_in_subfolder(self, repository: Path, subfolder: Path, ref: str): try: From 6916903a81c63272e8fefbbe283f3c7053fc387c Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Tue, 26 May 2026 00:35:51 +0200 Subject: [PATCH 07/94] Fix warnings/error messages related to cleaning scripts (#795) --- perpendicular-flap/fluid-fake/clean.sh | 8 ++++++++ perpendicular-flap/fluid-fake/run.sh | 5 +++++ perpendicular-flap/solid-fake/clean.sh | 8 ++++++++ perpendicular-flap/solid-fake/run.sh | 5 +++++ tools/cleaning-tools.sh | 4 ++-- 5 files changed, 28 insertions(+), 2 deletions(-) create mode 100755 perpendicular-flap/fluid-fake/clean.sh create mode 100755 perpendicular-flap/solid-fake/clean.sh diff --git a/perpendicular-flap/fluid-fake/clean.sh b/perpendicular-flap/fluid-fake/clean.sh new file mode 100755 index 000000000..8d5d37784 --- /dev/null +++ b/perpendicular-flap/fluid-fake/clean.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env sh +set -e -u + +. ../../tools/cleaning-tools.sh + +rm -rvf ./output/*.vtk +clean_precice_logs . +clean_case_logs . diff --git a/perpendicular-flap/fluid-fake/run.sh b/perpendicular-flap/fluid-fake/run.sh index cae9e3b83..57ae6f24f 100755 --- a/perpendicular-flap/fluid-fake/run.sh +++ b/perpendicular-flap/fluid-fake/run.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash set -e -u +. ../../tools/log.sh +exec > >(tee --append "$LOGFILE") 2>&1 + if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then python3 -m venv .venv @@ -8,3 +11,5 @@ then pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 fake.py + +close_log \ No newline at end of file diff --git a/perpendicular-flap/solid-fake/clean.sh b/perpendicular-flap/solid-fake/clean.sh new file mode 100755 index 000000000..8d5d37784 --- /dev/null +++ b/perpendicular-flap/solid-fake/clean.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env sh +set -e -u + +. ../../tools/cleaning-tools.sh + +rm -rvf ./output/*.vtk +clean_precice_logs . +clean_case_logs . diff --git a/perpendicular-flap/solid-fake/run.sh b/perpendicular-flap/solid-fake/run.sh index cae9e3b83..3bd2c8542 100755 --- a/perpendicular-flap/solid-fake/run.sh +++ b/perpendicular-flap/solid-fake/run.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash set -e -u +. ../../tools/log.sh +exec > >(tee --append "$LOGFILE") 2>&1 + if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then python3 -m venv .venv @@ -8,3 +11,5 @@ then pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 fake.py + +close_log diff --git a/tools/cleaning-tools.sh b/tools/cleaning-tools.sh index a1d029f4a..450ba407b 100755 --- a/tools/cleaning-tools.sh +++ b/tools/cleaning-tools.sh @@ -18,7 +18,7 @@ clean_tutorial() { fi for case in */; do - if [ "${case}" = images/ ] || [ "${case}" = reference-results/ ]; then + if [ "${case}" = images/ ] || [ "${case}" = reference-results/ ] || [ "${case}" = dumux/ ]; then continue fi case "${case}" in solver*) @@ -149,7 +149,7 @@ clean_openfoam() { if [ -n "${WM_PROJECT:-}" ] || error "No OpenFOAM environment is active."; then # shellcheck disable=SC1090 # This is an OpenFOAM file which we don't need to check . "${WM_PROJECT_DIR}/bin/tools/CleanFunctions" - cleanCase > /dev/null + region="*" cleanCase > /dev/null rm -rfv 0/uniform/functionObjects/functionObjectProperties history fi clean_precice_logs . From a615f745ca3dcd93865bc4389d6609394421dbdf Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Tue, 26 May 2026 00:52:46 +0200 Subject: [PATCH 08/94] Upgrade pre-commit hooks --- .pre-commit-config.yaml | 4 ++-- elastic-tube-3d/README.md | 2 +- flow-over-heated-plate-partitioned-flow/README.md | 2 +- quickstart/README.md | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index de5022122..07afb69b3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ repos: hooks: - id: format-precice-config - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.30.0 + rev: v0.48.0 hooks: - id: markdownlint exclude: ^(.github/pull_request_template.md|changelog-entries) @@ -22,7 +22,7 @@ repos: - id: clang-format exclude: '\.(json|m|mm)$' - repo: https://github.com/koalaman/shellcheck-precommit - rev: v0.10.0 + rev: v0.11.0 hooks: - id: shellcheck args: [ --external-sources, --exclude=SC1091 ] diff --git a/elastic-tube-3d/README.md b/elastic-tube-3d/README.md index 4adac850a..3d450b48d 100644 --- a/elastic-tube-3d/README.md +++ b/elastic-tube-3d/README.md @@ -31,7 +31,7 @@ Solid participant: * CalculiX. For more information, have a look at the [CalculiX adapter documentation](https://precice.org/adapter-calculix-overview.html). -* FEniCS. The structural model is currently limited to linear elasticity. Currently 3D functionality is experimental in the FEniCS adapter and more details can be found [here](https://github.com/precice/fenics-adapter/pull/133) For more information, have a look at the [FeniCS adapter documentation](https://precice.org/adapter-fenics.html). +* FEniCS. The structural model is currently limited to linear elasticity. Currently 3D functionality is experimental in the FEniCS adapter and more details can be found [in this discussion](https://github.com/precice/fenics-adapter/pull/133). For more information, have a look at the [FeniCS adapter documentation](https://precice.org/adapter-fenics.html). ## Running the simulation diff --git a/flow-over-heated-plate-partitioned-flow/README.md b/flow-over-heated-plate-partitioned-flow/README.md index 7fd84fbfe..2e7b4f47e 100644 --- a/flow-over-heated-plate-partitioned-flow/README.md +++ b/flow-over-heated-plate-partitioned-flow/README.md @@ -14,7 +14,7 @@ Get the [case files of this tutorial](https://github.com/precice/tutorials/tree/ The setup for this tutorial is similar to the [flow over a heated plate](https://precice.org/tutorials-flow-over-heated-plate.html). In this case we additionally partition the OpenFOAM fluid to create a three-way coupling using CHT (conjugate heat transfer) and FF (fluid-fluid coupling). The test case is two-dimensional and uses a serial-implicit coupling with Quasi-Newton acceleration for the fluid-fluid coupling. The CHT coupling between the solid and the fluid2 participant is changed to serial-explicit because it does not make sense numerically to have multiple serial-implicit schemes. -Note that it is usually recommended using the fully implicit parallel `coupling-scheme:multi` with more than two participants. (learn more about this [here](https://precice.org/configuration-coupling-multi.html)). However, in this basic example, it is sufficient to do the CHT coupling explicitly. +Note that it is usually recommended using the fully implicit parallel `coupling-scheme:multi` with more than two participants. (see [multi-coupling configuration](https://precice.org/configuration-coupling-multi.html)). However, in this basic example, it is sufficient to do the CHT coupling explicitly. The flow partitioning is done with the fluid-fluid module of the [preCICE OpenFOAM adapter](https://precice.org/adapter-openfoam-overview.html). Because we use buoyantPimpleFoam we have to tell the adapter that the coupled pressure has the name `p_rgh`. The temperature is coupled at the fluid-solid interface by exchanging *Temperature* and *Heat-Flux*, at the fluid-fluid interface it is *FlowTemperature* and *FlowTemperatureGradient*. Note the difference in naming: *Temperature* is used for conjugate heat transfer and *FlowTemperature* for fluid-fluid coupling. diff --git a/quickstart/README.md b/quickstart/README.md index 491b50637..1d4d418de 100644 --- a/quickstart/README.md +++ b/quickstart/README.md @@ -29,13 +29,13 @@ For this tutorial, we will mainly need to install preCICE, OpenFOAM, and the Ope sudo apt install ./libprecice3_3.4.1_resolute.deb ``` - | OS | Package | - | --- | --- | - | Ubuntu 22.04 Jammy Jellyfish | [`libprecice3_3.4.1_jammy.deb`](https://github.com/precice/precice/releases/download/v3.4.1/libprecice3_3.4.1_jammy.deb) | - | Ubuntu 24.04 Noble Numbat | [`libprecice3_3.4.1_noble.deb`](https://github.com/precice/precice/releases/download/v3.4.1/libprecice3_3.4.1_noble.deb) | + | OS | Package | + | --- | --- | + | Ubuntu 22.04 Jammy Jellyfish | [`libprecice3_3.4.1_jammy.deb`](https://github.com/precice/precice/releases/download/v3.4.1/libprecice3_3.4.1_jammy.deb) | + | Ubuntu 24.04 Noble Numbat | [`libprecice3_3.4.1_noble.deb`](https://github.com/precice/precice/releases/download/v3.4.1/libprecice3_3.4.1_noble.deb) | | Ubuntu 26.04 Resolute Raccoon | [`libprecice3_3.4.1_resolute.deb`](https://github.com/precice/precice/releases/download/v3.4.1/libprecice3_3.4.1_resolute.deb) | - | Debian 13 Trixie | [`libprecice3_3.4.1_trixie.deb`](https://github.com/precice/precice/releases/download/v3.4.1/libprecice3_3.4.1_trixie.deb) | - | Something else | See an [overview of options](https://precice.org/installation-overview.html) | + | Debian 13 Trixie | [`libprecice3_3.4.1_trixie.deb`](https://github.com/precice/precice/releases/download/v3.4.1/libprecice3_3.4.1_trixie.deb) | + | Something else | See an [overview of options](https://precice.org/installation-overview.html) | Facing any problems? [Ask for help](https://precice.org/community-channels.html). 2. We will use OpenFOAM here and in many of our tutorial cases, so [install OpenFOAM](https://precice.org/adapter-openfoam-support.html): From 2df616bda6104387846bb55b145adfe75bd4383c Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Tue, 26 May 2026 10:39:55 +0200 Subject: [PATCH 09/94] Quickstart: Disable foam-extend related options by default (#796) --- .../fluid-openfoam/constant/dynamicMeshDict | 5 ++++- quickstart/fluid-openfoam/system/controlDict | 2 ++ quickstart/fluid-openfoam/system/fvSchemes | 5 ++++- quickstart/fluid-openfoam/system/fvSolution | 9 ++++++--- tools/run-foam-extend.sh | 20 +++++++++++++++++++ 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/quickstart/fluid-openfoam/constant/dynamicMeshDict b/quickstart/fluid-openfoam/constant/dynamicMeshDict index eb04dd676..446748859 100644 --- a/quickstart/fluid-openfoam/constant/dynamicMeshDict +++ b/quickstart/fluid-openfoam/constant/dynamicMeshDict @@ -14,7 +14,10 @@ motionSolverLibs ("libfvMotionSolvers.so"); solver displacementLaplacian; // OpenFOAM9 or newer: rename "solver" to "motionSolver" -diffusivity uniform; // Only relevant to foam-extend +// The following entries are only relevant to foam-extend +/* uncomment +diffusivity uniform; +*/ // foam-extend displacementLaplacianCoeffs { diffusivity quadratic inverseDistance (flap); diff --git a/quickstart/fluid-openfoam/system/controlDict b/quickstart/fluid-openfoam/system/controlDict index 0237a4453..298e9843b 100644 --- a/quickstart/fluid-openfoam/system/controlDict +++ b/quickstart/fluid-openfoam/system/controlDict @@ -55,12 +55,14 @@ functions log true; rhoInf 10; // The following entries are only relevant to foam-extend + /* uncomment functionObjectLibs ( "libforces.so" ); outputControl timeStep; outputInterval 1; pName p; UName U; rhoName rhoInf; + */ // foam-extend CofR (0 0 0); } diff --git a/quickstart/fluid-openfoam/system/fvSchemes b/quickstart/fluid-openfoam/system/fvSchemes index 02b56e6fc..94242e0f7 100644 --- a/quickstart/fluid-openfoam/system/fvSchemes +++ b/quickstart/fluid-openfoam/system/fvSchemes @@ -21,7 +21,10 @@ divSchemes { default none; div(phi,U) Gauss linearUpwind grad(U); - div((nuEff*dev(T(grad(U))))) Gauss linear; // Only relevant to foam-extend + // The following entries are only relevant to foam-extend + /* uncomment + div((nuEff*dev(T(grad(U))))) Gauss linear; + */ // foam-extend div((nuEff*dev2(T(grad(U))))) Gauss linear; } diff --git a/quickstart/fluid-openfoam/system/fvSolution b/quickstart/fluid-openfoam/system/fvSolution index 9db2e2313..7b7785df5 100644 --- a/quickstart/fluid-openfoam/system/fvSolution +++ b/quickstart/fluid-openfoam/system/fvSolution @@ -14,10 +14,12 @@ solvers tolerance 1e-6; relTol 1e-4; smoother DICGaussSeidel; - // The following entries are only relevant to foam-extend + // The following entries are only relevant to foam-extend + /* uncomment agglomerator faceAreaPair; nCellsInCoarsestLevel 10; mergeLevels 1; + */ // foam-extend } pFinal @@ -83,7 +85,8 @@ potentialFlow nNonOrthogonalCorrectors 1; } -// The relaxationFactors and fieldBounds are only relevant to foam-extend +// The following entries are only relevant to foam-extend +/* uncomment relaxationFactors { U 0.7; @@ -95,4 +98,4 @@ fieldBounds p -1e5 1e5; U 100; } - +*/ // foam-extend diff --git a/tools/run-foam-extend.sh b/tools/run-foam-extend.sh index 9e0aa0e95..19484d705 100755 --- a/tools/run-foam-extend.sh +++ b/tools/run-foam-extend.sh @@ -19,8 +19,18 @@ sed -i "s,// application pimpleDyMFoam;,application pimpleDyMFoam;,g" sy sed -i '41i\ \ \ \ "liblduSolvers.so"' system/controlDict sed -i '41i\ \ \ \ "libforces.so"' system/controlDict sed -i "s,writeCompression off,writeCompression uncompressed,g" system/controlDict +sed -i "s,\/\* uncomment,// FOAMEXTENDBEGIN,g" system/controlDict +sed -i "s,\*\/ // foam-extend,// FOAMEXTENDEND,g" system/controlDict + +sed -i "s,\/\* uncomment,// FOAMEXTENDBEGIN,g" system/fvSchemes +sed -i "s,\*\/ // foam-extend,// FOAMEXTENDEND,g" system/fvSchemes + +sed -i "s,\/\* uncomment,// FOAMEXTENDBEGIN,g" system/fvSolution +sed -i "s,\*\/ // foam-extend,// FOAMEXTENDEND,g" system/fvSolution sed -i "s/libfvMotionSolvers\./libfvMotionSolver\./g" constant/dynamicMeshDict +sed -i "s,\/\* uncomment,// FOAMEXTENDBEGIN,g" constant/dynamicMeshDict +sed -i "s,\*\/ // foam-extend,// FOAMEXTENDEND,g" constant/dynamicMeshDict # OpenFOAM run functions: getApplication, getNumberOfProcessors # shellcheck disable=SC1090 # This is an OpenFOAM file which we don't need to check @@ -43,5 +53,15 @@ fi #sed -i '/ "liblduSolvers.so"/d' system/controlDict #sed -i '/ "libforces.so/d' system/controlDict #sed -i "s,writeCompression uncompressed,writeCompression off,g" system/controlDict +#sed -i "s,// FOAMEXTENDBEGIN,\/\* uncomment,g" system/controlDict +#sed -i "s,// FOAMEXTENDEND,\*\/ // foam-extend,g" system/controlDict +# +#sed -i "s,// FOAMEXTENDBEGIN,\/\* uncomment,g" system/fvSchemes +#sed -i "s,// FOAMEXTENDEND,\*\/ // foam-extend,g" system/fvSchemes +# +#sed -i "s,// FOAMEXTENDBEGIN,\/\* uncomment,g" system/fvSolution +#sed -i "s,// FOAMEXTENDEND,\*\/ // foam-extend,g" system/fvSolution # #sed -i "s/libfvMotionSolver\./libfvMotionSolvers\./g" constant/dynamicMeshDict +#sed -i "s,// FOAMEXTENDBEGIN,\/\* uncomment,g" constant/dynamicMeshDict +#sed -i "s,// FOAMEXTENDEND,\*\/ // foam-extend,g" constant/dynamicMeshDict From 90206fa3689567b33ae2c9dd327276559090f35a Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Tue, 26 May 2026 17:26:41 +0200 Subject: [PATCH 10/94] Make logger in precice-config.xml consistent across tutorials (#797) --- elastic-tube-1d/precice-config.xml | 7 +++++-- free-flow-over-porous-media/precice-config.xml | 7 +++---- oscillator-overlap/precice-config.xml | 7 +++++-- oscillator/precice-config.xml | 7 +++++-- two-scale-heat-conduction/precice-config.xml | 5 ++++- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/elastic-tube-1d/precice-config.xml b/elastic-tube-1d/precice-config.xml index d99f72cbd..5bd79cba8 100644 --- a/elastic-tube-1d/precice-config.xml +++ b/elastic-tube-1d/precice-config.xml @@ -1,7 +1,10 @@ - - + + diff --git a/free-flow-over-porous-media/precice-config.xml b/free-flow-over-porous-media/precice-config.xml index d06b2feec..09243353a 100644 --- a/free-flow-over-porous-media/precice-config.xml +++ b/free-flow-over-porous-media/precice-config.xml @@ -2,10 +2,9 @@ + filter="%Severity% > debug and %Rank% = 0" + format="---[precice] %ColorizedSeverity% %Message%" + enabled="true" /> diff --git a/oscillator-overlap/precice-config.xml b/oscillator-overlap/precice-config.xml index a09d28bde..5772202f9 100644 --- a/oscillator-overlap/precice-config.xml +++ b/oscillator-overlap/precice-config.xml @@ -1,7 +1,10 @@ - - + + diff --git a/oscillator/precice-config.xml b/oscillator/precice-config.xml index bfc1ffa92..afb6ac29f 100644 --- a/oscillator/precice-config.xml +++ b/oscillator/precice-config.xml @@ -1,7 +1,10 @@ - - + + diff --git a/two-scale-heat-conduction/precice-config.xml b/two-scale-heat-conduction/precice-config.xml index e1328fda1..3ca95ad2f 100644 --- a/two-scale-heat-conduction/precice-config.xml +++ b/two-scale-heat-conduction/precice-config.xml @@ -1,7 +1,10 @@ - + From 6044bb61ddfa191db9e27c4f8bcb26dd1c9d5697 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Tue, 26 May 2026 23:53:53 +0200 Subject: [PATCH 11/94] Add flow-over-heated-plate-nearest-projection test case to the release_test (#798) Co-authored-by: preCICE Tests VM --- .../fluid-openfoam_solid-openfoam.tar.gz | 4 +-- .../reference_results.metadata | 6 ++--- tools/tests/reference_versions.yaml | 2 +- tools/tests/tests.yaml | 26 ++++++++----------- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz b/flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz index 4a7f74021..14309ed10 100644 --- a/flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz +++ b/flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a61f981eb713f32f0b47435512857e346e5a9aac01794c815dc2531b2ca32e6 -size 1831520 +oid sha256:2a07a451fdaa93f02abd78186ad36cbd8b9db63b13633e07e6eb3d4f8487849d +size 1832020 diff --git a/flow-over-heated-plate-nearest-projection/reference-results/reference_results.metadata b/flow-over-heated-plate-nearest-projection/reference-results/reference_results.metadata index 7a1a921c8..e0e06ff60 100644 --- a/flow-over-heated-plate-nearest-projection/reference-results/reference_results.metadata +++ b/flow-over-heated-plate-nearest-projection/reference-results/reference_results.metadata @@ -11,7 +11,7 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-11 07:45:27 | 1a61f981eb713f32f0b47435512857e346e5a9aac01794c815dc2531b2ca32e6 | +| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-26 23:33:13 | 2a07a451fdaa93f02abd78186ad36cbd8b9db63b13633e07e6eb3d4f8487849d | ## List of arguments used to generate the files @@ -23,7 +23,7 @@ We also include some information on the machine used to generate them | OPENFOAM_ADAPTER_REF | v1.3.1 | | PYTHON_BINDINGS_REF | v3.2.0 | | FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | b18b3ef | +| TUTORIALS_REF | c725522598fedf6bf691f0292aafb85e97fae73d | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | @@ -40,7 +40,7 @@ We also include some information on the machine used to generate them ### uname -a -Linux precice-tests 5.15.0-177-generic #187-Ubuntu SMP Sat Apr 11 22:54:33 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux ### lscpu diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index c37dc5e1c..94f7bf6ae 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -4,7 +4,7 @@ OPENFOAM_EXECUTABLE: "openfoam2312" OPENFOAM_ADAPTER_REF: "v1.3.1" PYTHON_BINDINGS_REF: "v3.2.0" FENICS_ADAPTER_REF: "v2.2.0" -TUTORIALS_REF: "94c979c" # May 25, 2026 +TUTORIALS_REF: "c725522598fedf6bf691f0292aafb85e97fae73d" # May 26, 2026 PLATFORM: "ubuntu_2404" CALCULIX_VERSION: "2.20" CALCULIX_ADAPTER_REF: "v2.20.1" diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index 07abe7b7e..3fad38dc4 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -1,4 +1,12 @@ test_suites: + flow-over-heated-plate-nearest-projection: + tutorials: + - &flow-over-heated-plate-nearest-projection_fluid-openfoam_solid-openfoam + path: flow-over-heated-plate-nearest-projection + case_combination: + - fluid-openfoam + - solid-openfoam + reference_result: ./flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz flow-over-heated-plate-two-meshes: tutorials: - &flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix @@ -34,11 +42,7 @@ test_suites: - fluid-openfoam - solid-openfoam reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz - - path: flow-over-heated-plate-nearest-projection - case_combination: - - fluid-openfoam - - solid-openfoam - reference_result: ./flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz + - *flow-over-heated-plate-nearest-projection_fluid-openfoam_solid-openfoam - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix openfoam_adapter_release: tutorials: @@ -57,11 +61,7 @@ test_suites: - fluid-openfoam - solid-openfoam reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz - - path: flow-over-heated-plate-nearest-projection - case_combination: - - fluid-openfoam - - solid-openfoam - reference_result: ./flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz + - *flow-over-heated-plate-nearest-projection_fluid-openfoam_solid-openfoam - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix fenics_test: tutorials: @@ -142,11 +142,6 @@ test_suites: - fluid-openfoam - solid-fenics reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz - - path: flow-over-heated-plate-nearest-projection - case_combination: - - fluid-openfoam - - solid-openfoam - reference_result: ./flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz - path: multiple-perpendicular-flaps case_combination: - fluid-openfoam @@ -203,6 +198,7 @@ test_suites: - fluid-openfoam - solid-openfoam reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz + - *flow-over-heated-plate-nearest-projection_fluid-openfoam_solid-openfoam - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix - path: perpendicular-flap case_combination: From f19be4d0578af9ae2b6202e30742f9f1f7f40ed9 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Wed, 27 May 2026 11:19:14 +0200 Subject: [PATCH 12/94] System tests: Restructure and cleanup the tests.yaml (#799) --- changelog-entries/799.md | 1 + tools/tests/README.md | 19 +-- tools/tests/tests.yaml | 344 +++++++++++++++++++-------------------- 3 files changed, 177 insertions(+), 187 deletions(-) create mode 100644 changelog-entries/799.md diff --git a/changelog-entries/799.md b/changelog-entries/799.md new file mode 100644 index 000000000..12874ffc3 --- /dev/null +++ b/changelog-entries/799.md @@ -0,0 +1 @@ +- Restructured the list of system tests. Repositories triggering a test suite besides the `release_test` need to be updated. [#799](https://github.com/precice/tutorials/pull/799) \ No newline at end of file diff --git a/tools/tests/README.md b/tools/tests/README.md index bd935965b..ba4891f6b 100644 --- a/tools/tests/README.md +++ b/tools/tests/README.md @@ -45,7 +45,7 @@ Workflow for the preCICE v3 release testing: To test a certain test-suite defined in `tests.yaml`, use: ```bash -python3 systemtests.py --suites=fenics_test, +python3 systemtests.py --suites=fenics-adapter, ``` To discover all tests, use `python print_test_suites.py`. @@ -59,13 +59,13 @@ Go to Actions > [Run Testsuite (manual)](https://github.com/precice/tutorials/ac After bringing these changes to `master`, the manual triggering option should be visible on the top right. Until that happens, we can only trigger this workflow manually from the [GitHub CLI](https://github.blog/changelog/2021-04-15-github-cli-1-9-enables-you-to-work-with-github-actions-from-your-terminal/): ```shell -gh workflow run run_testsuite_manual.yml -f suites=fenics_test --ref=develop +gh workflow run run_testsuite_manual.yml -f suites=fenics-adapter --ref=develop ``` Another example, to use the latest releases and enable debug information of the tests: ```shell -gh workflow run run_testsuite_manual.yml -f suites=fenics_test -f build_args="PRECICE_REF:v3.1.1,PRECICE_PRESET:production-audit,OPENFOAM_ADAPTER_REF:v1.3.0,PYTHON_BINDINGS_REF:v3.1.0,FENICS_ADAPTER_REF:v2.1.0,SU2_VERSION:7.5.1,SU2_ADAPTER_REF:64d4aff,DEALII_ADAPTER_REF:02c5d18,TUTORIALS_REF:340b447" -f log_level=DEBUG --ref=develop +gh workflow run run_testsuite_manual.yml -f suites=fenics-adapter -f build_args="PRECICE_REF:v3.1.1,PRECICE_PRESET:production-audit,OPENFOAM_ADAPTER_REF:v1.3.0,PYTHON_BINDINGS_REF:v3.1.0,FENICS_ADAPTER_REF:v2.1.0,SU2_VERSION:7.5.1,SU2_ADAPTER_REF:64d4aff,DEALII_ADAPTER_REF:02c5d18,TUTORIALS_REF:340b447" -f log_level=DEBUG --ref=develop ``` where the `*_REF` should be a specific [commit-ish](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefcommit-ishacommit-ishalsocommittish). @@ -75,7 +75,7 @@ Example output: ```text Run cd tools/tests cd tools/tests - python systemtests.py --build_args=PRECICE_REF:v3.1.1,PRECICE_PRESET:production-audit,OPENFOAM_ADAPTER_REF:v1.3.0,PYTHON_BINDINGS_REF:v3.1.0,FENICS_ADAPTER_REF:v2.1.0 --suites=fenics_test --log-level=DEBUG + python systemtests.py --build_args=PRECICE_REF:v3.1.1,PRECICE_PRESET:production-audit,OPENFOAM_ADAPTER_REF:v1.3.0,PYTHON_BINDINGS_REF:v3.1.0,FENICS_ADAPTER_REF:v2.1.0 --suites=fenics-adapter --log-level=DEBUG cd ../../ shell: /usr/bin/bash -e {0} INFO: About to run the following systemtest in the directory /home/precice/runners_root/actions-runner-tutorial/_work/tutorials/tutorials/runs: @@ -316,14 +316,7 @@ Concrete tests are specified centrally in the file `tests.yaml`. For example: ```yaml test_suites: - openfoam_adapter_pr: - tutorials: - - path: flow-over-heated-plate - case_combination: - - fluid-openfoam - - solid-openfoam - reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz - openfoam_adapter_release: + openfoam-adapter: tutorials: - path: flow-over-heated-plate case_combination: @@ -340,7 +333,7 @@ test_suites: The optional `timeout` field (in seconds) sets the maximum time for the solver run and fieldcompare phases of that specific case. If omitted, it defaults to `GLOBAL_TIMEOUT` (currently 900s, overridable via the `PRECICE_SYSTEMTESTS_TIMEOUT` environment variable). -This defines two test suites, namely `openfoam_adapter_pr` and `openfoam_adapter_release`. Each of them defines which case combinations of which tutorials to run. +This defines the test suite `openfoam-adapter`, with a case combination to run. ### Generate Reference Results diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index 3fad38dc4..2ff8564eb 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -1,229 +1,225 @@ -test_suites: - flow-over-heated-plate-nearest-projection: - tutorials: - - &flow-over-heated-plate-nearest-projection_fluid-openfoam_solid-openfoam - path: flow-over-heated-plate-nearest-projection - case_combination: - - fluid-openfoam - - solid-openfoam - reference_result: ./flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz - flow-over-heated-plate-two-meshes: - tutorials: - - &flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix - path: flow-over-heated-plate-two-meshes - case_combination: - - fluid-openfoam - - solid-calculix - reference_result: ./flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz +# Test suites that can be triggered by the system tests. +# Define test suites per tutorial in the beginning of the file, +# then refer to these in the release_test and in the individual test +# suites for repositories, at the lower part of the file. +# +# Conventions: +# - Test suites follow the name of the respective tutorial or repository +# - Tests suites append the entries of the case_combination, separated by underscore +# - Test suites and tests are sorted alphabetically +# - Every test suite for a component includes by default all test cases that use that component. +# Skipped components are still mentioned in a comment. - quickstart_test: +test_suites: + elastic-tube-1d: tutorials: - - &quickstart - path: quickstart + - &elastic-tube-1d_fluid-cpp_solid-cpp + path: elastic-tube-1d case_combination: - - fluid-openfoam + - fluid-cpp - solid-cpp - reference_result: ./quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz - openfoam_adapter_pr: - tutorials: - - *quickstart - - path: flow-over-heated-plate - case_combination: - - fluid-openfoam - - solid-openfoam - reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz - - path: perpendicular-flap - case_combination: - - fluid-openfoam - - solid-calculix - reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz - - path: perpendicular-flap - case_combination: - - fluid-openfoam - - solid-openfoam - reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz - - *flow-over-heated-plate-nearest-projection_fluid-openfoam_solid-openfoam - - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix - openfoam_adapter_release: - tutorials: - - path: flow-over-heated-plate - case_combination: - - fluid-openfoam - - solid-openfoam - reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz - - path: perpendicular-flap + reference_result: ./elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz + - &elastic-tube-1d_fluid-cpp_solid-python + path: elastic-tube-1d case_combination: - - fluid-openfoam - - solid-calculix - reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz - - path: perpendicular-flap + - fluid-cpp + - solid-python + reference_result: ./elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz + - &elastic-tube-1d_fluid-python_solid-python + path: elastic-tube-1d case_combination: - - fluid-openfoam - - solid-openfoam - reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz - - *flow-over-heated-plate-nearest-projection_fluid-openfoam_solid-openfoam - - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix - fenics_test: + - fluid-python + - solid-python + reference_result: ./elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz + + flow-over-heated-plate: tutorials: - - path: flow-over-heated-plate + - &flow-over-heated-plate_fluid-openfoam_solid-fenics + path: flow-over-heated-plate case_combination: - fluid-openfoam - solid-fenics reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz - - path: perpendicular-flap + - &flow-over-heated-plate_fluid-openfoam_solid-nutils + path: flow-over-heated-plate case_combination: - fluid-openfoam - - solid-fenics - reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz - nutils_test: + - solid-nutils + reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz + - &flow-over-heated-plate_fluid-openfoam_solid-openfoam + path: flow-over-heated-plate + case_combination: + - fluid-openfoam + - solid-openfoam + reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz + + flow-over-heated-plate-nearest-projection: tutorials: - - path: flow-over-heated-plate + - &flow-over-heated-plate-nearest-projection_fluid-openfoam_solid-openfoam + path: flow-over-heated-plate-nearest-projection case_combination: - fluid-openfoam - - solid-nutils - reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz - calculix_test: + - solid-openfoam + reference_result: ./flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz + + flow-over-heated-plate-two-meshes: tutorials: - - path: perpendicular-flap + - &flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix + path: flow-over-heated-plate-two-meshes case_combination: - fluid-openfoam - solid-calculix - reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz - - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix - dumux_test: + reference_result: ./flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz + + free-flow-over-porous-media: tutorials: - - path: two-scale-heat-conduction - case_combination: - - macro-dumux - - micro-dumux - reference_result: ./two-scale-heat-conduction/reference-results/macro-dumux_micro-dumux.tar.gz # Too small values, expected to fail the comparisons. - - path: free-flow-over-porous-media + - &free-flow-over-porous-media_free-flow-dumux_porous-media-dumux + path: free-flow-over-porous-media case_combination: - free-flow-dumux - porous-media-dumux reference_result: ./free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz - micro_manager_test: - tutorials: - - path: two-scale-heat-conduction - case_combination: - - macro-dumux - - micro-dumux - reference_result: ./two-scale-heat-conduction/reference-results/macro-dumux_micro-dumux.tar.gz # Too small values, expected to fail the comparisons. - su2_test: - tutorials: - - path: perpendicular-flap - case_combination: - - fluid-su2 - - solid-fenics - reference_result: ./perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz - heat_exchanger_simplified_test: + + heat-exchanger-simplified: tutorials: - - &heat_exchanger_simplified + - &heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix path: heat-exchanger-simplified case_combination: - fluid-top-openfoam - fluid-bottom-openfoam - solid-calculix reference_result: ./heat-exchanger-simplified/reference-results/fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz - dealii_test: + + multiple-perpendicular-flaps: tutorials: - - path: perpendicular-flap - case_combination: - - fluid-openfoam - - solid-dealii - reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-dealii.tar.gz - - path: perpendicular-flap - case_combination: - - fluid-openfoam - - solid-openfoam - reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz - - path: perpendicular-flap - case_combination: - - fluid-openfoam - - solid-fenics - reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz - - path: multiple-perpendicular-flaps + - &multiple-perpendicular-flaps_fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii + path: multiple-perpendicular-flaps case_combination: - fluid-openfoam - solid-upstream-dealii - solid-downstream-dealii reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz - elastic_tube_1d_test: - tutorials: - - path: elastic-tube-1d - case_combination: - - fluid-cpp - - solid-cpp - reference_result: ./elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz - - path: elastic-tube-1d - case_combination: - - fluid-python - - solid-python - reference_result: ./elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz - - path: elastic-tube-1d - case_combination: - - fluid-cpp - - solid-python - reference_result: ./elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz - release_test: + + perpendicular-flap: tutorials: - - *quickstart - - path: elastic-tube-1d + - &perpendicular-flap_fluid-openfoam_solid-calculix + path: perpendicular-flap case_combination: - - fluid-cpp - - solid-cpp - reference_result: ./elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz - - path: elastic-tube-1d - case_combination: - - fluid-python - - solid-python - reference_result: ./elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz - - path: elastic-tube-1d - case_combination: - - fluid-cpp - - solid-python - reference_result: ./elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz - - path: flow-over-heated-plate + - fluid-openfoam + - solid-calculix + reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz + - &perpendicular-flap_fluid-openfoam_solid-dealii + path: perpendicular-flap case_combination: - fluid-openfoam - - solid-nutils - reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz - - path: flow-over-heated-plate + - solid-dealii + reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-dealii.tar.gz + - &perpendicular-flap_fluid-openfoam_solid-fenics + path: perpendicular-flap case_combination: - fluid-openfoam - solid-fenics - reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz - - path: flow-over-heated-plate + reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz + - &perpendicular-flap_fluid-openfoam_solid-openfoam + path: perpendicular-flap case_combination: - fluid-openfoam - solid-openfoam - reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz - - *flow-over-heated-plate-nearest-projection_fluid-openfoam_solid-openfoam - - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix - - path: perpendicular-flap - case_combination: - - fluid-openfoam - - solid-calculix - reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz - - path: perpendicular-flap + reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz + - &perpendicular-flap_fluid-su2_solid-fenics + path: perpendicular-flap case_combination: - fluid-su2 - solid-fenics reference_result: ./perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz - - path: perpendicular-flap + + two-scale-heat-conduction: + tutorials: + - &two-scale-heat-conduction_macro-dumux_micro-dumux + path: two-scale-heat-conduction case_combination: - - fluid-openfoam - - solid-dealii - reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-dealii.tar.gz - - path: multiple-perpendicular-flaps + - macro-dumux + - micro-dumux + reference_result: ./two-scale-heat-conduction/reference-results/macro-dumux_micro-dumux.tar.gz # Too small values, expected to fail the comparisons. + + quickstart: + tutorials: + - &quickstart_openfoam_cpp + path: quickstart case_combination: - fluid-openfoam - - solid-upstream-dealii - - solid-downstream-dealii - reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz - - *heat_exchanger_simplified - - path: free-flow-over-porous-media - case_combination: - - free-flow-dumux - - porous-media-dumux - reference_result: ./free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz + - solid-cpp + reference_result: ./quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz + +##################################################################### +## Test suites referring to the test suites defined above + + release_test: + tutorials: + - *elastic-tube-1d_fluid-cpp_solid-cpp + - *elastic-tube-1d_fluid-cpp_solid-python + - *elastic-tube-1d_fluid-python_solid-python + - *flow-over-heated-plate_fluid-openfoam_solid-fenics + - *flow-over-heated-plate_fluid-openfoam_solid-nutils + - *flow-over-heated-plate_fluid-openfoam_solid-openfoam + - *flow-over-heated-plate-nearest-projection_fluid-openfoam_solid-openfoam + - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix + - *free-flow-over-porous-media_free-flow-dumux_porous-media-dumux + - *heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix + - *multiple-perpendicular-flaps_fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii + - *perpendicular-flap_fluid-openfoam_solid-calculix + - *perpendicular-flap_fluid-openfoam_solid-dealii + - *perpendicular-flap_fluid-openfoam_solid-fenics + - *perpendicular-flap_fluid-openfoam_solid-openfoam + - *perpendicular-flap_fluid-su2_solid-fenics + # two-scale-heat-conduction_macro-dumux_micro-dumux excluded + - *quickstart_openfoam_cpp + + calculix-adapter: + tutorials: + - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix + - *heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix + - *perpendicular-flap_fluid-openfoam_solid-calculix + + dealii-adapter: + tutorials: + - *multiple-perpendicular-flaps_fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii + - *perpendicular-flap_fluid-openfoam_solid-dealii + + dumux-adapter: + tutorials: + - *free-flow-over-porous-media_free-flow-dumux_porous-media-dumux + - *two-scale-heat-conduction_macro-dumux_micro-dumux + + fenics-adapter: + tutorials: + - *flow-over-heated-plate_fluid-openfoam_solid-fenics + - *perpendicular-flap_fluid-openfoam_solid-fenics + - *perpendicular-flap_fluid-su2_solid-fenics + + micro-manager: + tutorials: + - *two-scale-heat-conduction_macro-dumux_micro-dumux + + nutils-adapter: # Not a repository + tutorials: + - *flow-over-heated-plate_fluid-openfoam_solid-nutils + + openfoam-adapter: + tutorials: + - *flow-over-heated-plate_fluid-openfoam_solid-fenics + - *flow-over-heated-plate_fluid-openfoam_solid-nutils + - *flow-over-heated-plate_fluid-openfoam_solid-openfoam + - *flow-over-heated-plate-nearest-projection_fluid-openfoam_solid-openfoam + - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix + - *heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix + - *multiple-perpendicular-flaps_fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii + - *perpendicular-flap_fluid-openfoam_solid-calculix + - *perpendicular-flap_fluid-openfoam_solid-dealii + - *perpendicular-flap_fluid-openfoam_solid-fenics + - *perpendicular-flap_fluid-openfoam_solid-openfoam + - *quickstart_openfoam_cpp + + su2-adapter: + tutorials: + - *perpendicular-flap_fluid-su2_solid-fenics From 90202a2f5b3637d41a5ad66d2206a0779921a876 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Wed, 27 May 2026 18:35:43 +0200 Subject: [PATCH 13/94] Switch system tests result from VTK to VTU (#802) --- changelog-entries/802.md | 1 + .../reference-results/fluid-cpp_solid-cpp.tar.gz | 4 ++-- .../fluid-cpp_solid-python.tar.gz | 4 ++-- .../fluid-python_solid-python.tar.gz | 4 ++-- .../reference-results/reference_results.metadata | 10 +++++----- .../fluid-openfoam_solid-openfoam.tar.gz | 4 ++-- .../reference-results/reference_results.metadata | 4 ++-- .../fluid-openfoam_solid-openfoam.tar.gz | 4 ++-- .../reference-results/reference_results.metadata | 4 ++-- .../fluid-openfoam_solid-fenics.tar.gz | 4 ++-- .../fluid-openfoam_solid-nutils.tar.gz | 4 ++-- .../fluid-openfoam_solid-openfoam.tar.gz | 4 ++-- .../reference-results/reference_results.metadata | 10 +++++----- .../free-flow-dumux_porous-media-dumux.tar.gz | 4 ++-- .../reference-results/reference_results.metadata | 6 +++--- ...oam_fluid-bottom-openfoam_solid-calculix.tar.gz | 4 ++-- .../reference-results/reference_results.metadata | 6 +++--- .../reference-results/reference_results.metadata | 6 +++--- .../fluid-openfoam_solid-calculix.tar.gz | 4 ++-- .../fluid-openfoam_solid-dealii.tar.gz | 4 ++-- .../fluid-openfoam_solid-fenics.tar.gz | 4 ++-- .../fluid-openfoam_solid-openfoam.tar.gz | 4 ++-- ...-upstream-dealii_solid-downstream-dealii.tar.gz | 4 ++-- .../fluid-su2_solid-fenics.tar.gz | 4 ++-- .../reference-results/reference_results.metadata | 14 +++++++------- .../fluid-openfoam_solid-cpp.tar.gz | 4 ++-- .../reference-results/reference_results.metadata | 6 +++--- tools/tests/README.md | 2 +- tools/tests/docker-compose.template.yaml | 2 +- tools/tests/reference_versions.yaml | 2 +- tools/tests/tests.yaml | 5 +++++ 31 files changed, 76 insertions(+), 70 deletions(-) create mode 100644 changelog-entries/802.md diff --git a/changelog-entries/802.md b/changelog-entries/802.md new file mode 100644 index 000000000..c9a8badcb --- /dev/null +++ b/changelog-entries/802.md @@ -0,0 +1 @@ +- Changed the system tests reference results format from VTK to VTU [#802](https://github.com/precice/tutorials/pull/802) \ No newline at end of file diff --git a/elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz b/elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz index d5adab288..16077f214 100644 --- a/elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz +++ b/elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:329e96749e9f573cffe116405ef2e5c2dbc1c1fc1328c588ea6546d70a902428 -size 706930 +oid sha256:1d885192309f3ddfd72630f0ee2bf1c23e6eb5682573355046c3c48e6feb22bf +size 250515 diff --git a/elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz b/elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz index b026f51ec..2e94cd824 100644 --- a/elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz +++ b/elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:92f9751843f55dd2ffa4c82d86193eecf40aebb0987ccaf7b02f2263a156f14c -size 707211 +oid sha256:7bb0f41035f24ab10689038f6f8c58ace7df4b953f29c35879452afbabe499ec +size 250667 diff --git a/elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz b/elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz index d752c1f9b..e6ea59d82 100644 --- a/elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz +++ b/elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff7e3e50b59d6bc6398c75678181bc12cccc0e1397fdba901503e1f1897dec4a -size 707288 +oid sha256:0a3a0532436ced9495b7a26e3b5b2a3cae589abe3928277586bc6495402d0654 +size 250733 diff --git a/elastic-tube-1d/reference-results/reference_results.metadata b/elastic-tube-1d/reference-results/reference_results.metadata index cf0fc5b06..367dce573 100644 --- a/elastic-tube-1d/reference-results/reference_results.metadata +++ b/elastic-tube-1d/reference-results/reference_results.metadata @@ -11,9 +11,9 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-cpp_solid-cpp.tar.gz | 2026-05-11 07:45:27 | 329e96749e9f573cffe116405ef2e5c2dbc1c1fc1328c588ea6546d70a902428 | -| fluid-cpp_solid-python.tar.gz | 2026-05-11 07:45:27 | 92f9751843f55dd2ffa4c82d86193eecf40aebb0987ccaf7b02f2263a156f14c | -| fluid-python_solid-python.tar.gz | 2026-05-11 07:45:27 | ff7e3e50b59d6bc6398c75678181bc12cccc0e1397fdba901503e1f1897dec4a | +| fluid-cpp_solid-cpp.tar.gz | 2026-05-27 16:22:51 | 1d885192309f3ddfd72630f0ee2bf1c23e6eb5682573355046c3c48e6feb22bf | +| fluid-cpp_solid-python.tar.gz | 2026-05-27 16:22:51 | 7bb0f41035f24ab10689038f6f8c58ace7df4b953f29c35879452afbabe499ec | +| fluid-python_solid-python.tar.gz | 2026-05-27 16:22:51 | 0a3a0532436ced9495b7a26e3b5b2a3cae589abe3928277586bc6495402d0654 | ## List of arguments used to generate the files @@ -25,7 +25,7 @@ We also include some information on the machine used to generate them | OPENFOAM_ADAPTER_REF | v1.3.1 | | PYTHON_BINDINGS_REF | v3.2.0 | | FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | b18b3ef | +| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | @@ -42,7 +42,7 @@ We also include some information on the machine used to generate them ### uname -a -Linux precice-tests 5.15.0-177-generic #187-Ubuntu SMP Sat Apr 11 22:54:33 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux ### lscpu diff --git a/flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz b/flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz index 14309ed10..37bf7e136 100644 --- a/flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz +++ b/flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a07a451fdaa93f02abd78186ad36cbd8b9db63b13633e07e6eb3d4f8487849d -size 1832020 +oid sha256:45b44d1a508dddb1cea4bdfacca4fc4c37adbcac0dfef30b2a8909a2e24eeeb9 +size 1374041 diff --git a/flow-over-heated-plate-nearest-projection/reference-results/reference_results.metadata b/flow-over-heated-plate-nearest-projection/reference-results/reference_results.metadata index e0e06ff60..58764a010 100644 --- a/flow-over-heated-plate-nearest-projection/reference-results/reference_results.metadata +++ b/flow-over-heated-plate-nearest-projection/reference-results/reference_results.metadata @@ -11,7 +11,7 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-26 23:33:13 | 2a07a451fdaa93f02abd78186ad36cbd8b9db63b13633e07e6eb3d4f8487849d | +| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-27 16:22:51 | 45b44d1a508dddb1cea4bdfacca4fc4c37adbcac0dfef30b2a8909a2e24eeeb9 | ## List of arguments used to generate the files @@ -23,7 +23,7 @@ We also include some information on the machine used to generate them | OPENFOAM_ADAPTER_REF | v1.3.1 | | PYTHON_BINDINGS_REF | v3.2.0 | | FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | c725522598fedf6bf691f0292aafb85e97fae73d | +| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | diff --git a/flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz b/flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz index ada37eda9..4d455551e 100644 --- a/flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz +++ b/flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1dc187254ac813e6d85fa172d1c36dac7d6f679e8abb6a24d61776e4eeaa94f4 -size 2194408 +oid sha256:965cdf279468c619e87df7c07986d97e8746c5d56f4690e3f77356e567b0bc8f +size 468654 diff --git a/flow-over-heated-plate-two-meshes/reference-results/reference_results.metadata b/flow-over-heated-plate-two-meshes/reference-results/reference_results.metadata index 3bc6fb7d7..d47f1589f 100644 --- a/flow-over-heated-plate-two-meshes/reference-results/reference_results.metadata +++ b/flow-over-heated-plate-two-meshes/reference-results/reference_results.metadata @@ -11,7 +11,7 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-25 23:52:12 | 1dc187254ac813e6d85fa172d1c36dac7d6f679e8abb6a24d61776e4eeaa94f4 | +| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-27 16:22:51 | 965cdf279468c619e87df7c07986d97e8746c5d56f4690e3f77356e567b0bc8f | ## List of arguments used to generate the files @@ -23,7 +23,7 @@ We also include some information on the machine used to generate them | OPENFOAM_ADAPTER_REF | v1.3.1 | | PYTHON_BINDINGS_REF | v3.2.0 | | FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | 94c979c | +| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | diff --git a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz index 2f0da01fe..09326bf39 100644 --- a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz +++ b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:059d338dd1e93b3661b052665fa297a55668e7e81d385d409b12dcbbe0ac74ed -size 771178 +oid sha256:428d220b36ae6c42a8c85ff321e0f7c2266ce3c7454f8bdd90b17e80504e8634 +size 214096 diff --git a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz index c8484c169..5e4b4295c 100644 --- a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz +++ b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:96d2748fcc27be2b8977b30192965ae5a736c679e3a2f26c2f6713348c2bc2f4 -size 532749 +oid sha256:8297a45ebb39d68aace1fc2ed6d88a93ed6b1a0bb529c8d7be557088c0f85beb +size 146863 diff --git a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz index 9d84f0178..c1c7da21d 100644 --- a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz +++ b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:758a2fb73703b7a693a8b2b2994dab0ff52e89b0b1ce25c8389571f3397cce15 -size 497675 +oid sha256:755ffddc08446524905d6eca45eb092bc65014dbf1547b4b60147dc9ce057856 +size 136231 diff --git a/flow-over-heated-plate/reference-results/reference_results.metadata b/flow-over-heated-plate/reference-results/reference_results.metadata index bff70c8d8..6f39d54ee 100644 --- a/flow-over-heated-plate/reference-results/reference_results.metadata +++ b/flow-over-heated-plate/reference-results/reference_results.metadata @@ -11,9 +11,9 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-11 07:45:27 | 758a2fb73703b7a693a8b2b2994dab0ff52e89b0b1ce25c8389571f3397cce15 | -| fluid-openfoam_solid-fenics.tar.gz | 2026-05-11 07:45:27 | 059d338dd1e93b3661b052665fa297a55668e7e81d385d409b12dcbbe0ac74ed | -| fluid-openfoam_solid-nutils.tar.gz | 2026-05-11 07:45:27 | 96d2748fcc27be2b8977b30192965ae5a736c679e3a2f26c2f6713348c2bc2f4 | +| fluid-openfoam_solid-fenics.tar.gz | 2026-05-27 16:22:51 | 428d220b36ae6c42a8c85ff321e0f7c2266ce3c7454f8bdd90b17e80504e8634 | +| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-27 16:22:51 | 755ffddc08446524905d6eca45eb092bc65014dbf1547b4b60147dc9ce057856 | +| fluid-openfoam_solid-nutils.tar.gz | 2026-05-27 16:22:51 | 8297a45ebb39d68aace1fc2ed6d88a93ed6b1a0bb529c8d7be557088c0f85beb | ## List of arguments used to generate the files @@ -25,7 +25,7 @@ We also include some information on the machine used to generate them | OPENFOAM_ADAPTER_REF | v1.3.1 | | PYTHON_BINDINGS_REF | v3.2.0 | | FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | b18b3ef | +| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | @@ -42,7 +42,7 @@ We also include some information on the machine used to generate them ### uname -a -Linux precice-tests 5.15.0-177-generic #187-Ubuntu SMP Sat Apr 11 22:54:33 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux ### lscpu diff --git a/free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz b/free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz index 7fd1bab2f..31cbfc931 100644 --- a/free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz +++ b/free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc2e6a3137af7f16976b0731817170a7bf664255ddb4173e2c4d5c0319487cf4 -size 2288 +oid sha256:b802da5d2e5ed73a35678837a134ee1a0b0f5b09593bfb6477c2d52c10fc290c +size 1588 diff --git a/free-flow-over-porous-media/reference-results/reference_results.metadata b/free-flow-over-porous-media/reference-results/reference_results.metadata index 0426fc1b1..517a6ec55 100644 --- a/free-flow-over-porous-media/reference-results/reference_results.metadata +++ b/free-flow-over-porous-media/reference-results/reference_results.metadata @@ -11,7 +11,7 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| free-flow-dumux_porous-media-dumux.tar.gz | 2026-05-11 07:45:27 | bc2e6a3137af7f16976b0731817170a7bf664255ddb4173e2c4d5c0319487cf4 | +| free-flow-dumux_porous-media-dumux.tar.gz | 2026-05-27 16:22:51 | b802da5d2e5ed73a35678837a134ee1a0b0f5b09593bfb6477c2d52c10fc290c | ## List of arguments used to generate the files @@ -23,7 +23,7 @@ We also include some information on the machine used to generate them | OPENFOAM_ADAPTER_REF | v1.3.1 | | PYTHON_BINDINGS_REF | v3.2.0 | | FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | b18b3ef | +| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | @@ -40,7 +40,7 @@ We also include some information on the machine used to generate them ### uname -a -Linux precice-tests 5.15.0-177-generic #187-Ubuntu SMP Sat Apr 11 22:54:33 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux ### lscpu diff --git a/heat-exchanger-simplified/reference-results/fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz b/heat-exchanger-simplified/reference-results/fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz index ee8822064..3f8ea56c2 100644 --- a/heat-exchanger-simplified/reference-results/fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz +++ b/heat-exchanger-simplified/reference-results/fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e440db6358e8582d12e83742f032e64574a83d72b583d914cf0aaa60c327438 -size 4344032 +oid sha256:9787ee7b41e7f896e6946512f3001beaa512c158726423bc76b47517c913005f +size 863801 diff --git a/heat-exchanger-simplified/reference-results/reference_results.metadata b/heat-exchanger-simplified/reference-results/reference_results.metadata index b51259e49..1c88480ff 100644 --- a/heat-exchanger-simplified/reference-results/reference_results.metadata +++ b/heat-exchanger-simplified/reference-results/reference_results.metadata @@ -11,7 +11,7 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz | 2026-05-11 07:45:27 | 5e440db6358e8582d12e83742f032e64574a83d72b583d914cf0aaa60c327438 | +| fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz | 2026-05-27 16:22:51 | 9787ee7b41e7f896e6946512f3001beaa512c158726423bc76b47517c913005f | ## List of arguments used to generate the files @@ -23,7 +23,7 @@ We also include some information on the machine used to generate them | OPENFOAM_ADAPTER_REF | v1.3.1 | | PYTHON_BINDINGS_REF | v3.2.0 | | FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | b18b3ef | +| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | @@ -40,7 +40,7 @@ We also include some information on the machine used to generate them ### uname -a -Linux precice-tests 5.15.0-177-generic #187-Ubuntu SMP Sat Apr 11 22:54:33 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux ### lscpu diff --git a/multiple-perpendicular-flaps/reference-results/reference_results.metadata b/multiple-perpendicular-flaps/reference-results/reference_results.metadata index c426ac97c..9df445e04 100644 --- a/multiple-perpendicular-flaps/reference-results/reference_results.metadata +++ b/multiple-perpendicular-flaps/reference-results/reference_results.metadata @@ -11,7 +11,7 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz | 2026-05-11 07:45:27 | ea2d5946f64667ba05ab3a3516cca7c3af3a59374ff22d0a69e0098c00c1ace1 | +| fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz | 2026-05-27 16:22:51 | d4bfc7abd3e14ae860d676d426acefc2d998bcbbb2ce79a189cadfc2aa7d3a65 | ## List of arguments used to generate the files @@ -23,7 +23,7 @@ We also include some information on the machine used to generate them | OPENFOAM_ADAPTER_REF | v1.3.1 | | PYTHON_BINDINGS_REF | v3.2.0 | | FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | b18b3ef | +| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | @@ -40,7 +40,7 @@ We also include some information on the machine used to generate them ### uname -a -Linux precice-tests 5.15.0-177-generic #187-Ubuntu SMP Sat Apr 11 22:54:33 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux ### lscpu diff --git a/perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz b/perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz index 7f7181740..db6565804 100644 --- a/perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz +++ b/perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fbb2c688033023cda1ff26f7c97bb123931014d66620b572c930e4b8e0bcf7f -size 13543772 +oid sha256:993ac60ae095bfc5e1e7ce8157c12c6d20093800eac91871b078cda83272e02b +size 4767902 diff --git a/perpendicular-flap/reference-results/fluid-openfoam_solid-dealii.tar.gz b/perpendicular-flap/reference-results/fluid-openfoam_solid-dealii.tar.gz index f10fe7a96..8f9feeab9 100644 --- a/perpendicular-flap/reference-results/fluid-openfoam_solid-dealii.tar.gz +++ b/perpendicular-flap/reference-results/fluid-openfoam_solid-dealii.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6fdbfbdf4636e3d646cb74741fcd986a025ae1a07462b2cd321d3da46e95d2f0 -size 7892633 +oid sha256:f15b2c7bbae2a678d19fc65fe86b6b5a529f06f0313d0a340f30c0079d0c0cc8 +size 3168535 diff --git a/perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz b/perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz index f9aacc6c6..604bb708f 100644 --- a/perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz +++ b/perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49cd8553f6ce718a23c8b727ffbb0b1e4c45b2286ecb071120ff9327b1ebf751 -size 3396348 +oid sha256:2f46e5ef72829f3fb68dfc6ec2b5db377d881d5446b2a1e283e718ee8bc2f225 +size 1388744 diff --git a/perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz b/perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz index 610232565..2a4d504c3 100644 --- a/perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz +++ b/perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e138d80b4640eac657ba3e03ac54391f92381b3555150887129fce6f6321facd -size 2368325 +oid sha256:afa2b5e7c1ce1dbe0457e78fa96a7250a6d10c31d51284722ed0f2d6079e74f2 +size 952670 diff --git a/perpendicular-flap/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz b/perpendicular-flap/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz index 2148af4e3..65894323c 100644 --- a/perpendicular-flap/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz +++ b/perpendicular-flap/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea2d5946f64667ba05ab3a3516cca7c3af3a59374ff22d0a69e0098c00c1ace1 -size 11725074 +oid sha256:d4bfc7abd3e14ae860d676d426acefc2d998bcbbb2ce79a189cadfc2aa7d3a65 +size 4892505 diff --git a/perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz b/perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz index b66c8f2c2..882b29df5 100644 --- a/perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz +++ b/perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8c41e5f1390b4b7038abf42e874438603efb0fa032a3f5f249c5475214cafcde -size 3410840 +oid sha256:1b56464502e14c212d8de8ea8321a2c640b20b3279c16e8ec0770f78e5db4e5b +size 1435421 diff --git a/perpendicular-flap/reference-results/reference_results.metadata b/perpendicular-flap/reference-results/reference_results.metadata index 10c898c8c..7df523318 100644 --- a/perpendicular-flap/reference-results/reference_results.metadata +++ b/perpendicular-flap/reference-results/reference_results.metadata @@ -11,11 +11,11 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-calculix.tar.gz | 2026-05-11 07:45:27 | 4fbb2c688033023cda1ff26f7c97bb123931014d66620b572c930e4b8e0bcf7f | -| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-11 07:45:27 | e138d80b4640eac657ba3e03ac54391f92381b3555150887129fce6f6321facd | -| fluid-su2_solid-fenics.tar.gz | 2026-05-11 07:45:27 | 8c41e5f1390b4b7038abf42e874438603efb0fa032a3f5f249c5475214cafcde | -| fluid-openfoam_solid-fenics.tar.gz | 2026-05-11 07:45:27 | 49cd8553f6ce718a23c8b727ffbb0b1e4c45b2286ecb071120ff9327b1ebf751 | -| fluid-openfoam_solid-dealii.tar.gz | 2026-05-11 07:45:27 | 6fdbfbdf4636e3d646cb74741fcd986a025ae1a07462b2cd321d3da46e95d2f0 | +| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-27 16:22:51 | afa2b5e7c1ce1dbe0457e78fa96a7250a6d10c31d51284722ed0f2d6079e74f2 | +| fluid-su2_solid-fenics.tar.gz | 2026-05-27 16:22:51 | 1b56464502e14c212d8de8ea8321a2c640b20b3279c16e8ec0770f78e5db4e5b | +| fluid-openfoam_solid-calculix.tar.gz | 2026-05-27 16:22:51 | 993ac60ae095bfc5e1e7ce8157c12c6d20093800eac91871b078cda83272e02b | +| fluid-openfoam_solid-fenics.tar.gz | 2026-05-27 16:22:51 | 2f46e5ef72829f3fb68dfc6ec2b5db377d881d5446b2a1e283e718ee8bc2f225 | +| fluid-openfoam_solid-dealii.tar.gz | 2026-05-27 16:22:51 | f15b2c7bbae2a678d19fc65fe86b6b5a529f06f0313d0a340f30c0079d0c0cc8 | ## List of arguments used to generate the files @@ -27,7 +27,7 @@ We also include some information on the machine used to generate them | OPENFOAM_ADAPTER_REF | v1.3.1 | | PYTHON_BINDINGS_REF | v3.2.0 | | FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | b18b3ef | +| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | @@ -44,7 +44,7 @@ We also include some information on the machine used to generate them ### uname -a -Linux precice-tests 5.15.0-177-generic #187-Ubuntu SMP Sat Apr 11 22:54:33 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux ### lscpu diff --git a/quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz b/quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz index cb8869345..72750b518 100644 --- a/quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz +++ b/quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:59f89f37b1378dc850bd00260a495b3b4133c5034c84dab46daddc718f8c2d53 -size 473043 +oid sha256:50f19459abea97a3d2ecfd10193f16b3e2b32f3b6ca5bdaa9ef233838ee1524f +size 199034 diff --git a/quickstart/reference-results/reference_results.metadata b/quickstart/reference-results/reference_results.metadata index 1d658214a..03a21a105 100644 --- a/quickstart/reference-results/reference_results.metadata +++ b/quickstart/reference-results/reference_results.metadata @@ -11,7 +11,7 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-cpp.tar.gz | 2026-05-11 07:45:27 | 59f89f37b1378dc850bd00260a495b3b4133c5034c84dab46daddc718f8c2d53 | +| fluid-openfoam_solid-cpp.tar.gz | 2026-05-27 16:22:51 | 50f19459abea97a3d2ecfd10193f16b3e2b32f3b6ca5bdaa9ef233838ee1524f | ## List of arguments used to generate the files @@ -23,7 +23,7 @@ We also include some information on the machine used to generate them | OPENFOAM_ADAPTER_REF | v1.3.1 | | PYTHON_BINDINGS_REF | v3.2.0 | | FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | b18b3ef | +| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | @@ -40,7 +40,7 @@ We also include some information on the machine used to generate them ### uname -a -Linux precice-tests 5.15.0-177-generic #187-Ubuntu SMP Sat Apr 11 22:54:33 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux ### lscpu diff --git a/tools/tests/README.md b/tools/tests/README.md index ba4891f6b..8a7df6e20 100644 --- a/tools/tests/README.md +++ b/tools/tests/README.md @@ -105,7 +105,7 @@ In this case, building and running seems to work out, but the tests fail because The easiest way to debug a systemtest run is first to have a look at the output written into the action on GitHub. If this does not provide enough hints, the next step is to download the generated `system_tests_run__` artifact. Note that by default this will only be generated if the systemtests fail. -Inside the archive, a test-specific subfolder like `flow-over-heated-plate_fluid-openfoam-solid-fenics_2023-11-19-211723` contains two log files: `system-tests-stderr.log` and `system-tests-stdout.log`. This can be a starting point for a further investigation. When fieldcompare runs with `--diff`, it writes VTK diff files under `precice-exports/`; if the comparison fails, those files are copied into a `diff-results/` subfolder in the same run directory (mirroring any subpaths under `precice-exports/`) so you can open them (e.g. in ParaView) to see where results differ from the reference. On successful comparisons, `diff-results/` is therefore absent. +Inside the archive, a test-specific subfolder like `flow-over-heated-plate_fluid-openfoam-solid-fenics_2023-11-19-211723` contains two log files: `system-tests-stderr.log` and `system-tests-stdout.log`. This can be a starting point for a further investigation. When fieldcompare runs with `--diff`, it writes VTK (.vtu) diff files under `precice-exports/`; if the comparison fails, those files are copied into a `diff-results/` subfolder in the same run directory (mirroring any subpaths under `precice-exports/`) so you can open them (e.g. in ParaView) to see where results differ from the reference. On successful comparisons, `diff-results/` is therefore absent. ## Adding new tests diff --git a/tools/tests/docker-compose.template.yaml b/tools/tests/docker-compose.template.yaml index 1eaddbeee..534e952c0 100644 --- a/tools/tests/docker-compose.template.yaml +++ b/tools/tests/docker-compose.template.yaml @@ -13,7 +13,7 @@ services: /bin/bash -c "id && cd '/runs/{{ tutorial_folder }}' && sed -i 's||= debug and %Rank% = 0\" enabled=\"true\" />|g' precice-config.xml && - sed -i 's%% %g' precice-config.xml && + sed -i 's%% %g' precice-config.xml && sed -i 's|m2n:sockets |m2n:sockets network=\"eth0\" |g' precice-config.xml && cat precice-config.xml" diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index 94f7bf6ae..ba17ce18b 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -4,7 +4,7 @@ OPENFOAM_EXECUTABLE: "openfoam2312" OPENFOAM_ADAPTER_REF: "v1.3.1" PYTHON_BINDINGS_REF: "v3.2.0" FENICS_ADAPTER_REF: "v2.2.0" -TUTORIALS_REF: "c725522598fedf6bf691f0292aafb85e97fae73d" # May 26, 2026 +TUTORIALS_REF: "aba048a5a36806cc2fd5a768e13174b82f365822" # May 26, 2026 PLATFORM: "ubuntu_2404" CALCULIX_VERSION: "2.20" CALCULIX_ADAPTER_REF: "v2.20.1" diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index 2ff8564eb..a8dad6891 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -223,3 +223,8 @@ test_suites: su2-adapter: tutorials: - *perpendicular-flap_fluid-su2_solid-fenics + +# A test suite used for the system tests development + system-tests-dev: + tutorials: + - *elastic-tube-1d_fluid-cpp_solid-cpp \ No newline at end of file From 19281bc33cc02fb16ad9f048b281a31dbb66aa11 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Wed, 27 May 2026 19:35:44 +0200 Subject: [PATCH 14/94] Add elastic-tube-3d to the system tests (#800) --- .../generate_reference_results_manual.yml | 8 +++ .../generate_reference_results_workflow.yml | 5 ++ elastic-tube-3d/metadata.yaml | 2 +- .../fluid-openfoam_solid-calculix.tar.gz | 3 + .../fluid-openfoam_solid-fenics.tar.gz | 3 + .../reference_results.metadata | 72 +++++++++++++++++++ tools/tests/reference_versions.yaml | 2 +- tools/tests/tests.yaml | 23 ++++++ 8 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 elastic-tube-3d/reference-results/fluid-openfoam_solid-calculix.tar.gz create mode 100644 elastic-tube-3d/reference-results/fluid-openfoam_solid-fenics.tar.gz create mode 100644 elastic-tube-3d/reference-results/reference_results.metadata diff --git a/.github/workflows/generate_reference_results_manual.yml b/.github/workflows/generate_reference_results_manual.yml index 5dbcf3f28..6959b5dc3 100644 --- a/.github/workflows/generate_reference_results_manual.yml +++ b/.github/workflows/generate_reference_results_manual.yml @@ -15,6 +15,13 @@ on: default: '' required: false type: string + clean_docker: + description: 'Clean Docker before running' + default: 'TRUE' + type: choice + options: + - 'FALSE' + - 'TRUE' log_level: description: 'Logging verbosity level used for the systemtests' default: 'INFO' @@ -34,4 +41,5 @@ jobs: from_ref: ${{ inputs.from_ref }} commit_msg: ${{ inputs.commit_msg }} suites: ${{ inputs.suites }} + clean_docker: ${{ inputs.clean_docker }} log_level: ${{ inputs.log_level }} \ No newline at end of file diff --git a/.github/workflows/generate_reference_results_workflow.yml b/.github/workflows/generate_reference_results_workflow.yml index 6ac87f392..d2670666e 100644 --- a/.github/workflows/generate_reference_results_workflow.yml +++ b/.github/workflows/generate_reference_results_workflow.yml @@ -15,6 +15,10 @@ on: default: '' required: false type: string + clean_docker: + description: 'Clean Docker before running' + default: 'TRUE' + type: string log_level: description: 'Logging verbosity level used for the systemtests' required: true @@ -51,6 +55,7 @@ jobs: run: | pip install --user -r tools/tests/requirements.txt - name: Cleanup Docker cache + if: ${{ inputs.clean_docker == 'TRUE' }} # Remove all Docker containers, images, and build caches # to ensure consistent results and as storage cleanup run: | diff --git a/elastic-tube-3d/metadata.yaml b/elastic-tube-3d/metadata.yaml index 04b45db3c..438888d8b 100644 --- a/elastic-tube-3d/metadata.yaml +++ b/elastic-tube-3d/metadata.yaml @@ -10,7 +10,7 @@ cases: fluid-openfoam: participant: Fluid directory: ./fluid-openfoam - run: ./run.sh + run: ./run.sh -parallel component: openfoam-adapter solid-calculix: diff --git a/elastic-tube-3d/reference-results/fluid-openfoam_solid-calculix.tar.gz b/elastic-tube-3d/reference-results/fluid-openfoam_solid-calculix.tar.gz new file mode 100644 index 000000000..1c6092064 --- /dev/null +++ b/elastic-tube-3d/reference-results/fluid-openfoam_solid-calculix.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dcd773b4c173936f5214a481991dec05b7d8fbdd5ad4136f6cb2a68839303fed +size 1100941 diff --git a/elastic-tube-3d/reference-results/fluid-openfoam_solid-fenics.tar.gz b/elastic-tube-3d/reference-results/fluid-openfoam_solid-fenics.tar.gz new file mode 100644 index 000000000..8a7346a5d --- /dev/null +++ b/elastic-tube-3d/reference-results/fluid-openfoam_solid-fenics.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9fb5a39115545cf8d3b160662c1e330dc8000a87a23b3169a655a390f61eaa8e +size 461799 diff --git a/elastic-tube-3d/reference-results/reference_results.metadata b/elastic-tube-3d/reference-results/reference_results.metadata new file mode 100644 index 000000000..832c3efd7 --- /dev/null +++ b/elastic-tube-3d/reference-results/reference_results.metadata @@ -0,0 +1,72 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| fluid-openfoam_solid-fenics.tar.gz | 2026-05-27 19:16:34 | 9fb5a39115545cf8d3b160662c1e330dc8000a87a23b3169a655a390f61eaa8e | +| fluid-openfoam_solid-calculix.tar.gz | 2026-05-27 19:16:34 | dcd773b4c173936f5214a481991dec05b7d8fbdd5ad4136f6cb2a68839303fed | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PRECICE_REF | d199bb3 | +| PRECICE_PRESET | production-audit | +| OPENFOAM_EXECUTABLE | openfoam2312 | +| OPENFOAM_ADAPTER_REF | v1.3.1 | +| PYTHON_BINDINGS_REF | v3.2.0 | +| FENICS_ADAPTER_REF | v2.2.0 | +| TUTORIALS_REF | ab8eaceb42daebc6c4d7d8864c25997f983522e5 | +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| SU2_VERSION | 7.5.1 | +| SU2_ADAPTER_REF | 64d4aff | +| DEALII_ADAPTER_REF | 02c5d18 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| DUMUX_ADAPTER_REF | 0e914bb | +| MICRO_MANAGER_REF | v0.8.0 | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index ba17ce18b..1b19d5676 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -4,7 +4,7 @@ OPENFOAM_EXECUTABLE: "openfoam2312" OPENFOAM_ADAPTER_REF: "v1.3.1" PYTHON_BINDINGS_REF: "v3.2.0" FENICS_ADAPTER_REF: "v2.2.0" -TUTORIALS_REF: "aba048a5a36806cc2fd5a768e13174b82f365822" # May 26, 2026 +TUTORIALS_REF: "ab8eaceb42daebc6c4d7d8864c25997f983522e5" # May 27, 2026 PLATFORM: "ubuntu_2404" CALCULIX_VERSION: "2.20" CALCULIX_ADAPTER_REF: "v2.20.1" diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index a8dad6891..b97883c7c 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -32,6 +32,23 @@ test_suites: - solid-python reference_result: ./elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz + elastic-tube-3d: + tutorials: + - &elastic-tube-3d_fluid-openfoam_solid-calculix + path: elastic-tube-3d + case_combination: + - fluid-openfoam + - solid-calculix + max_time_windows: 3 + reference_result: ./elastic-tube-3d/reference-results/fluid-openfoam_solid-calculix.tar.gz + - &elastic-tube-3d_fluid-openfoam_solid-fenics + path: elastic-tube-3d + case_combination: + - fluid-openfoam + - solid-fenics + max_time_windows: 1 + reference_result: ./elastic-tube-3d/reference-results/fluid-openfoam_solid-fenics.tar.gz + flow-over-heated-plate: tutorials: - &flow-over-heated-plate_fluid-openfoam_solid-fenics @@ -159,6 +176,8 @@ test_suites: - *elastic-tube-1d_fluid-cpp_solid-cpp - *elastic-tube-1d_fluid-cpp_solid-python - *elastic-tube-1d_fluid-python_solid-python + - *elastic-tube-3d_fluid-openfoam_solid-calculix + - *elastic-tube-3d_fluid-openfoam_solid-fenics - *flow-over-heated-plate_fluid-openfoam_solid-fenics - *flow-over-heated-plate_fluid-openfoam_solid-nutils - *flow-over-heated-plate_fluid-openfoam_solid-openfoam @@ -177,6 +196,7 @@ test_suites: calculix-adapter: tutorials: + - *elastic-tube-3d_fluid-openfoam_solid-calculix - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix - *heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix - *perpendicular-flap_fluid-openfoam_solid-calculix @@ -193,6 +213,7 @@ test_suites: fenics-adapter: tutorials: + - *elastic-tube-3d_fluid-openfoam_solid-fenics - *flow-over-heated-plate_fluid-openfoam_solid-fenics - *perpendicular-flap_fluid-openfoam_solid-fenics - *perpendicular-flap_fluid-su2_solid-fenics @@ -207,6 +228,8 @@ test_suites: openfoam-adapter: tutorials: + - *elastic-tube-3d_fluid-openfoam_solid-calculix + - *elastic-tube-3d_fluid-openfoam_solid-fenics - *flow-over-heated-plate_fluid-openfoam_solid-fenics - *flow-over-heated-plate_fluid-openfoam_solid-nutils - *flow-over-heated-plate_fluid-openfoam_solid-openfoam From daeef20736a93391aaa84bfea02cf7f33d91c0cb Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Wed, 27 May 2026 19:54:25 +0200 Subject: [PATCH 15/94] Make some OpenFOAM cases parallel (#803) --- flow-over-heated-plate-nearest-projection/metadata.yaml | 4 ++-- flow-over-heated-plate-two-meshes/metadata.yaml | 2 +- flow-over-heated-plate/metadata.yaml | 4 ++-- perpendicular-flap/metadata.yaml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/flow-over-heated-plate-nearest-projection/metadata.yaml b/flow-over-heated-plate-nearest-projection/metadata.yaml index dbd1936c8..046528f09 100644 --- a/flow-over-heated-plate-nearest-projection/metadata.yaml +++ b/flow-over-heated-plate-nearest-projection/metadata.yaml @@ -10,11 +10,11 @@ cases: fluid-openfoam: participant: Fluid directory: ./fluid-openfoam - run: ./run.sh + run: ./run.sh -parallel component: openfoam-adapter solid-openfoam: participant: Solid directory: ./solid-openfoam - run: ./run.sh + run: ./run.sh -parallel component: openfoam-adapter diff --git a/flow-over-heated-plate-two-meshes/metadata.yaml b/flow-over-heated-plate-two-meshes/metadata.yaml index bf0d2f2f0..c457cca11 100644 --- a/flow-over-heated-plate-two-meshes/metadata.yaml +++ b/flow-over-heated-plate-two-meshes/metadata.yaml @@ -10,7 +10,7 @@ cases: fluid-openfoam: participant: Fluid directory: ./fluid-openfoam - run: ./run.sh + run: ./run.sh -parallel component: openfoam-adapter solid-calculix: diff --git a/flow-over-heated-plate/metadata.yaml b/flow-over-heated-plate/metadata.yaml index 0fea0d842..5d01b9e4f 100644 --- a/flow-over-heated-plate/metadata.yaml +++ b/flow-over-heated-plate/metadata.yaml @@ -10,7 +10,7 @@ cases: fluid-openfoam: participant: Fluid directory: ./fluid-openfoam - run: ./run.sh + run: ./run.sh -parallel component: openfoam-adapter solid-fenics: @@ -28,7 +28,7 @@ cases: solid-openfoam: participant: Solid directory: ./solid-openfoam - run: ./run.sh + run: ./run.sh -parallel component: openfoam-adapter diff --git a/perpendicular-flap/metadata.yaml b/perpendicular-flap/metadata.yaml index 78d8acef9..57f255847 100644 --- a/perpendicular-flap/metadata.yaml +++ b/perpendicular-flap/metadata.yaml @@ -16,7 +16,7 @@ cases: fluid-openfoam: participant: Fluid directory: ./fluid-openfoam - run: ./run.sh + run: ./run.sh -parallel component: openfoam-adapter fluid-su2: @@ -52,7 +52,7 @@ cases: solid-openfoam: participant: Solid directory: ./solid-openfoam - run: ./run.sh + run: ./run.sh -parallel component: openfoam-adapter # solid-solids4foam: From 730edfb3c82a3b033fc7e4d4040ba1a1ea288a44 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Wed, 27 May 2026 20:17:13 +0200 Subject: [PATCH 16/94] Restrict the max-time in some system tests (#804) --- tools/tests/tests.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index b97883c7c..8d8a729ad 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -24,6 +24,7 @@ test_suites: case_combination: - fluid-cpp - solid-python + max_time: 0.1 reference_result: ./elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz - &elastic-tube-1d_fluid-python_solid-python path: elastic-tube-1d @@ -62,6 +63,7 @@ test_suites: case_combination: - fluid-openfoam - solid-nutils + max_time: 0.1 reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz - &flow-over-heated-plate_fluid-openfoam_solid-openfoam path: flow-over-heated-plate @@ -77,6 +79,7 @@ test_suites: case_combination: - fluid-openfoam - solid-openfoam + max_time: 0.1 reference_result: ./flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz flow-over-heated-plate-two-meshes: @@ -86,6 +89,7 @@ test_suites: case_combination: - fluid-openfoam - solid-calculix + max_time: 0.1 reference_result: ./flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz free-flow-over-porous-media: @@ -105,6 +109,7 @@ test_suites: - fluid-top-openfoam - fluid-bottom-openfoam - solid-calculix + max_time: 0.1 reference_result: ./heat-exchanger-simplified/reference-results/fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz multiple-perpendicular-flaps: @@ -115,6 +120,7 @@ test_suites: - fluid-openfoam - solid-upstream-dealii - solid-downstream-dealii + max_time: 0.1 reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz perpendicular-flap: @@ -136,18 +142,21 @@ test_suites: case_combination: - fluid-openfoam - solid-fenics + max_time: 0.1 reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz - &perpendicular-flap_fluid-openfoam_solid-openfoam path: perpendicular-flap case_combination: - fluid-openfoam - solid-openfoam + max_time: 0.1 reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz - &perpendicular-flap_fluid-su2_solid-fenics path: perpendicular-flap case_combination: - fluid-su2 - solid-fenics + max_time: 0.1 reference_result: ./perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz two-scale-heat-conduction: From 8b5187288a45dbcf3e13a0611cd2adba7194cad9 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Wed, 27 May 2026 21:22:02 +0200 Subject: [PATCH 17/94] Migrate heat-exchanger/download-meshes.sh into the run.sh scripts (#805) --- changelog-entries/805.md | 1 + heat-exchanger/README.md | 4 +--- heat-exchanger/download-meshes.sh | 22 ---------------------- heat-exchanger/fluid-inner-openfoam/run.sh | 6 ++++++ heat-exchanger/fluid-outer-openfoam/run.sh | 6 ++++++ heat-exchanger/solid-calculix/run.sh | 7 +++++-- 6 files changed, 19 insertions(+), 27 deletions(-) create mode 100644 changelog-entries/805.md delete mode 100755 heat-exchanger/download-meshes.sh diff --git a/changelog-entries/805.md b/changelog-entries/805.md new file mode 100644 index 000000000..fa3cf0946 --- /dev/null +++ b/changelog-entries/805.md @@ -0,0 +1 @@ +- Removed the heat-exchanger/download-meshes.sh script. The meshes are now automatically downloaded by each run.sh script. [#805](https://github.com/precice/tutorials/pull/805) \ No newline at end of file diff --git a/heat-exchanger/README.md b/heat-exchanger/README.md index b891a4580..c14ab5225 100644 --- a/heat-exchanger/README.md +++ b/heat-exchanger/README.md @@ -42,11 +42,9 @@ preCICE configuration (image generated using the [precice-config-visualizer](htt ## Running the Simulation {% note %} -Since the already prepared case contains mesh files of approx. 50MB in size, we currently host these files outside of the tutorials repository and you can download and extract them automatically in the appropriate locations by running the download-meshes.sh script. You can [help us improve this](https://github.com/precice/tutorials/issues/6)! +Since the already prepared case contains mesh files of approx. 50MB in size, we currently host these files outside of the tutorials repository and are downloaded automatically the first time. You can [help us improve this](https://github.com/precice/tutorials/issues/6)! {% endnote %} -Before starting the simulation for the first time you need to download the mesh files and copy them into the appropriate location. The shell script `./download-meshes.sh` will handle these things automatically. Afterwards, the simulation setup is ready to run. - In order to run the coupled simulation, you can simply step into the participant directories and execute`./run.sh` (or `./run.sh -parallel` for running a fluid participant in parallel). The simulation will need several minutes or up to an hour on a laptop to end (t=500). Before repeating the simulation, you can use the `clean-tutorial.sh` script to clean-up any previous results and log files. ## Post-processing diff --git a/heat-exchanger/download-meshes.sh b/heat-exchanger/download-meshes.sh deleted file mode 100755 index 02f2a72f7..000000000 --- a/heat-exchanger/download-meshes.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env sh - -echo "This tutorial is based on a case prepared with SimScale." -echo "Since the mesh files are several MB large, we don't store them in the Git repository." -echo "This script downloads and extracts the missing files." -echo "" - -echo "Downloading the Solid case..." -wget -nv -O - https://syncandshare.lrz.de/dl/fi3c9Xt5UzBc5hJvXzsLBHXn/Solid.tar.gz | tar -xzv -mv ./Solid/* solid-calculix -rm -r ./Solid -sed -i 's/Solid/\./g' solid-calculix/solid.inp - -echo "Downloading and extracting the Inner-Fluid mesh..." -wget -nv -O - https://syncandshare.lrz.de/dl/fiNsYGC1DKzgio4jS5NhsXg7/polyMesh.org.tar.gz | tar -xzv -C fluid-inner-openfoam/constant -mv fluid-inner-openfoam/constant/polyMesh.org fluid-inner-openfoam/constant/polyMesh - -echo "Downloading and extracting the Outer-Fluid mesh..." -wget -nv -O - https://syncandshare.lrz.de/dl/fiEZRQ8rcVWRkoyZvANim1R1/polyMesh.org.tar.gz | tar -xzv -C fluid-outer-openfoam/constant -mv fluid-outer-openfoam/constant/polyMesh.org fluid-outer-openfoam/constant/polyMesh - -echo "Completed." diff --git a/heat-exchanger/fluid-inner-openfoam/run.sh b/heat-exchanger/fluid-inner-openfoam/run.sh index 3c9f805ce..353e8816a 100755 --- a/heat-exchanger/fluid-inner-openfoam/run.sh +++ b/heat-exchanger/fluid-inner-openfoam/run.sh @@ -4,6 +4,12 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 +if [ ! -f constant/polyMesh/boundary ]; then + echo "Downloading and extracting the Inner-Fluid mesh..." + wget -nv -O - https://syncandshare.lrz.de/dl/fiNsYGC1DKzgio4jS5NhsXg7/polyMesh.org.tar.gz | tar -xzv -C constant + mv constant/polyMesh.org constant/polyMesh +fi + ../../tools/run-openfoam.sh "$@" . ../../tools/openfoam-remove-empty-dirs.sh && openfoam_remove_empty_dirs diff --git a/heat-exchanger/fluid-outer-openfoam/run.sh b/heat-exchanger/fluid-outer-openfoam/run.sh index 3c9f805ce..fb3b7fa48 100755 --- a/heat-exchanger/fluid-outer-openfoam/run.sh +++ b/heat-exchanger/fluid-outer-openfoam/run.sh @@ -4,6 +4,12 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 +if [ ! -f constant/polyMesh/boundary ]; then + echo "Downloading and extracting the Outer-Fluid mesh..." + wget -nv -O - https://syncandshare.lrz.de/dl/fiEZRQ8rcVWRkoyZvANim1R1/polyMesh.org.tar.gz | tar -xzv -C constant + mv constant/polyMesh.org constant/polyMesh +fi + ../../tools/run-openfoam.sh "$@" . ../../tools/openfoam-remove-empty-dirs.sh && openfoam_remove_empty_dirs diff --git a/heat-exchanger/solid-calculix/run.sh b/heat-exchanger/solid-calculix/run.sh index 1b1434904..00ade8ecc 100755 --- a/heat-exchanger/solid-calculix/run.sh +++ b/heat-exchanger/solid-calculix/run.sh @@ -5,8 +5,11 @@ set -e -u exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -f all.msh ]; then - echo "Mesh files not found. Use the Download_meshes script to download them." - exit + echo "Downloading the pre-processed Solid case..." + wget -nv -O - https://syncandshare.lrz.de/dl/fi3c9Xt5UzBc5hJvXzsLBHXn/Solid.tar.gz | tar -xzv + mv ./Solid/* . + rm -r ./Solid + sed -i 's/Solid/\./g' solid.inp fi export OMP_NUM_THREADS=1 From d06461d922b05f5b804e87a9e455f7793a09dca4 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Wed, 27 May 2026 21:35:29 +0200 Subject: [PATCH 18/94] Add the heat exchanger to the system tests (#806) --- changelog-entries/806.md | 1 + heat-exchanger/metadata.yaml | 29 +++++++++++++++++++++++++++++ tools/tests/tests.yaml | 14 ++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 changelog-entries/806.md create mode 100644 heat-exchanger/metadata.yaml diff --git a/changelog-entries/806.md b/changelog-entries/806.md new file mode 100644 index 000000000..c2436149d --- /dev/null +++ b/changelog-entries/806.md @@ -0,0 +1 @@ +- Added the heat-exchanger tutorial to the system tests [#806](https://github.com/precice/tutorials/pull/806) \ No newline at end of file diff --git a/heat-exchanger/metadata.yaml b/heat-exchanger/metadata.yaml new file mode 100644 index 000000000..54974dd77 --- /dev/null +++ b/heat-exchanger/metadata.yaml @@ -0,0 +1,29 @@ +name: Heat exchanger +path: heat-exchanger # relative to git repo +url: https://precice.org/tutorials-heat-exchanger.html + +participants: + - Fluid-Inner + - Fluid-Outer + - Solid + +cases: + fluid-inner-openfoam: + participant: Fluid-Inner + directory: ./fluid-inner-openfoam + run: ./run.sh + component: openfoam-adapter + + fluid-outer-openfoam: + participant: Fluid-Outer + directory: ./fluid-outer-openfoam + run: ./run.sh + component: openfoam-adapter + + solid-calculix: + participant: Solid + directory: ./solid-calculix + run: ./run.sh + component: calculix-adapter + + diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index 8d8a729ad..3770dde23 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -101,6 +101,17 @@ test_suites: - porous-media-dumux reference_result: ./free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz + heat-exchanger: + tutorials: + - &heat-exchanger_fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam + path: heat-exchanger + case_combination: + - fluid-inner-openfoam + - fluid-outer-openfoam + - solid-calculix + max_time_windows: 3 + reference_result: ./heat-exchanger/reference-results/fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam.tar.gz + heat-exchanger-simplified: tutorials: - &heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix @@ -193,6 +204,7 @@ test_suites: - *flow-over-heated-plate-nearest-projection_fluid-openfoam_solid-openfoam - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix - *free-flow-over-porous-media_free-flow-dumux_porous-media-dumux + - *heat-exchanger_fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam - *heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix - *multiple-perpendicular-flaps_fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii - *perpendicular-flap_fluid-openfoam_solid-calculix @@ -207,6 +219,7 @@ test_suites: tutorials: - *elastic-tube-3d_fluid-openfoam_solid-calculix - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix + - *heat-exchanger_fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam - *heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix - *perpendicular-flap_fluid-openfoam_solid-calculix @@ -244,6 +257,7 @@ test_suites: - *flow-over-heated-plate_fluid-openfoam_solid-openfoam - *flow-over-heated-plate-nearest-projection_fluid-openfoam_solid-openfoam - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix + - *heat-exchanger_fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam - *heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix - *multiple-perpendicular-flaps_fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii - *perpendicular-flap_fluid-openfoam_solid-calculix From bbd41019d7e4fdb0310d1c332f52cca9a3eac85e Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Wed, 27 May 2026 22:11:22 +0200 Subject: [PATCH 19/94] Add partitioned-pipe tutorials to the system tests (#807) * Add partitioned-pipe to the system tests * Add partitioned-pipe-two-phase to the system tests * Add partitioned-pipe-multiscale to the system tests * Add missing venv checks in partitioned-pipe-multiscale --- .../fluid1d-left-nutils/run.sh | 9 ++-- .../fluid1d-right-nutils/run.sh | 9 ++-- partitioned-pipe-multiscale/metadata.yaml | 20 ++++++++ partitioned-pipe-two-phase/metadata.yaml | 20 ++++++++ partitioned-pipe/metadata.yaml | 32 +++++++++++++ tools/tests/tests.yaml | 48 ++++++++++++++++++- 6 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 partitioned-pipe-multiscale/metadata.yaml create mode 100644 partitioned-pipe-two-phase/metadata.yaml create mode 100644 partitioned-pipe/metadata.yaml diff --git a/partitioned-pipe-multiscale/fluid1d-left-nutils/run.sh b/partitioned-pipe-multiscale/fluid1d-left-nutils/run.sh index 4e978edf1..fb474e9a8 100755 --- a/partitioned-pipe-multiscale/fluid1d-left-nutils/run.sh +++ b/partitioned-pipe-multiscale/fluid1d-left-nutils/run.sh @@ -4,9 +4,12 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -python3 -m venv .venv -. .venv/bin/activate -pip install -r requirements.txt && pip freeze > pip-installed-packages.log +if [ ! -v PRECICE_TUTORIALS_NO_VENV ] +then + python3 -m venv .venv + . .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log +fi NUTILS_RICHOUTPUT=no python3 ../solver-fluid1d-nutils/Fluid1D.py side=Left diff --git a/partitioned-pipe-multiscale/fluid1d-right-nutils/run.sh b/partitioned-pipe-multiscale/fluid1d-right-nutils/run.sh index e064d1e66..0593f1eeb 100755 --- a/partitioned-pipe-multiscale/fluid1d-right-nutils/run.sh +++ b/partitioned-pipe-multiscale/fluid1d-right-nutils/run.sh @@ -4,9 +4,12 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -python3 -m venv .venv -. .venv/bin/activate -pip install -r requirements.txt && pip freeze > pip-installed-packages.log +if [ ! -v PRECICE_TUTORIALS_NO_VENV ] +then + python3 -m venv .venv + . .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log +fi NUTILS_RICHOUTPUT=no python3 ../solver-fluid1d-nutils/Fluid1D.py side=Right diff --git a/partitioned-pipe-multiscale/metadata.yaml b/partitioned-pipe-multiscale/metadata.yaml new file mode 100644 index 000000000..666e91026 --- /dev/null +++ b/partitioned-pipe-multiscale/metadata.yaml @@ -0,0 +1,20 @@ +name: Partitioned pipe multiscale +path: partitioned-pipe-multiscale # relative to git repo +url: https://precice.org/tutorials-partitioned-pipe-multiscale.html + +participants: + - Fluid1DLeft + - Fluid3DRight + +cases: + fluid1d-left-nutils: + participant: Fluid1DLeft + directory: ./fluid1d-left-nutils + run: ./run.sh + component: nutils-adapter + + fluid3d-right-openfoam: + participant: Fluid3DRight + directory: ./fluid3d-right-openfoam + run: ./run.sh + component: openfoam-adapter diff --git a/partitioned-pipe-two-phase/metadata.yaml b/partitioned-pipe-two-phase/metadata.yaml new file mode 100644 index 000000000..03790cd79 --- /dev/null +++ b/partitioned-pipe-two-phase/metadata.yaml @@ -0,0 +1,20 @@ +name: Partitioned pipe two phase +path: partitioned-pipe-two-phase # relative to git repo +url: https://precice.org/tutorials-partitioned-pipe-two-phase.html + +participants: + - Fluid1 + - Fluid2 + +cases: + fluid1-openfoam: + participant: Fluid1 + directory: ./fluid1-openfoam + run: ./run.sh + component: openfoam-adapter + + fluid2-openfoam: + participant: Fluid2 + directory: ./fluid2-openfoam + run: ./run.sh + component: openfoam-adapter diff --git a/partitioned-pipe/metadata.yaml b/partitioned-pipe/metadata.yaml new file mode 100644 index 000000000..2b92c351e --- /dev/null +++ b/partitioned-pipe/metadata.yaml @@ -0,0 +1,32 @@ +name: Partitioned pipe +path: partitioned-pipe # relative to git repo +url: https://precice.org/tutorials-partitioned-pipe.html + +participants: + - Fluid1 + - Fluid2 + +cases: + fluid1-openfoam-pimplefoam: + participant: Fluid1 + directory: ./fluid1-openfoam-pimplefoam + run: ./run.sh -parallel + component: openfoam-adapter + + fluid1-openfoam-sonicliquidfoam: + participant: Fluid1 + directory: ./fluid1-openfoam-sonicliquidfoam + run: ./run.sh -parallel + component: openfoam-adapter + + fluid2-openfoam-pimplefoam: + participant: Fluid2 + directory: ./fluid2-openfoam-pimplefoam + run: ./run.sh -parallel + component: openfoam-adapter + + fluid2-openfoam-sonicliquidfoam: + participant: Fluid2 + directory: ./fluid2-openfoam-sonicliquidfoam + run: ./run.sh -parallel + component: openfoam-adapter \ No newline at end of file diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index 3770dde23..eff8426b3 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -132,7 +132,44 @@ test_suites: - solid-upstream-dealii - solid-downstream-dealii max_time: 0.1 - reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz + reference_result: ./multiple-perpendicular-flaps/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz + + partitioned-pipe: + tutorials: + - &partitioned-pipe_fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam + path: partitioned-pipe + case_combination: + - fluid1-openfoam-pimplefoam + - fluid2-openfoam-pimplefoam + max_time: 0.1 + reference_result: ./partitioned-pipe/reference-results/fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam.tar.gz + - &partitioned-pipe_fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam + path: partitioned-pipe + case_combination: + - fluid1-openfoam-sonicliquidfoam + - fluid2-openfoam-sonicliquidfoam + max_time: 0.1 + reference_result: ./partitioned-pipe/reference-results/fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam.tar.gz + + partitioned-pipe-multiscale: + tutorials: + - &partitioned-pipe-multiscale_fluid1d-left-nutils_fluid3d-right-openfoam + path: partitioned-pipe-multiscale + case_combination: + - fluid1d-left-nutils + - fluid3d-right-openfoam + max_time: 0.1 + reference_result: ./partitioned-pipe-multiscale/reference-results/fluid1d-left-nutils_fluid3d-right-openfoam + + partitioned-pipe-two-phase: + tutorials: + - &partitioned-pipe-two-phase_fluid1-openfoam_fluid2-openfoam + path: partitioned-pipe-two-phase + case_combination: + - fluid1-openfoam + - fluid2-openfoam + max_time: 0.1 + reference_result: ./partitioned-pipe-two-phase/reference-results/fluid1-openfoam_fluid2-openfoam.tar.gz perpendicular-flap: tutorials: @@ -207,6 +244,10 @@ test_suites: - *heat-exchanger_fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam - *heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix - *multiple-perpendicular-flaps_fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii + - *partitioned-pipe_fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam + - *partitioned-pipe_fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam + - *partitioned-pipe-multiscale_fluid1d-left-nutils_fluid3d-right-openfoam + - *partitioned-pipe-two-phase_fluid1-openfoam_fluid2-openfoam - *perpendicular-flap_fluid-openfoam_solid-calculix - *perpendicular-flap_fluid-openfoam_solid-dealii - *perpendicular-flap_fluid-openfoam_solid-fenics @@ -247,6 +288,7 @@ test_suites: nutils-adapter: # Not a repository tutorials: - *flow-over-heated-plate_fluid-openfoam_solid-nutils + - *partitioned-pipe-multiscale_fluid1d-left-nutils_fluid3d-right-openfoam openfoam-adapter: tutorials: @@ -260,6 +302,10 @@ test_suites: - *heat-exchanger_fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam - *heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix - *multiple-perpendicular-flaps_fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii + - *partitioned-pipe_fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam + - *partitioned-pipe_fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam + - *partitioned-pipe-multiscale_fluid1d-left-nutils_fluid3d-right-openfoam + - *partitioned-pipe-two-phase_fluid1-openfoam_fluid2-openfoam - *perpendicular-flap_fluid-openfoam_solid-calculix - *perpendicular-flap_fluid-openfoam_solid-dealii - *perpendicular-flap_fluid-openfoam_solid-fenics From b5e3046cf3aa0307d22c50ad0f3084acd5b57474 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Wed, 27 May 2026 22:17:36 +0200 Subject: [PATCH 20/94] Add missing checks for PRECICE_TUTORIALS_NO_VENV (#808) --- oscillator-overlap/mass-left-python/run.sh | 9 ++++++--- oscillator-overlap/mass-right-python/run.sh | 9 ++++++--- oscillator/mass-left-python/run.sh | 9 ++++++--- oscillator/mass-right-python/run.sh | 9 ++++++--- water-hammer/fluid1d-left-nutils/run.sh | 9 ++++++--- water-hammer/fluid1d-right-nutils/run.sh | 9 ++++++--- 6 files changed, 36 insertions(+), 18 deletions(-) diff --git a/oscillator-overlap/mass-left-python/run.sh b/oscillator-overlap/mass-left-python/run.sh index 8268553b8..5a9c18658 100755 --- a/oscillator-overlap/mass-left-python/run.sh +++ b/oscillator-overlap/mass-left-python/run.sh @@ -4,9 +4,12 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -python3 -m venv .venv -. .venv/bin/activate -pip install -r ../solver-python/requirements.txt +if [ ! -v PRECICE_TUTORIALS_NO_VENV ] +then + python3 -m venv .venv + . .venv/bin/activate + pip install -r ../solver-python/requirements.txt && pip freeze > pip-installed-packages.log +fi python3 ../solver-python/oscillator.py Mass-Left diff --git a/oscillator-overlap/mass-right-python/run.sh b/oscillator-overlap/mass-right-python/run.sh index 51ddf0eac..be993c49f 100755 --- a/oscillator-overlap/mass-right-python/run.sh +++ b/oscillator-overlap/mass-right-python/run.sh @@ -4,9 +4,12 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -python3 -m venv .venv -. .venv/bin/activate -pip install -r ../solver-python/requirements.txt +if [ ! -v PRECICE_TUTORIALS_NO_VENV ] +then + python3 -m venv .venv + . .venv/bin/activate + pip install -r ../solver-python/requirements.txt && pip freeze > pip-installed-packages.log +fi python3 ../solver-python/oscillator.py Mass-Right diff --git a/oscillator/mass-left-python/run.sh b/oscillator/mass-left-python/run.sh index 8268553b8..5a9c18658 100755 --- a/oscillator/mass-left-python/run.sh +++ b/oscillator/mass-left-python/run.sh @@ -4,9 +4,12 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -python3 -m venv .venv -. .venv/bin/activate -pip install -r ../solver-python/requirements.txt +if [ ! -v PRECICE_TUTORIALS_NO_VENV ] +then + python3 -m venv .venv + . .venv/bin/activate + pip install -r ../solver-python/requirements.txt && pip freeze > pip-installed-packages.log +fi python3 ../solver-python/oscillator.py Mass-Left diff --git a/oscillator/mass-right-python/run.sh b/oscillator/mass-right-python/run.sh index 51ddf0eac..be993c49f 100755 --- a/oscillator/mass-right-python/run.sh +++ b/oscillator/mass-right-python/run.sh @@ -4,9 +4,12 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -python3 -m venv .venv -. .venv/bin/activate -pip install -r ../solver-python/requirements.txt +if [ ! -v PRECICE_TUTORIALS_NO_VENV ] +then + python3 -m venv .venv + . .venv/bin/activate + pip install -r ../solver-python/requirements.txt && pip freeze > pip-installed-packages.log +fi python3 ../solver-python/oscillator.py Mass-Right diff --git a/water-hammer/fluid1d-left-nutils/run.sh b/water-hammer/fluid1d-left-nutils/run.sh index 4e978edf1..fb474e9a8 100755 --- a/water-hammer/fluid1d-left-nutils/run.sh +++ b/water-hammer/fluid1d-left-nutils/run.sh @@ -4,9 +4,12 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -python3 -m venv .venv -. .venv/bin/activate -pip install -r requirements.txt && pip freeze > pip-installed-packages.log +if [ ! -v PRECICE_TUTORIALS_NO_VENV ] +then + python3 -m venv .venv + . .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log +fi NUTILS_RICHOUTPUT=no python3 ../solver-fluid1d-nutils/Fluid1D.py side=Left diff --git a/water-hammer/fluid1d-right-nutils/run.sh b/water-hammer/fluid1d-right-nutils/run.sh index e064d1e66..0593f1eeb 100755 --- a/water-hammer/fluid1d-right-nutils/run.sh +++ b/water-hammer/fluid1d-right-nutils/run.sh @@ -4,9 +4,12 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -python3 -m venv .venv -. .venv/bin/activate -pip install -r requirements.txt && pip freeze > pip-installed-packages.log +if [ ! -v PRECICE_TUTORIALS_NO_VENV ] +then + python3 -m venv .venv + . .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log +fi NUTILS_RICHOUTPUT=no python3 ../solver-fluid1d-nutils/Fluid1D.py side=Right From 47b9f35c3051853bdd89fc9793565bd6f5d1aae8 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Wed, 27 May 2026 22:25:44 +0200 Subject: [PATCH 21/94] Update reference versions (#809) --- tools/tests/reference_versions.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index 1b19d5676..34101e3e3 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -1,17 +1,17 @@ -PRECICE_REF: "d199bb3" # develop, January 20, 2026 +PRECICE_REF: "v3.4.1" PRECICE_PRESET: "production-audit" OPENFOAM_EXECUTABLE: "openfoam2312" -OPENFOAM_ADAPTER_REF: "v1.3.1" -PYTHON_BINDINGS_REF: "v3.2.0" -FENICS_ADAPTER_REF: "v2.2.0" -TUTORIALS_REF: "ab8eaceb42daebc6c4d7d8864c25997f983522e5" # May 27, 2026 +OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 +PYTHON_BINDINGS_REF: "v3.4.0" +FENICS_ADAPTER_REF: "v2.3.0" +TUTORIALS_REF: "b5e3046" # develop, May 27, 2026 PLATFORM: "ubuntu_2404" CALCULIX_VERSION: "2.20" CALCULIX_ADAPTER_REF: "v2.20.1" SU2_VERSION: "7.5.1" -SU2_ADAPTER_REF: "64d4aff" # Distribution v2404 -DEALII_ADAPTER_REF: "02c5d18" # develop, April 12, 2025 +SU2_ADAPTER_REF: "5abe79b" # develop, May 27, 2026 +DEALII_ADAPTER_REF: "a421d92" # develop, May 27, 2026 DUNE_VERSION: "2.9" DUMUX_VERSION: "3.7" -DUMUX_ADAPTER_REF: "0e914bb" # develop, March 18, 2026 -MICRO_MANAGER_REF: "v0.8.0" +DUMUX_ADAPTER_REF: "3f3f54f" # develop, May 27, 2026 +MICRO_MANAGER_REF: "v0.10.1" From f4130b0c14b29bf872605b2539f12ef21f649fa4 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Wed, 27 May 2026 23:36:42 +0200 Subject: [PATCH 22/94] Fix heat-exchanger max-time --- tools/tests/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index eff8426b3..fc3e423cc 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -109,7 +109,7 @@ test_suites: - fluid-inner-openfoam - fluid-outer-openfoam - solid-calculix - max_time_windows: 3 + max_time: 3 reference_result: ./heat-exchanger/reference-results/fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam.tar.gz heat-exchanger-simplified: From 568867a30cedf84d6908715c12a94ca3843c240a Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Wed, 27 May 2026 23:37:43 +0200 Subject: [PATCH 23/94] Update tutorials reference version --- tools/tests/reference_versions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index 34101e3e3..40e1dd20e 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -4,7 +4,7 @@ OPENFOAM_EXECUTABLE: "openfoam2312" OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 PYTHON_BINDINGS_REF: "v3.4.0" FENICS_ADAPTER_REF: "v2.3.0" -TUTORIALS_REF: "b5e3046" # develop, May 27, 2026 +TUTORIALS_REF: "f4130b0" # develop, May 27, 2026 PLATFORM: "ubuntu_2404" CALCULIX_VERSION: "2.20" CALCULIX_ADAPTER_REF: "v2.20.1" From 3af7d98e61ae6e1ec02681cd39eb065d185690f0 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 28 May 2026 09:08:09 +0200 Subject: [PATCH 24/94] Make the partitioned-pipe with sonicliquidfoam tutorial automatic (#810) --- .gitignore | 1 + changelog-entries/810.md | 1 + partitioned-pipe/README.md | 2 +- .../fluid1-openfoam-sonicliquidfoam/clean.sh | 6 ++++++ partitioned-pipe/fluid1-openfoam-sonicliquidfoam/run.sh | 9 +++++++++ .../fluid2-openfoam-sonicliquidfoam/clean.sh | 6 ++++++ partitioned-pipe/fluid2-openfoam-sonicliquidfoam/run.sh | 9 +++++++++ 7 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 changelog-entries/810.md diff --git a/.gitignore b/.gitignore index 4b7dafe69..1bec1510a 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ profiling.db profiling.csv trace.json profiling.pftrace +precice-config.xml.orig # C++ *.o diff --git a/changelog-entries/810.md b/changelog-entries/810.md new file mode 100644 index 000000000..93f67102c --- /dev/null +++ b/changelog-entries/810.md @@ -0,0 +1 @@ +- Automated the execution of the sonicliquidfoam participants in the partitioned-pipe tutorial. Manually editing the precice-config.xml is no longer required to run these cases. [#810](https://github.com/precice/tutorials/pull/810) \ No newline at end of file diff --git a/partitioned-pipe/README.md b/partitioned-pipe/README.md index 458a8709a..16a45c11a 100644 --- a/partitioned-pipe/README.md +++ b/partitioned-pipe/README.md @@ -27,7 +27,7 @@ Both for Fluid1 and Fluid2, the following participants are available: * OpenFOAM (pimpleFoam). An incompressible OpenFOAM solver. For more information, have a look at the [OpenFOAM adapter documentation](https://precice.org/adapter-openfoam-overview.html). -* OpenFOAM (sonicLiquidFoam). A compressible OpenFOAM solver. For more information, have a look at the [OpenFOAM adapter documentation](https://precice.org/adapter-openfoam-overview.html). +* OpenFOAM (sonicLiquidFoam). A compressible OpenFOAM solver. For more information, have a look at the [OpenFOAM adapter documentation](https://precice.org/adapter-openfoam-overview.html). This setup gives better results if we also couple the pressure gradient. The run scripts enable the respective comments in the `precice-config.xml`. ## Running the Simulation diff --git a/partitioned-pipe/fluid1-openfoam-sonicliquidfoam/clean.sh b/partitioned-pipe/fluid1-openfoam-sonicliquidfoam/clean.sh index b64fc5101..06860134e 100755 --- a/partitioned-pipe/fluid1-openfoam-sonicliquidfoam/clean.sh +++ b/partitioned-pipe/fluid1-openfoam-sonicliquidfoam/clean.sh @@ -4,3 +4,9 @@ set -e -u . ../../tools/cleaning-tools.sh clean_openfoam . + +if [ -f ../precice-config.xml.orig ]; then + echo "Restoring the precice-config.xml from precice-config.xml.orig" + cp -r ../precice-config.xml.orig ../precice-config.xml + rm ../precice-config.xml.orig +fi \ No newline at end of file diff --git a/partitioned-pipe/fluid1-openfoam-sonicliquidfoam/run.sh b/partitioned-pipe/fluid1-openfoam-sonicliquidfoam/run.sh index 8f55fbfa5..883849233 100755 --- a/partitioned-pipe/fluid1-openfoam-sonicliquidfoam/run.sh +++ b/partitioned-pipe/fluid1-openfoam-sonicliquidfoam/run.sh @@ -4,6 +4,15 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 +# The precice-config.xml has the PressureGradient field commented out. +# That improves the results for the compressible case, so we un-comment it. +if [ ! -f ../precice-config.xml.orig ]; then + echo "Modifying the ../precice-config.xml to enable PressureGradient (see precice-config.xml.orig for the original)" + cp -r ../precice-config.xml ../precice-config.xml.orig + sed -i "s,,,g" ../precice-config.xml +fi + blockMesh ../../tools/run-openfoam.sh "$@" diff --git a/partitioned-pipe/fluid2-openfoam-sonicliquidfoam/clean.sh b/partitioned-pipe/fluid2-openfoam-sonicliquidfoam/clean.sh index b64fc5101..06860134e 100755 --- a/partitioned-pipe/fluid2-openfoam-sonicliquidfoam/clean.sh +++ b/partitioned-pipe/fluid2-openfoam-sonicliquidfoam/clean.sh @@ -4,3 +4,9 @@ set -e -u . ../../tools/cleaning-tools.sh clean_openfoam . + +if [ -f ../precice-config.xml.orig ]; then + echo "Restoring the precice-config.xml from precice-config.xml.orig" + cp -r ../precice-config.xml.orig ../precice-config.xml + rm ../precice-config.xml.orig +fi \ No newline at end of file diff --git a/partitioned-pipe/fluid2-openfoam-sonicliquidfoam/run.sh b/partitioned-pipe/fluid2-openfoam-sonicliquidfoam/run.sh index 8f55fbfa5..883849233 100755 --- a/partitioned-pipe/fluid2-openfoam-sonicliquidfoam/run.sh +++ b/partitioned-pipe/fluid2-openfoam-sonicliquidfoam/run.sh @@ -4,6 +4,15 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 +# The precice-config.xml has the PressureGradient field commented out. +# That improves the results for the compressible case, so we un-comment it. +if [ ! -f ../precice-config.xml.orig ]; then + echo "Modifying the ../precice-config.xml to enable PressureGradient (see precice-config.xml.orig for the original)" + cp -r ../precice-config.xml ../precice-config.xml.orig + sed -i "s,,,g" ../precice-config.xml +fi + blockMesh ../../tools/run-openfoam.sh "$@" From e6ccc3b67896c25ceb956445eadb9d734b833dca Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 28 May 2026 09:22:57 +0200 Subject: [PATCH 25/94] Make some tests shorter --- tools/tests/tests.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index fc3e423cc..226e6e015 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -63,7 +63,7 @@ test_suites: case_combination: - fluid-openfoam - solid-nutils - max_time: 0.1 + max_time: 0.05 reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz - &flow-over-heated-plate_fluid-openfoam_solid-openfoam path: flow-over-heated-plate @@ -120,7 +120,7 @@ test_suites: - fluid-top-openfoam - fluid-bottom-openfoam - solid-calculix - max_time: 0.1 + max_time: 0.05 reference_result: ./heat-exchanger-simplified/reference-results/fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz multiple-perpendicular-flaps: @@ -158,7 +158,7 @@ test_suites: case_combination: - fluid1d-left-nutils - fluid3d-right-openfoam - max_time: 0.1 + max_time: 0.05 reference_result: ./partitioned-pipe-multiscale/reference-results/fluid1d-left-nutils_fluid3d-right-openfoam partitioned-pipe-two-phase: @@ -197,7 +197,7 @@ test_suites: case_combination: - fluid-openfoam - solid-openfoam - max_time: 0.1 + max_time: 0.03 reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz - &perpendicular-flap_fluid-su2_solid-fenics path: perpendicular-flap From af6bba08caa5924a5dd3ceb1f28ea3e8f27b90c9 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 28 May 2026 09:23:52 +0200 Subject: [PATCH 26/94] Make some parallel test cases serial, to avoid oversubscription Similar cases are still running in parallel. --- flow-over-heated-plate/metadata.yaml | 4 ++-- perpendicular-flap/metadata.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/flow-over-heated-plate/metadata.yaml b/flow-over-heated-plate/metadata.yaml index 5d01b9e4f..53bd7e3ab 100644 --- a/flow-over-heated-plate/metadata.yaml +++ b/flow-over-heated-plate/metadata.yaml @@ -10,7 +10,7 @@ cases: fluid-openfoam: participant: Fluid directory: ./fluid-openfoam - run: ./run.sh -parallel + run: ./run.sh component: openfoam-adapter solid-fenics: @@ -28,7 +28,7 @@ cases: solid-openfoam: participant: Solid directory: ./solid-openfoam - run: ./run.sh -parallel + run: ./run.sh component: openfoam-adapter diff --git a/perpendicular-flap/metadata.yaml b/perpendicular-flap/metadata.yaml index 57f255847..fd26b7d28 100644 --- a/perpendicular-flap/metadata.yaml +++ b/perpendicular-flap/metadata.yaml @@ -16,7 +16,7 @@ cases: fluid-openfoam: participant: Fluid directory: ./fluid-openfoam - run: ./run.sh -parallel + run: ./run.sh component: openfoam-adapter fluid-su2: @@ -52,7 +52,7 @@ cases: solid-openfoam: participant: Solid directory: ./solid-openfoam - run: ./run.sh -parallel + run: ./run.sh component: openfoam-adapter # solid-solids4foam: From b09c2e4b9733045a0b8792bccc55c62758966bf1 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 28 May 2026 09:24:52 +0200 Subject: [PATCH 27/94] Update TUTORIALS_REF --- tools/tests/reference_versions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index 40e1dd20e..88daae7a1 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -4,7 +4,7 @@ OPENFOAM_EXECUTABLE: "openfoam2312" OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 PYTHON_BINDINGS_REF: "v3.4.0" FENICS_ADAPTER_REF: "v2.3.0" -TUTORIALS_REF: "f4130b0" # develop, May 27, 2026 +TUTORIALS_REF: "af6bba0" # develop, May 28, 2026 PLATFORM: "ubuntu_2404" CALCULIX_VERSION: "2.20" CALCULIX_ADAPTER_REF: "v2.20.1" From 827b3c778f8871cae11b20d1fc15843a48f95521 Mon Sep 17 00:00:00 2001 From: preCICE Tests VM Date: Thu, 28 May 2026 09:49:03 +0200 Subject: [PATCH 28/94] Update reference results --- .../fluid-cpp_solid-cpp.tar.gz | 4 +- .../fluid-cpp_solid-python.tar.gz | 4 +- .../fluid-python_solid-python.tar.gz | 4 +- .../reference_results.metadata | 24 +++---- .../fluid-openfoam_solid-calculix.tar.gz | 4 +- .../fluid-openfoam_solid-fenics.tar.gz | 4 +- .../reference_results.metadata | 22 +++--- .../fluid-openfoam_solid-openfoam.tar.gz | 4 +- .../reference_results.metadata | 20 +++--- .../fluid-openfoam_solid-openfoam.tar.gz | 4 +- .../reference_results.metadata | 20 +++--- .../fluid-openfoam_solid-fenics.tar.gz | 4 +- .../fluid-openfoam_solid-nutils.tar.gz | 4 +- .../fluid-openfoam_solid-openfoam.tar.gz | 4 +- .../reference_results.metadata | 24 +++---- .../free-flow-dumux_porous-media-dumux.tar.gz | 4 +- .../reference_results.metadata | 20 +++--- ...luid-bottom-openfoam_solid-calculix.tar.gz | 4 +- .../reference_results.metadata | 20 +++--- ...solid-calculix_fluid-outer-openfoam.tar.gz | 3 + .../reference_results.metadata | 71 ++++++++++++++++++ ...ream-dealii_solid-downstream-dealii.tar.gz | 3 + .../reference_results.metadata | 20 +++--- .../reference_results.metadata | 71 ++++++++++++++++++ .../fluid1-openfoam_fluid2-openfoam.tar.gz | 3 + .../reference_results.metadata | 71 ++++++++++++++++++ ...mplefoam_fluid2-openfoam-pimplefoam.tar.gz | 3 + ...oam_fluid2-openfoam-sonicliquidfoam.tar.gz | 3 + .../reference_results.metadata | 72 +++++++++++++++++++ .../fluid-openfoam_solid-calculix.tar.gz | 4 +- .../fluid-openfoam_solid-dealii.tar.gz | 4 +- .../fluid-openfoam_solid-fenics.tar.gz | 4 +- .../fluid-openfoam_solid-openfoam.tar.gz | 4 +- .../fluid-su2_solid-fenics.tar.gz | 4 +- .../reference_results.metadata | 28 ++++---- .../fluid-openfoam_solid-cpp.tar.gz | 4 +- .../reference_results.metadata | 20 +++--- 37 files changed, 445 insertions(+), 145 deletions(-) create mode 100644 heat-exchanger/reference-results/fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam.tar.gz create mode 100644 heat-exchanger/reference-results/reference_results.metadata create mode 100644 multiple-perpendicular-flaps/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz create mode 100644 partitioned-pipe-multiscale/reference-results/reference_results.metadata create mode 100644 partitioned-pipe-two-phase/reference-results/fluid1-openfoam_fluid2-openfoam.tar.gz create mode 100644 partitioned-pipe-two-phase/reference-results/reference_results.metadata create mode 100644 partitioned-pipe/reference-results/fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam.tar.gz create mode 100644 partitioned-pipe/reference-results/fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam.tar.gz create mode 100644 partitioned-pipe/reference-results/reference_results.metadata diff --git a/elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz b/elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz index 16077f214..383c1474b 100644 --- a/elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz +++ b/elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d885192309f3ddfd72630f0ee2bf1c23e6eb5682573355046c3c48e6feb22bf -size 250515 +oid sha256:dcac24d624a65deb88db28700983d5e51034ffecee0ce531e77411826da5fab4 +size 250384 diff --git a/elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz b/elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz index 2e94cd824..c86ae5777 100644 --- a/elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz +++ b/elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7bb0f41035f24ab10689038f6f8c58ace7df4b953f29c35879452afbabe499ec -size 250667 +oid sha256:077fe088ea23154db78f9021fac0b2af14cebef781a0053ec1b832e7050d3b63 +size 25949 diff --git a/elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz b/elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz index e6ea59d82..795a4fee3 100644 --- a/elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz +++ b/elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a3a0532436ced9495b7a26e3b5b2a3cae589abe3928277586bc6495402d0654 -size 250733 +oid sha256:802a4f9d82320a220c89238fe6214b0befb3831f4e84743810d6a9853609de2a +size 250793 diff --git a/elastic-tube-1d/reference-results/reference_results.metadata b/elastic-tube-1d/reference-results/reference_results.metadata index 367dce573..5b9b66d5c 100644 --- a/elastic-tube-1d/reference-results/reference_results.metadata +++ b/elastic-tube-1d/reference-results/reference_results.metadata @@ -11,31 +11,31 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-cpp_solid-cpp.tar.gz | 2026-05-27 16:22:51 | 1d885192309f3ddfd72630f0ee2bf1c23e6eb5682573355046c3c48e6feb22bf | -| fluid-cpp_solid-python.tar.gz | 2026-05-27 16:22:51 | 7bb0f41035f24ab10689038f6f8c58ace7df4b953f29c35879452afbabe499ec | -| fluid-python_solid-python.tar.gz | 2026-05-27 16:22:51 | 0a3a0532436ced9495b7a26e3b5b2a3cae589abe3928277586bc6495402d0654 | +| fluid-python_solid-python.tar.gz | 2026-05-28 09:29:43 | 802a4f9d82320a220c89238fe6214b0befb3831f4e84743810d6a9853609de2a | +| fluid-cpp_solid-python.tar.gz | 2026-05-28 09:29:43 | 077fe088ea23154db78f9021fac0b2af14cebef781a0053ec1b832e7050d3b63 | +| fluid-cpp_solid-cpp.tar.gz | 2026-05-28 09:29:43 | dcac24d624a65deb88db28700983d5e51034ffecee0ce531e77411826da5fab4 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | d199bb3 | +| PRECICE_REF | v3.4.1 | | PRECICE_PRESET | production-audit | | OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | v1.3.1 | -| PYTHON_BINDINGS_REF | v3.2.0 | -| FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PYTHON_BINDINGS_REF | v3.4.0 | +| FENICS_ADAPTER_REF | v2.3.0 | +| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | | SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 64d4aff | -| DEALII_ADAPTER_REF | 02c5d18 | +| SU2_ADAPTER_REF | 5abe79b | +| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | -| DUMUX_ADAPTER_REF | 0e914bb | -| MICRO_MANAGER_REF | v0.8.0 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/elastic-tube-3d/reference-results/fluid-openfoam_solid-calculix.tar.gz b/elastic-tube-3d/reference-results/fluid-openfoam_solid-calculix.tar.gz index 1c6092064..c2122af64 100644 --- a/elastic-tube-3d/reference-results/fluid-openfoam_solid-calculix.tar.gz +++ b/elastic-tube-3d/reference-results/fluid-openfoam_solid-calculix.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dcd773b4c173936f5214a481991dec05b7d8fbdd5ad4136f6cb2a68839303fed -size 1100941 +oid sha256:bb945c0935c53894eabc1bb21c38386603ab88a3d46c5add9c9c52e2c8735608 +size 1100829 diff --git a/elastic-tube-3d/reference-results/fluid-openfoam_solid-fenics.tar.gz b/elastic-tube-3d/reference-results/fluid-openfoam_solid-fenics.tar.gz index 8a7346a5d..2916e65c0 100644 --- a/elastic-tube-3d/reference-results/fluid-openfoam_solid-fenics.tar.gz +++ b/elastic-tube-3d/reference-results/fluid-openfoam_solid-fenics.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9fb5a39115545cf8d3b160662c1e330dc8000a87a23b3169a655a390f61eaa8e -size 461799 +oid sha256:3f408f5cf707aa1d2bffe17313ef5f91c5cc4803c266988d25358b8d74d4cc36 +size 461399 diff --git a/elastic-tube-3d/reference-results/reference_results.metadata b/elastic-tube-3d/reference-results/reference_results.metadata index 832c3efd7..eb31c9ab8 100644 --- a/elastic-tube-3d/reference-results/reference_results.metadata +++ b/elastic-tube-3d/reference-results/reference_results.metadata @@ -11,30 +11,30 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-fenics.tar.gz | 2026-05-27 19:16:34 | 9fb5a39115545cf8d3b160662c1e330dc8000a87a23b3169a655a390f61eaa8e | -| fluid-openfoam_solid-calculix.tar.gz | 2026-05-27 19:16:34 | dcd773b4c173936f5214a481991dec05b7d8fbdd5ad4136f6cb2a68839303fed | +| fluid-openfoam_solid-fenics.tar.gz | 2026-05-28 09:29:43 | 3f408f5cf707aa1d2bffe17313ef5f91c5cc4803c266988d25358b8d74d4cc36 | +| fluid-openfoam_solid-calculix.tar.gz | 2026-05-28 09:29:43 | bb945c0935c53894eabc1bb21c38386603ab88a3d46c5add9c9c52e2c8735608 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | d199bb3 | +| PRECICE_REF | v3.4.1 | | PRECICE_PRESET | production-audit | | OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | v1.3.1 | -| PYTHON_BINDINGS_REF | v3.2.0 | -| FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | ab8eaceb42daebc6c4d7d8864c25997f983522e5 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PYTHON_BINDINGS_REF | v3.4.0 | +| FENICS_ADAPTER_REF | v2.3.0 | +| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | | SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 64d4aff | -| DEALII_ADAPTER_REF | 02c5d18 | +| SU2_ADAPTER_REF | 5abe79b | +| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | -| DUMUX_ADAPTER_REF | 0e914bb | -| MICRO_MANAGER_REF | v0.8.0 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz b/flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz index 37bf7e136..b15205330 100644 --- a/flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz +++ b/flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:45b44d1a508dddb1cea4bdfacca4fc4c37adbcac0dfef30b2a8909a2e24eeeb9 -size 1374041 +oid sha256:9747357ae3ab382c43c4970503f4fea6f33fcf0cfe4c519cf5e6c23f9e95ed55 +size 59051 diff --git a/flow-over-heated-plate-nearest-projection/reference-results/reference_results.metadata b/flow-over-heated-plate-nearest-projection/reference-results/reference_results.metadata index 58764a010..df44d9fb5 100644 --- a/flow-over-heated-plate-nearest-projection/reference-results/reference_results.metadata +++ b/flow-over-heated-plate-nearest-projection/reference-results/reference_results.metadata @@ -11,29 +11,29 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-27 16:22:51 | 45b44d1a508dddb1cea4bdfacca4fc4c37adbcac0dfef30b2a8909a2e24eeeb9 | +| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-28 09:29:43 | 9747357ae3ab382c43c4970503f4fea6f33fcf0cfe4c519cf5e6c23f9e95ed55 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | d199bb3 | +| PRECICE_REF | v3.4.1 | | PRECICE_PRESET | production-audit | | OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | v1.3.1 | -| PYTHON_BINDINGS_REF | v3.2.0 | -| FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PYTHON_BINDINGS_REF | v3.4.0 | +| FENICS_ADAPTER_REF | v2.3.0 | +| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | | SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 64d4aff | -| DEALII_ADAPTER_REF | 02c5d18 | +| SU2_ADAPTER_REF | 5abe79b | +| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | -| DUMUX_ADAPTER_REF | 0e914bb | -| MICRO_MANAGER_REF | v0.8.0 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz b/flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz index 4d455551e..425b51a21 100644 --- a/flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz +++ b/flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:965cdf279468c619e87df7c07986d97e8746c5d56f4690e3f77356e567b0bc8f -size 468654 +oid sha256:6c84deec0fe351b2ba2353097a2ae4b8fa576cad82bd93a5f91bf2eec1d3af0a +size 40791 diff --git a/flow-over-heated-plate-two-meshes/reference-results/reference_results.metadata b/flow-over-heated-plate-two-meshes/reference-results/reference_results.metadata index d47f1589f..d91d1c197 100644 --- a/flow-over-heated-plate-two-meshes/reference-results/reference_results.metadata +++ b/flow-over-heated-plate-two-meshes/reference-results/reference_results.metadata @@ -11,29 +11,29 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-27 16:22:51 | 965cdf279468c619e87df7c07986d97e8746c5d56f4690e3f77356e567b0bc8f | +| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-28 09:29:43 | 6c84deec0fe351b2ba2353097a2ae4b8fa576cad82bd93a5f91bf2eec1d3af0a | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | d199bb3 | +| PRECICE_REF | v3.4.1 | | PRECICE_PRESET | production-audit | | OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | v1.3.1 | -| PYTHON_BINDINGS_REF | v3.2.0 | -| FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PYTHON_BINDINGS_REF | v3.4.0 | +| FENICS_ADAPTER_REF | v2.3.0 | +| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | | SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 64d4aff | -| DEALII_ADAPTER_REF | 02c5d18 | +| SU2_ADAPTER_REF | 5abe79b | +| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | -| DUMUX_ADAPTER_REF | 0e914bb | -| MICRO_MANAGER_REF | v0.8.0 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz index 09326bf39..5a54408b2 100644 --- a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz +++ b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:428d220b36ae6c42a8c85ff321e0f7c2266ce3c7454f8bdd90b17e80504e8634 -size 214096 +oid sha256:e360b6b410c119b360c08a243fe18e8ec28893f3ea37cd33f229858cd35ad465 +size 214056 diff --git a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz index 5e4b4295c..4a800b29a 100644 --- a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz +++ b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8297a45ebb39d68aace1fc2ed6d88a93ed6b1a0bb529c8d7be557088c0f85beb -size 146863 +oid sha256:56d6090748bd9823d24074a22161136e0616cfd7c7bb51a84afaba6353c1f9d9 +size 6999 diff --git a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz index c1c7da21d..a7b8d4370 100644 --- a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz +++ b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:755ffddc08446524905d6eca45eb092bc65014dbf1547b4b60147dc9ce057856 -size 136231 +oid sha256:fbb69f5a8383fd0d9829c717ebde32133e9bcaad4cefd234d22664e2811429eb +size 136144 diff --git a/flow-over-heated-plate/reference-results/reference_results.metadata b/flow-over-heated-plate/reference-results/reference_results.metadata index 6f39d54ee..a169473b5 100644 --- a/flow-over-heated-plate/reference-results/reference_results.metadata +++ b/flow-over-heated-plate/reference-results/reference_results.metadata @@ -11,31 +11,31 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-fenics.tar.gz | 2026-05-27 16:22:51 | 428d220b36ae6c42a8c85ff321e0f7c2266ce3c7454f8bdd90b17e80504e8634 | -| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-27 16:22:51 | 755ffddc08446524905d6eca45eb092bc65014dbf1547b4b60147dc9ce057856 | -| fluid-openfoam_solid-nutils.tar.gz | 2026-05-27 16:22:51 | 8297a45ebb39d68aace1fc2ed6d88a93ed6b1a0bb529c8d7be557088c0f85beb | +| fluid-openfoam_solid-fenics.tar.gz | 2026-05-28 09:29:43 | e360b6b410c119b360c08a243fe18e8ec28893f3ea37cd33f229858cd35ad465 | +| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-28 09:29:43 | fbb69f5a8383fd0d9829c717ebde32133e9bcaad4cefd234d22664e2811429eb | +| fluid-openfoam_solid-nutils.tar.gz | 2026-05-28 09:29:43 | 56d6090748bd9823d24074a22161136e0616cfd7c7bb51a84afaba6353c1f9d9 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | d199bb3 | +| PRECICE_REF | v3.4.1 | | PRECICE_PRESET | production-audit | | OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | v1.3.1 | -| PYTHON_BINDINGS_REF | v3.2.0 | -| FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PYTHON_BINDINGS_REF | v3.4.0 | +| FENICS_ADAPTER_REF | v2.3.0 | +| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | | SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 64d4aff | -| DEALII_ADAPTER_REF | 02c5d18 | +| SU2_ADAPTER_REF | 5abe79b | +| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | -| DUMUX_ADAPTER_REF | 0e914bb | -| MICRO_MANAGER_REF | v0.8.0 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz b/free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz index 31cbfc931..27bbbc29f 100644 --- a/free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz +++ b/free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b802da5d2e5ed73a35678837a134ee1a0b0f5b09593bfb6477c2d52c10fc290c -size 1588 +oid sha256:f08bd2862c7a656b91c6eadb989ea6e0d5a81dd658b569ee390a170ff574bad5 +size 1582 diff --git a/free-flow-over-porous-media/reference-results/reference_results.metadata b/free-flow-over-porous-media/reference-results/reference_results.metadata index 517a6ec55..7701e8342 100644 --- a/free-flow-over-porous-media/reference-results/reference_results.metadata +++ b/free-flow-over-porous-media/reference-results/reference_results.metadata @@ -11,29 +11,29 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| free-flow-dumux_porous-media-dumux.tar.gz | 2026-05-27 16:22:51 | b802da5d2e5ed73a35678837a134ee1a0b0f5b09593bfb6477c2d52c10fc290c | +| free-flow-dumux_porous-media-dumux.tar.gz | 2026-05-28 09:29:43 | f08bd2862c7a656b91c6eadb989ea6e0d5a81dd658b569ee390a170ff574bad5 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | d199bb3 | +| PRECICE_REF | v3.4.1 | | PRECICE_PRESET | production-audit | | OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | v1.3.1 | -| PYTHON_BINDINGS_REF | v3.2.0 | -| FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PYTHON_BINDINGS_REF | v3.4.0 | +| FENICS_ADAPTER_REF | v2.3.0 | +| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | | SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 64d4aff | -| DEALII_ADAPTER_REF | 02c5d18 | +| SU2_ADAPTER_REF | 5abe79b | +| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | -| DUMUX_ADAPTER_REF | 0e914bb | -| MICRO_MANAGER_REF | v0.8.0 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/heat-exchanger-simplified/reference-results/fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz b/heat-exchanger-simplified/reference-results/fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz index 3f8ea56c2..de51bdf3a 100644 --- a/heat-exchanger-simplified/reference-results/fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz +++ b/heat-exchanger-simplified/reference-results/fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9787ee7b41e7f896e6946512f3001beaa512c158726423bc76b47517c913005f -size 863801 +oid sha256:4a80d4c65362f87483056145f7640198c5294cf76fdd1805951d2e733e940f0d +size 43254 diff --git a/heat-exchanger-simplified/reference-results/reference_results.metadata b/heat-exchanger-simplified/reference-results/reference_results.metadata index 1c88480ff..fdbfb365e 100644 --- a/heat-exchanger-simplified/reference-results/reference_results.metadata +++ b/heat-exchanger-simplified/reference-results/reference_results.metadata @@ -11,29 +11,29 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz | 2026-05-27 16:22:51 | 9787ee7b41e7f896e6946512f3001beaa512c158726423bc76b47517c913005f | +| fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz | 2026-05-28 09:29:43 | 4a80d4c65362f87483056145f7640198c5294cf76fdd1805951d2e733e940f0d | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | d199bb3 | +| PRECICE_REF | v3.4.1 | | PRECICE_PRESET | production-audit | | OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | v1.3.1 | -| PYTHON_BINDINGS_REF | v3.2.0 | -| FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PYTHON_BINDINGS_REF | v3.4.0 | +| FENICS_ADAPTER_REF | v2.3.0 | +| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | | SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 64d4aff | -| DEALII_ADAPTER_REF | 02c5d18 | +| SU2_ADAPTER_REF | 5abe79b | +| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | -| DUMUX_ADAPTER_REF | 0e914bb | -| MICRO_MANAGER_REF | v0.8.0 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/heat-exchanger/reference-results/fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam.tar.gz b/heat-exchanger/reference-results/fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam.tar.gz new file mode 100644 index 000000000..2e108d13b --- /dev/null +++ b/heat-exchanger/reference-results/fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d7a810976a3d60e8455f31f7af2b773cdd75fedfe495c15184c2b4a6ac7ee4c4 +size 13167651 diff --git a/heat-exchanger/reference-results/reference_results.metadata b/heat-exchanger/reference-results/reference_results.metadata new file mode 100644 index 000000000..e9957eaa4 --- /dev/null +++ b/heat-exchanger/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam.tar.gz | 2026-05-28 09:29:43 | d7a810976a3d60e8455f31f7af2b773cdd75fedfe495c15184c2b4a6ac7ee4c4 | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PRECICE_REF | v3.4.1 | +| PRECICE_PRESET | production-audit | +| OPENFOAM_EXECUTABLE | openfoam2312 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PYTHON_BINDINGS_REF | v3.4.0 | +| FENICS_ADAPTER_REF | v2.3.0 | +| TUTORIALS_REF | af6bba0 | +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| SU2_VERSION | 7.5.1 | +| SU2_ADAPTER_REF | 5abe79b | +| DEALII_ADAPTER_REF | a421d92 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/multiple-perpendicular-flaps/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz b/multiple-perpendicular-flaps/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz new file mode 100644 index 000000000..8eea5b00a --- /dev/null +++ b/multiple-perpendicular-flaps/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8ae5e3ac120708db57069704180f20b2a8fb749183d4d921af9c9361c2ab9c4 +size 108231 diff --git a/multiple-perpendicular-flaps/reference-results/reference_results.metadata b/multiple-perpendicular-flaps/reference-results/reference_results.metadata index 9df445e04..c109b3a4e 100644 --- a/multiple-perpendicular-flaps/reference-results/reference_results.metadata +++ b/multiple-perpendicular-flaps/reference-results/reference_results.metadata @@ -11,29 +11,29 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz | 2026-05-27 16:22:51 | d4bfc7abd3e14ae860d676d426acefc2d998bcbbb2ce79a189cadfc2aa7d3a65 | +| fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz | 2026-05-28 09:29:43 | b8ae5e3ac120708db57069704180f20b2a8fb749183d4d921af9c9361c2ab9c4 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | d199bb3 | +| PRECICE_REF | v3.4.1 | | PRECICE_PRESET | production-audit | | OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | v1.3.1 | -| PYTHON_BINDINGS_REF | v3.2.0 | -| FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PYTHON_BINDINGS_REF | v3.4.0 | +| FENICS_ADAPTER_REF | v2.3.0 | +| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | | SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 64d4aff | -| DEALII_ADAPTER_REF | 02c5d18 | +| SU2_ADAPTER_REF | 5abe79b | +| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | -| DUMUX_ADAPTER_REF | 0e914bb | -| MICRO_MANAGER_REF | v0.8.0 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/partitioned-pipe-multiscale/reference-results/reference_results.metadata b/partitioned-pipe-multiscale/reference-results/reference_results.metadata new file mode 100644 index 000000000..8ca6d0077 --- /dev/null +++ b/partitioned-pipe-multiscale/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| fluid1d-left-nutils_fluid3d-right-openfoam | 2026-05-28 09:29:43 | 07d0360a28c03e58b410dbe858ceaa2c4e16a1f234c802e49a1d34ecbd201759 | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PRECICE_REF | v3.4.1 | +| PRECICE_PRESET | production-audit | +| OPENFOAM_EXECUTABLE | openfoam2312 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PYTHON_BINDINGS_REF | v3.4.0 | +| FENICS_ADAPTER_REF | v2.3.0 | +| TUTORIALS_REF | af6bba0 | +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| SU2_VERSION | 7.5.1 | +| SU2_ADAPTER_REF | 5abe79b | +| DEALII_ADAPTER_REF | a421d92 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/partitioned-pipe-two-phase/reference-results/fluid1-openfoam_fluid2-openfoam.tar.gz b/partitioned-pipe-two-phase/reference-results/fluid1-openfoam_fluid2-openfoam.tar.gz new file mode 100644 index 000000000..534ea80a2 --- /dev/null +++ b/partitioned-pipe-two-phase/reference-results/fluid1-openfoam_fluid2-openfoam.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:88d5e0bc64adc71a1d2bd94f15d43fb0f75935eb1a727153edf0cbd732f26319 +size 2523 diff --git a/partitioned-pipe-two-phase/reference-results/reference_results.metadata b/partitioned-pipe-two-phase/reference-results/reference_results.metadata new file mode 100644 index 000000000..45b69aaa8 --- /dev/null +++ b/partitioned-pipe-two-phase/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| fluid1-openfoam_fluid2-openfoam.tar.gz | 2026-05-28 09:29:43 | 88d5e0bc64adc71a1d2bd94f15d43fb0f75935eb1a727153edf0cbd732f26319 | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PRECICE_REF | v3.4.1 | +| PRECICE_PRESET | production-audit | +| OPENFOAM_EXECUTABLE | openfoam2312 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PYTHON_BINDINGS_REF | v3.4.0 | +| FENICS_ADAPTER_REF | v2.3.0 | +| TUTORIALS_REF | af6bba0 | +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| SU2_VERSION | 7.5.1 | +| SU2_ADAPTER_REF | 5abe79b | +| DEALII_ADAPTER_REF | a421d92 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/partitioned-pipe/reference-results/fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam.tar.gz b/partitioned-pipe/reference-results/fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam.tar.gz new file mode 100644 index 000000000..f36fdeb19 --- /dev/null +++ b/partitioned-pipe/reference-results/fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74aaef5b7bdeb2999e947a6d67c3c5cdc39489bb7f0eedf1ddbf6bcfe4046c84 +size 253157 diff --git a/partitioned-pipe/reference-results/fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam.tar.gz b/partitioned-pipe/reference-results/fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam.tar.gz new file mode 100644 index 000000000..6f3e512bf --- /dev/null +++ b/partitioned-pipe/reference-results/fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0844a705c909f2525f6d126fc76c52f506f1313a0f38a8dec1e4a3a99a5b2c32 +size 377146 diff --git a/partitioned-pipe/reference-results/reference_results.metadata b/partitioned-pipe/reference-results/reference_results.metadata new file mode 100644 index 000000000..0791a9eaf --- /dev/null +++ b/partitioned-pipe/reference-results/reference_results.metadata @@ -0,0 +1,72 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam.tar.gz | 2026-05-28 09:29:43 | 74aaef5b7bdeb2999e947a6d67c3c5cdc39489bb7f0eedf1ddbf6bcfe4046c84 | +| fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam.tar.gz | 2026-05-28 09:29:43 | 0844a705c909f2525f6d126fc76c52f506f1313a0f38a8dec1e4a3a99a5b2c32 | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PRECICE_REF | v3.4.1 | +| PRECICE_PRESET | production-audit | +| OPENFOAM_EXECUTABLE | openfoam2312 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PYTHON_BINDINGS_REF | v3.4.0 | +| FENICS_ADAPTER_REF | v2.3.0 | +| TUTORIALS_REF | af6bba0 | +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| SU2_VERSION | 7.5.1 | +| SU2_ADAPTER_REF | 5abe79b | +| DEALII_ADAPTER_REF | a421d92 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz b/perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz index db6565804..637d9286c 100644 --- a/perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz +++ b/perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:993ac60ae095bfc5e1e7ce8157c12c6d20093800eac91871b078cda83272e02b -size 4767902 +oid sha256:092c7c3f286c6b757bad44a9ac8bdb33d11c9f934045d806f1868a41142aeb88 +size 4768778 diff --git a/perpendicular-flap/reference-results/fluid-openfoam_solid-dealii.tar.gz b/perpendicular-flap/reference-results/fluid-openfoam_solid-dealii.tar.gz index 8f9feeab9..98b3ea9b1 100644 --- a/perpendicular-flap/reference-results/fluid-openfoam_solid-dealii.tar.gz +++ b/perpendicular-flap/reference-results/fluid-openfoam_solid-dealii.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f15b2c7bbae2a678d19fc65fe86b6b5a529f06f0313d0a340f30c0079d0c0cc8 -size 3168535 +oid sha256:1ee47dd459486ef149ef1583a08b605bf6f02a16e48727d15519c6f18d53bc61 +size 3168395 diff --git a/perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz b/perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz index 604bb708f..d2d7e6c2e 100644 --- a/perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz +++ b/perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f46e5ef72829f3fb68dfc6ec2b5db377d881d5446b2a1e283e718ee8bc2f225 -size 1388744 +oid sha256:ca9af8314a88b4542ffdfcba9314f35d6014d80ef1a7751990ff725d5881cb00 +size 31241 diff --git a/perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz b/perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz index 2a4d504c3..8b536918e 100644 --- a/perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz +++ b/perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:afa2b5e7c1ce1dbe0457e78fa96a7250a6d10c31d51284722ed0f2d6079e74f2 -size 952670 +oid sha256:7e83659cc1321057b41b92bc6bf68d38cda0331095ee87c650611dff076d6ea9 +size 4556 diff --git a/perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz b/perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz index 882b29df5..4815b7f6a 100644 --- a/perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz +++ b/perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1b56464502e14c212d8de8ea8321a2c640b20b3279c16e8ec0770f78e5db4e5b -size 1435421 +oid sha256:69fca563854a1ca1ef5d2f5ec56a231e22c3d3f09af2a4b635430ab37fd6f670 +size 32193 diff --git a/perpendicular-flap/reference-results/reference_results.metadata b/perpendicular-flap/reference-results/reference_results.metadata index 7df523318..e5fd1de4d 100644 --- a/perpendicular-flap/reference-results/reference_results.metadata +++ b/perpendicular-flap/reference-results/reference_results.metadata @@ -11,33 +11,33 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-27 16:22:51 | afa2b5e7c1ce1dbe0457e78fa96a7250a6d10c31d51284722ed0f2d6079e74f2 | -| fluid-su2_solid-fenics.tar.gz | 2026-05-27 16:22:51 | 1b56464502e14c212d8de8ea8321a2c640b20b3279c16e8ec0770f78e5db4e5b | -| fluid-openfoam_solid-calculix.tar.gz | 2026-05-27 16:22:51 | 993ac60ae095bfc5e1e7ce8157c12c6d20093800eac91871b078cda83272e02b | -| fluid-openfoam_solid-fenics.tar.gz | 2026-05-27 16:22:51 | 2f46e5ef72829f3fb68dfc6ec2b5db377d881d5446b2a1e283e718ee8bc2f225 | -| fluid-openfoam_solid-dealii.tar.gz | 2026-05-27 16:22:51 | f15b2c7bbae2a678d19fc65fe86b6b5a529f06f0313d0a340f30c0079d0c0cc8 | +| fluid-openfoam_solid-calculix.tar.gz | 2026-05-28 09:29:43 | 092c7c3f286c6b757bad44a9ac8bdb33d11c9f934045d806f1868a41142aeb88 | +| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-28 09:29:43 | 7e83659cc1321057b41b92bc6bf68d38cda0331095ee87c650611dff076d6ea9 | +| fluid-openfoam_solid-fenics.tar.gz | 2026-05-28 09:29:43 | ca9af8314a88b4542ffdfcba9314f35d6014d80ef1a7751990ff725d5881cb00 | +| fluid-openfoam_solid-dealii.tar.gz | 2026-05-28 09:29:43 | 1ee47dd459486ef149ef1583a08b605bf6f02a16e48727d15519c6f18d53bc61 | +| fluid-su2_solid-fenics.tar.gz | 2026-05-28 09:29:43 | 69fca563854a1ca1ef5d2f5ec56a231e22c3d3f09af2a4b635430ab37fd6f670 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | d199bb3 | +| PRECICE_REF | v3.4.1 | | PRECICE_PRESET | production-audit | | OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | v1.3.1 | -| PYTHON_BINDINGS_REF | v3.2.0 | -| FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PYTHON_BINDINGS_REF | v3.4.0 | +| FENICS_ADAPTER_REF | v2.3.0 | +| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | | SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 64d4aff | -| DEALII_ADAPTER_REF | 02c5d18 | +| SU2_ADAPTER_REF | 5abe79b | +| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | -| DUMUX_ADAPTER_REF | 0e914bb | -| MICRO_MANAGER_REF | v0.8.0 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz b/quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz index 72750b518..3fa978a18 100644 --- a/quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz +++ b/quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50f19459abea97a3d2ecfd10193f16b3e2b32f3b6ca5bdaa9ef233838ee1524f -size 199034 +oid sha256:6b068911de262c02b25f869c503992d15cb32e3d011c214d8eca7bc2306ec197 +size 198946 diff --git a/quickstart/reference-results/reference_results.metadata b/quickstart/reference-results/reference_results.metadata index 03a21a105..c9f2e0c7e 100644 --- a/quickstart/reference-results/reference_results.metadata +++ b/quickstart/reference-results/reference_results.metadata @@ -11,29 +11,29 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-cpp.tar.gz | 2026-05-27 16:22:51 | 50f19459abea97a3d2ecfd10193f16b3e2b32f3b6ca5bdaa9ef233838ee1524f | +| fluid-openfoam_solid-cpp.tar.gz | 2026-05-28 09:29:43 | 6b068911de262c02b25f869c503992d15cb32e3d011c214d8eca7bc2306ec197 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | d199bb3 | +| PRECICE_REF | v3.4.1 | | PRECICE_PRESET | production-audit | | OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | v1.3.1 | -| PYTHON_BINDINGS_REF | v3.2.0 | -| FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | aba048a5a36806cc2fd5a768e13174b82f365822 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PYTHON_BINDINGS_REF | v3.4.0 | +| FENICS_ADAPTER_REF | v2.3.0 | +| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | | CALCULIX_ADAPTER_REF | v2.20.1 | | SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 64d4aff | -| DEALII_ADAPTER_REF | 02c5d18 | +| SU2_ADAPTER_REF | 5abe79b | +| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | -| DUMUX_ADAPTER_REF | 0e914bb | -| MICRO_MANAGER_REF | v0.8.0 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine From 12552f7688e6bd1f3c20a849a177a347e7e3c5e7 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 28 May 2026 11:40:21 +0200 Subject: [PATCH 29/94] Restructure reference_versions.yaml and remove dependencies from workflow calls (#812) --- .../system-tests-latest-components.yml | 4 --- .github/workflows/system-tests-pr.yml | 4 --- changelog-entries/812.md | 1 + tools/tests/reference_versions.yaml | 34 ++++++++++++------- 4 files changed, 23 insertions(+), 20 deletions(-) create mode 100644 changelog-entries/812.md diff --git a/.github/workflows/system-tests-latest-components.yml b/.github/workflows/system-tests-latest-components.yml index a68b8639c..1f30e6097 100644 --- a/.github/workflows/system-tests-latest-components.yml +++ b/.github/workflows/system-tests-latest-components.yml @@ -166,15 +166,11 @@ jobs: build_args: "PLATFORM:ubuntu_2404,\ PRECICE_REF:${{ needs.gather-refs.outputs.ref-precice }},\ PYTHON_BINDINGS_REF:${{ needs.gather-refs.outputs.ref-python-bindings }},\ - CALCULIX_VERSION:2.20,\ CALCULIX_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-calculix-adapter }},\ - DUMUX_VERSION:3.7,\ DUMUX_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-dumux-adapter }},\ FENICS_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-fenics-adapter }},\ MICRO_MANAGER_REF:${{ needs.gather-refs.outputs.ref-micro-manager }},\ - OPENFOAM_EXECUTABLE:openfoam2512,\ OPENFOAM_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-openfoam-adapter }},\ - SU2_VERSION:7.5.1,\ SU2_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-su2-adapter }},\ TUTORIALS_REF:${{ needs.gather-refs.outputs.ref-tutorials }}" system_tests_branch: develop diff --git a/.github/workflows/system-tests-pr.yml b/.github/workflows/system-tests-pr.yml index 39bd92501..f59b6fc70 100644 --- a/.github/workflows/system-tests-pr.yml +++ b/.github/workflows/system-tests-pr.yml @@ -154,15 +154,11 @@ jobs: build_args: "PLATFORM:ubuntu_2404,\ PRECICE_REF:${{ needs.gather-refs.outputs.ref-precice }},\ PYTHON_BINDINGS_REF:${{ needs.gather-refs.outputs.ref-python-bindings }},\ - CALCULIX_VERSION:2.20,\ CALCULIX_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-calculix-adapter }},\ - DUMUX_VERSION:3.7,\ DUMUX_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-dumux-adapter }},\ FENICS_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-fenics-adapter }},\ MICRO_MANAGER_REF:${{ needs.gather-refs.outputs.ref-micro-manager }},\ - OPENFOAM_EXECUTABLE:openfoam2512,\ OPENFOAM_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-openfoam-adapter }},\ - SU2_VERSION:7.5.1,\ SU2_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-su2-adapter }},\ TUTORIALS_PR:${{ github.event.number }},\ TUTORIALS_REF:${{ github.event.pull_request.head.sha }}" diff --git a/changelog-entries/812.md b/changelog-entries/812.md new file mode 100644 index 000000000..aeaed428c --- /dev/null +++ b/changelog-entries/812.md @@ -0,0 +1 @@ +- Removed obsolete version default overrides in the system tests CI workflows [#812](https://github.com/precice/tutorials/pull/812). \ No newline at end of file diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index 88daae7a1..3a4e4077f 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -1,17 +1,27 @@ -PRECICE_REF: "v3.4.1" -PRECICE_PRESET: "production-audit" -OPENFOAM_EXECUTABLE: "openfoam2312" -OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 -PYTHON_BINDINGS_REF: "v3.4.0" -FENICS_ADAPTER_REF: "v2.3.0" -TUTORIALS_REF: "af6bba0" # develop, May 28, 2026 +# Versions used by the generate_reference_results.py script, +# and default values for unspecified values in systemtests.py. + +# Dockerfile used (See dockerfiles/) PLATFORM: "ubuntu_2404" + +# Third-party dependencies CALCULIX_VERSION: "2.20" -CALCULIX_ADAPTER_REF: "v2.20.1" -SU2_VERSION: "7.5.1" -SU2_ADAPTER_REF: "5abe79b" # develop, May 27, 2026 -DEALII_ADAPTER_REF: "a421d92" # develop, May 27, 2026 DUNE_VERSION: "2.9" DUMUX_VERSION: "3.7" -DUMUX_ADAPTER_REF: "3f3f54f" # develop, May 27, 2026 +OPENFOAM_EXECUTABLE: "openfoam2512" +SU2_VERSION: "7.5.1" + +# Tested components +FENICS_ADAPTER_REF: "v2.3.0" +CALCULIX_ADAPTER_REF: "v2.20.1" +DEALII_ADAPTER_REF: "a421d92" # develop, May 27, 2026 +DUMUX_ADAPTER_REF: "3f3f54f" # develop, May 27, 2026 MICRO_MANAGER_REF: "v0.10.1" +OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 +PRECICE_REF: "v3.4.1" +PYTHON_BINDINGS_REF: "v3.4.0" +SU2_ADAPTER_REF: "5abe79b" # develop, May 27, 2026 +TUTORIALS_REF: "af6bba0" # develop, May 28, 2026 + +# Additional settings +PRECICE_PRESET: "production-audit" \ No newline at end of file From 9cb706825a78cae224e427c436fefa875db45e77 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 28 May 2026 11:42:45 +0200 Subject: [PATCH 30/94] Prune Docker networks at the end of generate_reference_results_workflow.yml Similarly to the run_testsuite_workflow.yml --- .github/workflows/generate_reference_results_workflow.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/generate_reference_results_workflow.yml b/.github/workflows/generate_reference_results_workflow.yml index d2670666e..2b2e98964 100644 --- a/.github/workflows/generate_reference_results_workflow.yml +++ b/.github/workflows/generate_reference_results_workflow.yml @@ -104,3 +104,6 @@ jobs: runs/*/system-tests-stdout.log runs/*/system-tests-stderr.log runs/*/*/system-tests_*.log + - name: tidy up the docker + run: | + docker network prune -f From 2d2e62c1eb5ce8b6c773f9fae0501b355ff3d074 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 28 May 2026 11:43:24 +0200 Subject: [PATCH 31/94] Update TUTORIALS_REF --- tools/tests/reference_versions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index 3a4e4077f..13e8f9f0c 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -21,7 +21,7 @@ OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 PRECICE_REF: "v3.4.1" PYTHON_BINDINGS_REF: "v3.4.0" SU2_ADAPTER_REF: "5abe79b" # develop, May 27, 2026 -TUTORIALS_REF: "af6bba0" # develop, May 28, 2026 +TUTORIALS_REF: "9cb7068" # develop, May 28, 2026 # Additional settings PRECICE_PRESET: "production-audit" \ No newline at end of file From 1f9d68c09b71e01be86f8ffd795775b5c8213adf Mon Sep 17 00:00:00 2001 From: preCICE Tests VM Date: Thu, 28 May 2026 12:08:20 +0200 Subject: [PATCH 32/94] Update reference results --- .../fluid-cpp_solid-cpp.tar.gz | 4 +-- .../fluid-cpp_solid-python.tar.gz | 4 +-- .../fluid-python_solid-python.tar.gz | 4 +-- .../reference_results.metadata | 28 ++++++++-------- .../fluid-openfoam_solid-calculix.tar.gz | 4 +-- .../fluid-openfoam_solid-fenics.tar.gz | 4 +-- .../reference_results.metadata | 26 +++++++-------- .../fluid-openfoam_solid-openfoam.tar.gz | 4 +-- .../reference_results.metadata | 24 +++++++------- .../fluid-openfoam_solid-openfoam.tar.gz | 4 +-- .../reference_results.metadata | 24 +++++++------- .../fluid-openfoam_solid-fenics.tar.gz | 4 +-- .../fluid-openfoam_solid-nutils.tar.gz | 4 +-- .../fluid-openfoam_solid-openfoam.tar.gz | 4 +-- .../reference_results.metadata | 28 ++++++++-------- .../free-flow-dumux_porous-media-dumux.tar.gz | 4 +-- .../reference_results.metadata | 24 +++++++------- ...luid-bottom-openfoam_solid-calculix.tar.gz | 4 +-- .../reference_results.metadata | 24 +++++++------- ...solid-calculix_fluid-outer-openfoam.tar.gz | 4 +-- .../reference_results.metadata | 24 +++++++------- ...ream-dealii_solid-downstream-dealii.tar.gz | 4 +-- .../reference_results.metadata | 24 +++++++------- .../reference_results.metadata | 24 +++++++------- .../fluid1-openfoam_fluid2-openfoam.tar.gz | 4 +-- .../reference_results.metadata | 24 +++++++------- ...mplefoam_fluid2-openfoam-pimplefoam.tar.gz | 4 +-- ...oam_fluid2-openfoam-sonicliquidfoam.tar.gz | 4 +-- .../reference_results.metadata | 26 +++++++-------- .../fluid-openfoam_solid-calculix.tar.gz | 4 +-- .../fluid-openfoam_solid-dealii.tar.gz | 4 +-- .../fluid-openfoam_solid-fenics.tar.gz | 4 +-- .../fluid-openfoam_solid-openfoam.tar.gz | 4 +-- .../fluid-su2_solid-fenics.tar.gz | 4 +-- .../reference_results.metadata | 32 +++++++++---------- .../fluid-openfoam_solid-cpp.tar.gz | 4 +-- .../reference_results.metadata | 24 +++++++------- 37 files changed, 224 insertions(+), 224 deletions(-) diff --git a/elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz b/elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz index 383c1474b..b9fec481f 100644 --- a/elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz +++ b/elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dcac24d624a65deb88db28700983d5e51034ffecee0ce531e77411826da5fab4 -size 250384 +oid sha256:6cdb7515613c0d6ef5f7e3d86c08e4d0dd933ccec1b41c5459c956d8b68c84e0 +size 250418 diff --git a/elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz b/elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz index c86ae5777..9d0f43a77 100644 --- a/elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz +++ b/elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:077fe088ea23154db78f9021fac0b2af14cebef781a0053ec1b832e7050d3b63 -size 25949 +oid sha256:3a87326b2673b9a17ce94a9c280d4c76664a9465946a1b18e9fa118c70be51a6 +size 25983 diff --git a/elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz b/elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz index 795a4fee3..3057f1567 100644 --- a/elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz +++ b/elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:802a4f9d82320a220c89238fe6214b0befb3831f4e84743810d6a9853609de2a -size 250793 +oid sha256:fcd3ac4ecceddbe7c429af155e50175f38998fad1449b117aa8c60ef7b1bff12 +size 250792 diff --git a/elastic-tube-1d/reference-results/reference_results.metadata b/elastic-tube-1d/reference-results/reference_results.metadata index 5b9b66d5c..82a75f601 100644 --- a/elastic-tube-1d/reference-results/reference_results.metadata +++ b/elastic-tube-1d/reference-results/reference_results.metadata @@ -11,31 +11,31 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-python_solid-python.tar.gz | 2026-05-28 09:29:43 | 802a4f9d82320a220c89238fe6214b0befb3831f4e84743810d6a9853609de2a | -| fluid-cpp_solid-python.tar.gz | 2026-05-28 09:29:43 | 077fe088ea23154db78f9021fac0b2af14cebef781a0053ec1b832e7050d3b63 | -| fluid-cpp_solid-cpp.tar.gz | 2026-05-28 09:29:43 | dcac24d624a65deb88db28700983d5e51034ffecee0ce531e77411826da5fab4 | +| fluid-cpp_solid-cpp.tar.gz | 2026-05-28 11:46:06 | 6cdb7515613c0d6ef5f7e3d86c08e4d0dd933ccec1b41c5459c956d8b68c84e0 | +| fluid-python_solid-python.tar.gz | 2026-05-28 11:46:06 | fcd3ac4ecceddbe7c429af155e50175f38998fad1449b117aa8c60ef7b1bff12 | +| fluid-cpp_solid-python.tar.gz | 2026-05-28 11:46:06 | 3a87326b2673b9a17ce94a9c280d4c76664a9465946a1b18e9fa118c70be51a6 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | v3.4.1 | -| PRECICE_PRESET | production-audit | -| OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | 2c3062c | -| PYTHON_BINDINGS_REF | v3.4.0 | -| FENICS_ADAPTER_REF | v2.3.0 | -| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | -| CALCULIX_ADAPTER_REF | v2.20.1 | -| SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 5abe79b | -| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | | DUMUX_ADAPTER_REF | 3f3f54f | | MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 9cb7068 | +| PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/elastic-tube-3d/reference-results/fluid-openfoam_solid-calculix.tar.gz b/elastic-tube-3d/reference-results/fluid-openfoam_solid-calculix.tar.gz index c2122af64..d409a8bf1 100644 --- a/elastic-tube-3d/reference-results/fluid-openfoam_solid-calculix.tar.gz +++ b/elastic-tube-3d/reference-results/fluid-openfoam_solid-calculix.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb945c0935c53894eabc1bb21c38386603ab88a3d46c5add9c9c52e2c8735608 -size 1100829 +oid sha256:cb1fc445a9c31b7065b172c8fb35b05796367d7b350c0c648df0b4293a5c30bb +size 1100768 diff --git a/elastic-tube-3d/reference-results/fluid-openfoam_solid-fenics.tar.gz b/elastic-tube-3d/reference-results/fluid-openfoam_solid-fenics.tar.gz index 2916e65c0..2cb32d19b 100644 --- a/elastic-tube-3d/reference-results/fluid-openfoam_solid-fenics.tar.gz +++ b/elastic-tube-3d/reference-results/fluid-openfoam_solid-fenics.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f408f5cf707aa1d2bffe17313ef5f91c5cc4803c266988d25358b8d74d4cc36 -size 461399 +oid sha256:1ac3c623f4d831d8df501e095bc66d14968cde9f80dbb237eb46642c269808ad +size 461609 diff --git a/elastic-tube-3d/reference-results/reference_results.metadata b/elastic-tube-3d/reference-results/reference_results.metadata index eb31c9ab8..caec30c27 100644 --- a/elastic-tube-3d/reference-results/reference_results.metadata +++ b/elastic-tube-3d/reference-results/reference_results.metadata @@ -11,30 +11,30 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-fenics.tar.gz | 2026-05-28 09:29:43 | 3f408f5cf707aa1d2bffe17313ef5f91c5cc4803c266988d25358b8d74d4cc36 | -| fluid-openfoam_solid-calculix.tar.gz | 2026-05-28 09:29:43 | bb945c0935c53894eabc1bb21c38386603ab88a3d46c5add9c9c52e2c8735608 | +| fluid-openfoam_solid-fenics.tar.gz | 2026-05-28 11:46:06 | 1ac3c623f4d831d8df501e095bc66d14968cde9f80dbb237eb46642c269808ad | +| fluid-openfoam_solid-calculix.tar.gz | 2026-05-28 11:46:06 | cb1fc445a9c31b7065b172c8fb35b05796367d7b350c0c648df0b4293a5c30bb | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | v3.4.1 | -| PRECICE_PRESET | production-audit | -| OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | 2c3062c | -| PYTHON_BINDINGS_REF | v3.4.0 | -| FENICS_ADAPTER_REF | v2.3.0 | -| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | -| CALCULIX_ADAPTER_REF | v2.20.1 | -| SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 5abe79b | -| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | | DUMUX_ADAPTER_REF | 3f3f54f | | MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 9cb7068 | +| PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz b/flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz index b15205330..81b93176c 100644 --- a/flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz +++ b/flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9747357ae3ab382c43c4970503f4fea6f33fcf0cfe4c519cf5e6c23f9e95ed55 -size 59051 +oid sha256:3e15f7e09b131d5939d6d87e9c617603752aeb39110c595db6535b7a2e9fc6a4 +size 58968 diff --git a/flow-over-heated-plate-nearest-projection/reference-results/reference_results.metadata b/flow-over-heated-plate-nearest-projection/reference-results/reference_results.metadata index df44d9fb5..638d36e88 100644 --- a/flow-over-heated-plate-nearest-projection/reference-results/reference_results.metadata +++ b/flow-over-heated-plate-nearest-projection/reference-results/reference_results.metadata @@ -11,29 +11,29 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-28 09:29:43 | 9747357ae3ab382c43c4970503f4fea6f33fcf0cfe4c519cf5e6c23f9e95ed55 | +| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-28 11:46:06 | 3e15f7e09b131d5939d6d87e9c617603752aeb39110c595db6535b7a2e9fc6a4 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | v3.4.1 | -| PRECICE_PRESET | production-audit | -| OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | 2c3062c | -| PYTHON_BINDINGS_REF | v3.4.0 | -| FENICS_ADAPTER_REF | v2.3.0 | -| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | -| CALCULIX_ADAPTER_REF | v2.20.1 | -| SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 5abe79b | -| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | | DUMUX_ADAPTER_REF | 3f3f54f | | MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 9cb7068 | +| PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz b/flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz index 425b51a21..adfa95dfd 100644 --- a/flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz +++ b/flow-over-heated-plate-two-meshes/reference-results/fluid-openfoam_solid-openfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c84deec0fe351b2ba2353097a2ae4b8fa576cad82bd93a5f91bf2eec1d3af0a -size 40791 +oid sha256:19a5abd5df4ebdc556279600d6dfa31734f9ea3d7f13f51aec5e0526f638c3f9 +size 40717 diff --git a/flow-over-heated-plate-two-meshes/reference-results/reference_results.metadata b/flow-over-heated-plate-two-meshes/reference-results/reference_results.metadata index d91d1c197..748d536bc 100644 --- a/flow-over-heated-plate-two-meshes/reference-results/reference_results.metadata +++ b/flow-over-heated-plate-two-meshes/reference-results/reference_results.metadata @@ -11,29 +11,29 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-28 09:29:43 | 6c84deec0fe351b2ba2353097a2ae4b8fa576cad82bd93a5f91bf2eec1d3af0a | +| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-28 11:46:06 | 19a5abd5df4ebdc556279600d6dfa31734f9ea3d7f13f51aec5e0526f638c3f9 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | v3.4.1 | -| PRECICE_PRESET | production-audit | -| OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | 2c3062c | -| PYTHON_BINDINGS_REF | v3.4.0 | -| FENICS_ADAPTER_REF | v2.3.0 | -| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | -| CALCULIX_ADAPTER_REF | v2.20.1 | -| SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 5abe79b | -| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | | DUMUX_ADAPTER_REF | 3f3f54f | | MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 9cb7068 | +| PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz index 5a54408b2..24e5c7bb3 100644 --- a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz +++ b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e360b6b410c119b360c08a243fe18e8ec28893f3ea37cd33f229858cd35ad465 -size 214056 +oid sha256:38a2d7a489dc943abd52a9565e92ca43a747490654929629231ce856906e4723 +size 214130 diff --git a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz index 4a800b29a..4486f2cc4 100644 --- a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz +++ b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-nutils.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56d6090748bd9823d24074a22161136e0616cfd7c7bb51a84afaba6353c1f9d9 -size 6999 +oid sha256:e5c5243767d2b24c473a985e239152c72dcf040f49bc02c99f1175a0e7db1835 +size 6986 diff --git a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz index a7b8d4370..e9975cd76 100644 --- a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz +++ b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fbb69f5a8383fd0d9829c717ebde32133e9bcaad4cefd234d22664e2811429eb -size 136144 +oid sha256:7a92099a21a3043486453f0ceb1700ce79febad10e6e7ca6852435f31f5a9f7e +size 136311 diff --git a/flow-over-heated-plate/reference-results/reference_results.metadata b/flow-over-heated-plate/reference-results/reference_results.metadata index a169473b5..d0f0654ae 100644 --- a/flow-over-heated-plate/reference-results/reference_results.metadata +++ b/flow-over-heated-plate/reference-results/reference_results.metadata @@ -11,31 +11,31 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-fenics.tar.gz | 2026-05-28 09:29:43 | e360b6b410c119b360c08a243fe18e8ec28893f3ea37cd33f229858cd35ad465 | -| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-28 09:29:43 | fbb69f5a8383fd0d9829c717ebde32133e9bcaad4cefd234d22664e2811429eb | -| fluid-openfoam_solid-nutils.tar.gz | 2026-05-28 09:29:43 | 56d6090748bd9823d24074a22161136e0616cfd7c7bb51a84afaba6353c1f9d9 | +| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-28 11:46:06 | 7a92099a21a3043486453f0ceb1700ce79febad10e6e7ca6852435f31f5a9f7e | +| fluid-openfoam_solid-fenics.tar.gz | 2026-05-28 11:46:06 | 38a2d7a489dc943abd52a9565e92ca43a747490654929629231ce856906e4723 | +| fluid-openfoam_solid-nutils.tar.gz | 2026-05-28 11:46:06 | e5c5243767d2b24c473a985e239152c72dcf040f49bc02c99f1175a0e7db1835 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | v3.4.1 | -| PRECICE_PRESET | production-audit | -| OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | 2c3062c | -| PYTHON_BINDINGS_REF | v3.4.0 | -| FENICS_ADAPTER_REF | v2.3.0 | -| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | -| CALCULIX_ADAPTER_REF | v2.20.1 | -| SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 5abe79b | -| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | | DUMUX_ADAPTER_REF | 3f3f54f | | MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 9cb7068 | +| PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz b/free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz index 27bbbc29f..b736237f1 100644 --- a/free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz +++ b/free-flow-over-porous-media/reference-results/free-flow-dumux_porous-media-dumux.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f08bd2862c7a656b91c6eadb989ea6e0d5a81dd658b569ee390a170ff574bad5 -size 1582 +oid sha256:376f19f23b29d97dee96c0f6eda00d892a3bddd1deed521c919cc65b8c041f64 +size 1596 diff --git a/free-flow-over-porous-media/reference-results/reference_results.metadata b/free-flow-over-porous-media/reference-results/reference_results.metadata index 7701e8342..0be5193d2 100644 --- a/free-flow-over-porous-media/reference-results/reference_results.metadata +++ b/free-flow-over-porous-media/reference-results/reference_results.metadata @@ -11,29 +11,29 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| free-flow-dumux_porous-media-dumux.tar.gz | 2026-05-28 09:29:43 | f08bd2862c7a656b91c6eadb989ea6e0d5a81dd658b569ee390a170ff574bad5 | +| free-flow-dumux_porous-media-dumux.tar.gz | 2026-05-28 11:46:06 | 376f19f23b29d97dee96c0f6eda00d892a3bddd1deed521c919cc65b8c041f64 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | v3.4.1 | -| PRECICE_PRESET | production-audit | -| OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | 2c3062c | -| PYTHON_BINDINGS_REF | v3.4.0 | -| FENICS_ADAPTER_REF | v2.3.0 | -| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | -| CALCULIX_ADAPTER_REF | v2.20.1 | -| SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 5abe79b | -| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | | DUMUX_ADAPTER_REF | 3f3f54f | | MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 9cb7068 | +| PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/heat-exchanger-simplified/reference-results/fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz b/heat-exchanger-simplified/reference-results/fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz index de51bdf3a..1d13caaf9 100644 --- a/heat-exchanger-simplified/reference-results/fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz +++ b/heat-exchanger-simplified/reference-results/fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a80d4c65362f87483056145f7640198c5294cf76fdd1805951d2e733e940f0d -size 43254 +oid sha256:1eb7b0fad4eb132608091ffa5350a0457dbfaae9c4993b42886eaf87a51de61e +size 43432 diff --git a/heat-exchanger-simplified/reference-results/reference_results.metadata b/heat-exchanger-simplified/reference-results/reference_results.metadata index fdbfb365e..1d6d2ffcb 100644 --- a/heat-exchanger-simplified/reference-results/reference_results.metadata +++ b/heat-exchanger-simplified/reference-results/reference_results.metadata @@ -11,29 +11,29 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz | 2026-05-28 09:29:43 | 4a80d4c65362f87483056145f7640198c5294cf76fdd1805951d2e733e940f0d | +| fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix.tar.gz | 2026-05-28 11:46:06 | 1eb7b0fad4eb132608091ffa5350a0457dbfaae9c4993b42886eaf87a51de61e | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | v3.4.1 | -| PRECICE_PRESET | production-audit | -| OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | 2c3062c | -| PYTHON_BINDINGS_REF | v3.4.0 | -| FENICS_ADAPTER_REF | v2.3.0 | -| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | -| CALCULIX_ADAPTER_REF | v2.20.1 | -| SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 5abe79b | -| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | | DUMUX_ADAPTER_REF | 3f3f54f | | MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 9cb7068 | +| PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/heat-exchanger/reference-results/fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam.tar.gz b/heat-exchanger/reference-results/fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam.tar.gz index 2e108d13b..74b595c0c 100644 --- a/heat-exchanger/reference-results/fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam.tar.gz +++ b/heat-exchanger/reference-results/fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7a810976a3d60e8455f31f7af2b773cdd75fedfe495c15184c2b4a6ac7ee4c4 -size 13167651 +oid sha256:a7f1ffdb89cde47214eb1d97772c0d884c2b078d73578c7da27270b4463c8eb2 +size 13167591 diff --git a/heat-exchanger/reference-results/reference_results.metadata b/heat-exchanger/reference-results/reference_results.metadata index e9957eaa4..5f6f7148b 100644 --- a/heat-exchanger/reference-results/reference_results.metadata +++ b/heat-exchanger/reference-results/reference_results.metadata @@ -11,29 +11,29 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam.tar.gz | 2026-05-28 09:29:43 | d7a810976a3d60e8455f31f7af2b773cdd75fedfe495c15184c2b4a6ac7ee4c4 | +| fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam.tar.gz | 2026-05-28 11:46:06 | a7f1ffdb89cde47214eb1d97772c0d884c2b078d73578c7da27270b4463c8eb2 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | v3.4.1 | -| PRECICE_PRESET | production-audit | -| OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | 2c3062c | -| PYTHON_BINDINGS_REF | v3.4.0 | -| FENICS_ADAPTER_REF | v2.3.0 | -| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | -| CALCULIX_ADAPTER_REF | v2.20.1 | -| SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 5abe79b | -| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | | DUMUX_ADAPTER_REF | 3f3f54f | | MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 9cb7068 | +| PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/multiple-perpendicular-flaps/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz b/multiple-perpendicular-flaps/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz index 8eea5b00a..163ae0240 100644 --- a/multiple-perpendicular-flaps/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz +++ b/multiple-perpendicular-flaps/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8ae5e3ac120708db57069704180f20b2a8fb749183d4d921af9c9361c2ab9c4 -size 108231 +oid sha256:53f71fb905f3714c4d30a963ba47139e1a93fa3ed3fc838856baa9fd81b949d7 +size 108391 diff --git a/multiple-perpendicular-flaps/reference-results/reference_results.metadata b/multiple-perpendicular-flaps/reference-results/reference_results.metadata index c109b3a4e..e6126cdb8 100644 --- a/multiple-perpendicular-flaps/reference-results/reference_results.metadata +++ b/multiple-perpendicular-flaps/reference-results/reference_results.metadata @@ -11,29 +11,29 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz | 2026-05-28 09:29:43 | b8ae5e3ac120708db57069704180f20b2a8fb749183d4d921af9c9361c2ab9c4 | +| fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz | 2026-05-28 11:46:06 | 53f71fb905f3714c4d30a963ba47139e1a93fa3ed3fc838856baa9fd81b949d7 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | v3.4.1 | -| PRECICE_PRESET | production-audit | -| OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | 2c3062c | -| PYTHON_BINDINGS_REF | v3.4.0 | -| FENICS_ADAPTER_REF | v2.3.0 | -| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | -| CALCULIX_ADAPTER_REF | v2.20.1 | -| SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 5abe79b | -| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | | DUMUX_ADAPTER_REF | 3f3f54f | | MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 9cb7068 | +| PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/partitioned-pipe-multiscale/reference-results/reference_results.metadata b/partitioned-pipe-multiscale/reference-results/reference_results.metadata index 8ca6d0077..f5736e0a1 100644 --- a/partitioned-pipe-multiscale/reference-results/reference_results.metadata +++ b/partitioned-pipe-multiscale/reference-results/reference_results.metadata @@ -11,29 +11,29 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid1d-left-nutils_fluid3d-right-openfoam | 2026-05-28 09:29:43 | 07d0360a28c03e58b410dbe858ceaa2c4e16a1f234c802e49a1d34ecbd201759 | +| fluid1d-left-nutils_fluid3d-right-openfoam | 2026-05-28 11:46:06 | 02f4fd383a7dccdab5a7abdfd5c0da77c76ffa9b3bb8674880a976b349af2747 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | v3.4.1 | -| PRECICE_PRESET | production-audit | -| OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | 2c3062c | -| PYTHON_BINDINGS_REF | v3.4.0 | -| FENICS_ADAPTER_REF | v2.3.0 | -| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | -| CALCULIX_ADAPTER_REF | v2.20.1 | -| SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 5abe79b | -| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | | DUMUX_ADAPTER_REF | 3f3f54f | | MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 9cb7068 | +| PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/partitioned-pipe-two-phase/reference-results/fluid1-openfoam_fluid2-openfoam.tar.gz b/partitioned-pipe-two-phase/reference-results/fluid1-openfoam_fluid2-openfoam.tar.gz index 534ea80a2..01957f955 100644 --- a/partitioned-pipe-two-phase/reference-results/fluid1-openfoam_fluid2-openfoam.tar.gz +++ b/partitioned-pipe-two-phase/reference-results/fluid1-openfoam_fluid2-openfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88d5e0bc64adc71a1d2bd94f15d43fb0f75935eb1a727153edf0cbd732f26319 -size 2523 +oid sha256:7e8fc0ad04b413d09909f98cdc070fcb1255833c516f8fc6d8a34c8fa82a2d0b +size 2508 diff --git a/partitioned-pipe-two-phase/reference-results/reference_results.metadata b/partitioned-pipe-two-phase/reference-results/reference_results.metadata index 45b69aaa8..97a7ada77 100644 --- a/partitioned-pipe-two-phase/reference-results/reference_results.metadata +++ b/partitioned-pipe-two-phase/reference-results/reference_results.metadata @@ -11,29 +11,29 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid1-openfoam_fluid2-openfoam.tar.gz | 2026-05-28 09:29:43 | 88d5e0bc64adc71a1d2bd94f15d43fb0f75935eb1a727153edf0cbd732f26319 | +| fluid1-openfoam_fluid2-openfoam.tar.gz | 2026-05-28 11:46:06 | 7e8fc0ad04b413d09909f98cdc070fcb1255833c516f8fc6d8a34c8fa82a2d0b | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | v3.4.1 | -| PRECICE_PRESET | production-audit | -| OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | 2c3062c | -| PYTHON_BINDINGS_REF | v3.4.0 | -| FENICS_ADAPTER_REF | v2.3.0 | -| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | -| CALCULIX_ADAPTER_REF | v2.20.1 | -| SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 5abe79b | -| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | | DUMUX_ADAPTER_REF | 3f3f54f | | MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 9cb7068 | +| PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/partitioned-pipe/reference-results/fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam.tar.gz b/partitioned-pipe/reference-results/fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam.tar.gz index f36fdeb19..5cc6204e0 100644 --- a/partitioned-pipe/reference-results/fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam.tar.gz +++ b/partitioned-pipe/reference-results/fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74aaef5b7bdeb2999e947a6d67c3c5cdc39489bb7f0eedf1ddbf6bcfe4046c84 -size 253157 +oid sha256:500166363879b3767f5256dc640d61fa466005e707a45644a7c78a4ea6518132 +size 253022 diff --git a/partitioned-pipe/reference-results/fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam.tar.gz b/partitioned-pipe/reference-results/fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam.tar.gz index 6f3e512bf..821630ecb 100644 --- a/partitioned-pipe/reference-results/fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam.tar.gz +++ b/partitioned-pipe/reference-results/fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0844a705c909f2525f6d126fc76c52f506f1313a0f38a8dec1e4a3a99a5b2c32 -size 377146 +oid sha256:a7aa2d9a4bacc89fe494ee9e812bc5b437957a00943a13faf28887ca4a55fa39 +size 377022 diff --git a/partitioned-pipe/reference-results/reference_results.metadata b/partitioned-pipe/reference-results/reference_results.metadata index 0791a9eaf..751c4ae41 100644 --- a/partitioned-pipe/reference-results/reference_results.metadata +++ b/partitioned-pipe/reference-results/reference_results.metadata @@ -11,30 +11,30 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam.tar.gz | 2026-05-28 09:29:43 | 74aaef5b7bdeb2999e947a6d67c3c5cdc39489bb7f0eedf1ddbf6bcfe4046c84 | -| fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam.tar.gz | 2026-05-28 09:29:43 | 0844a705c909f2525f6d126fc76c52f506f1313a0f38a8dec1e4a3a99a5b2c32 | +| fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam.tar.gz | 2026-05-28 11:46:06 | 500166363879b3767f5256dc640d61fa466005e707a45644a7c78a4ea6518132 | +| fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam.tar.gz | 2026-05-28 11:46:06 | a7aa2d9a4bacc89fe494ee9e812bc5b437957a00943a13faf28887ca4a55fa39 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | v3.4.1 | -| PRECICE_PRESET | production-audit | -| OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | 2c3062c | -| PYTHON_BINDINGS_REF | v3.4.0 | -| FENICS_ADAPTER_REF | v2.3.0 | -| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | -| CALCULIX_ADAPTER_REF | v2.20.1 | -| SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 5abe79b | -| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | | DUMUX_ADAPTER_REF | 3f3f54f | | MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 9cb7068 | +| PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz b/perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz index 637d9286c..f03c95539 100644 --- a/perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz +++ b/perpendicular-flap/reference-results/fluid-openfoam_solid-calculix.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:092c7c3f286c6b757bad44a9ac8bdb33d11c9f934045d806f1868a41142aeb88 -size 4768778 +oid sha256:1d8b37c6aa705766f5a9013381a044cc825b807bfcfc06b26adaea88c4de8afb +size 4769628 diff --git a/perpendicular-flap/reference-results/fluid-openfoam_solid-dealii.tar.gz b/perpendicular-flap/reference-results/fluid-openfoam_solid-dealii.tar.gz index 98b3ea9b1..e377f47c8 100644 --- a/perpendicular-flap/reference-results/fluid-openfoam_solid-dealii.tar.gz +++ b/perpendicular-flap/reference-results/fluid-openfoam_solid-dealii.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ee47dd459486ef149ef1583a08b605bf6f02a16e48727d15519c6f18d53bc61 -size 3168395 +oid sha256:562d713526dcd4d2daaa10b92fc09d01fcae88017d0d50b7dd78394c661410f9 +size 3167756 diff --git a/perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz b/perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz index d2d7e6c2e..53d0c3580 100644 --- a/perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz +++ b/perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca9af8314a88b4542ffdfcba9314f35d6014d80ef1a7751990ff725d5881cb00 -size 31241 +oid sha256:002d647077d0f4558bdf692c858a12d8078bf460dbb336bdba22389f8f54eac9 +size 31275 diff --git a/perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz b/perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz index 8b536918e..ed3b5dc3b 100644 --- a/perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz +++ b/perpendicular-flap/reference-results/fluid-openfoam_solid-openfoam.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e83659cc1321057b41b92bc6bf68d38cda0331095ee87c650611dff076d6ea9 -size 4556 +oid sha256:347c9e1a521146c2855f17798edd9ca2aa1ed8e24b2f1412655c8260d860a96a +size 4532 diff --git a/perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz b/perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz index 4815b7f6a..1e2c6c5a4 100644 --- a/perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz +++ b/perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69fca563854a1ca1ef5d2f5ec56a231e22c3d3f09af2a4b635430ab37fd6f670 -size 32193 +oid sha256:6f77d1458d64b243af525c09d62cfa7da217d6ea5d0924a769c73ef50e500757 +size 32181 diff --git a/perpendicular-flap/reference-results/reference_results.metadata b/perpendicular-flap/reference-results/reference_results.metadata index e5fd1de4d..147de2d01 100644 --- a/perpendicular-flap/reference-results/reference_results.metadata +++ b/perpendicular-flap/reference-results/reference_results.metadata @@ -11,33 +11,33 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-calculix.tar.gz | 2026-05-28 09:29:43 | 092c7c3f286c6b757bad44a9ac8bdb33d11c9f934045d806f1868a41142aeb88 | -| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-28 09:29:43 | 7e83659cc1321057b41b92bc6bf68d38cda0331095ee87c650611dff076d6ea9 | -| fluid-openfoam_solid-fenics.tar.gz | 2026-05-28 09:29:43 | ca9af8314a88b4542ffdfcba9314f35d6014d80ef1a7751990ff725d5881cb00 | -| fluid-openfoam_solid-dealii.tar.gz | 2026-05-28 09:29:43 | 1ee47dd459486ef149ef1583a08b605bf6f02a16e48727d15519c6f18d53bc61 | -| fluid-su2_solid-fenics.tar.gz | 2026-05-28 09:29:43 | 69fca563854a1ca1ef5d2f5ec56a231e22c3d3f09af2a4b635430ab37fd6f670 | +| fluid-openfoam_solid-fenics.tar.gz | 2026-05-28 11:46:06 | 002d647077d0f4558bdf692c858a12d8078bf460dbb336bdba22389f8f54eac9 | +| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-28 11:46:06 | 347c9e1a521146c2855f17798edd9ca2aa1ed8e24b2f1412655c8260d860a96a | +| fluid-su2_solid-fenics.tar.gz | 2026-05-28 11:46:06 | 6f77d1458d64b243af525c09d62cfa7da217d6ea5d0924a769c73ef50e500757 | +| fluid-openfoam_solid-calculix.tar.gz | 2026-05-28 11:46:06 | 1d8b37c6aa705766f5a9013381a044cc825b807bfcfc06b26adaea88c4de8afb | +| fluid-openfoam_solid-dealii.tar.gz | 2026-05-28 11:46:06 | 562d713526dcd4d2daaa10b92fc09d01fcae88017d0d50b7dd78394c661410f9 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | v3.4.1 | -| PRECICE_PRESET | production-audit | -| OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | 2c3062c | -| PYTHON_BINDINGS_REF | v3.4.0 | -| FENICS_ADAPTER_REF | v2.3.0 | -| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | -| CALCULIX_ADAPTER_REF | v2.20.1 | -| SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 5abe79b | -| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | | DUMUX_ADAPTER_REF | 3f3f54f | | MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 9cb7068 | +| PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine diff --git a/quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz b/quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz index 3fa978a18..ca9630071 100644 --- a/quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz +++ b/quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b068911de262c02b25f869c503992d15cb32e3d011c214d8eca7bc2306ec197 -size 198946 +oid sha256:1e3357da0f3683de0d3433504197b1471d585a3d31d0f6da8caed416ec08558b +size 198911 diff --git a/quickstart/reference-results/reference_results.metadata b/quickstart/reference-results/reference_results.metadata index c9f2e0c7e..54578177b 100644 --- a/quickstart/reference-results/reference_results.metadata +++ b/quickstart/reference-results/reference_results.metadata @@ -11,29 +11,29 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-cpp.tar.gz | 2026-05-28 09:29:43 | 6b068911de262c02b25f869c503992d15cb32e3d011c214d8eca7bc2306ec197 | +| fluid-openfoam_solid-cpp.tar.gz | 2026-05-28 11:46:06 | 1e3357da0f3683de0d3433504197b1471d585a3d31d0f6da8caed416ec08558b | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | v3.4.1 | -| PRECICE_PRESET | production-audit | -| OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | 2c3062c | -| PYTHON_BINDINGS_REF | v3.4.0 | -| FENICS_ADAPTER_REF | v2.3.0 | -| TUTORIALS_REF | af6bba0 | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | -| CALCULIX_ADAPTER_REF | v2.20.1 | -| SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 5abe79b | -| DEALII_ADAPTER_REF | a421d92 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | | DUMUX_ADAPTER_REF | 3f3f54f | | MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 9cb7068 | +| PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine From e0b75ccdeace239fbd807e11862deecd43453e0c Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 28 May 2026 13:06:29 +0200 Subject: [PATCH 33/94] Remove unused and conflictling export:vtu from geometric multiscale tutorials (#813) --- partitioned-pipe-multiscale/precice-config-1d-1d.xml | 2 -- partitioned-pipe-multiscale/precice-config-1d-3d.xml | 2 -- partitioned-pipe-multiscale/precice-config-3d-1d.xml | 2 -- partitioned-pipe-multiscale/precice-config-3d-3d.xml | 2 -- water-hammer/precice-config-1d-1d.xml | 2 -- water-hammer/precice-config-1d-3d.xml | 2 -- water-hammer/precice-config-3d-1d.xml | 2 -- water-hammer/precice-config-3d-3d.xml | 2 -- 8 files changed, 16 deletions(-) diff --git a/partitioned-pipe-multiscale/precice-config-1d-1d.xml b/partitioned-pipe-multiscale/precice-config-1d-1d.xml index ca78d8c2e..56b57d57a 100644 --- a/partitioned-pipe-multiscale/precice-config-1d-1d.xml +++ b/partitioned-pipe-multiscale/precice-config-1d-1d.xml @@ -32,7 +32,6 @@ from="Fluid1DLeft-Mesh" to="Fluid1DRight-Mesh" constraint="consistent" /> - @@ -45,7 +44,6 @@ from="Fluid1DRight-Mesh" to="Fluid1DLeft-Mesh" constraint="consistent" /> - diff --git a/partitioned-pipe-multiscale/precice-config-1d-3d.xml b/partitioned-pipe-multiscale/precice-config-1d-3d.xml index 5eef4fe97..2d6230668 100644 --- a/partitioned-pipe-multiscale/precice-config-1d-3d.xml +++ b/partitioned-pipe-multiscale/precice-config-1d-3d.xml @@ -37,7 +37,6 @@ from="Fluid3DRight-Mesh" to="Fluid1DLeft-Mesh" constraint="consistent" /> - @@ -56,7 +55,6 @@ from="Fluid1DLeft-Mesh" to="Fluid3DRight-Mesh" constraint="consistent" /> - diff --git a/partitioned-pipe-multiscale/precice-config-3d-1d.xml b/partitioned-pipe-multiscale/precice-config-3d-1d.xml index addaea07e..8355f165e 100644 --- a/partitioned-pipe-multiscale/precice-config-3d-1d.xml +++ b/partitioned-pipe-multiscale/precice-config-3d-1d.xml @@ -37,7 +37,6 @@ from="Fluid3DLeft-Mesh" to="Fluid1DRight-Mesh" constraint="consistent" /> - @@ -56,7 +55,6 @@ from="Fluid1DRight-Mesh" to="Fluid3DLeft-Mesh" constraint="consistent" /> - diff --git a/partitioned-pipe-multiscale/precice-config-3d-3d.xml b/partitioned-pipe-multiscale/precice-config-3d-3d.xml index a296a8970..0ab5beef5 100644 --- a/partitioned-pipe-multiscale/precice-config-3d-3d.xml +++ b/partitioned-pipe-multiscale/precice-config-3d-3d.xml @@ -32,7 +32,6 @@ from="Fluid3DLeft-Mesh" to="Fluid3DRight-Mesh" constraint="consistent" /> - @@ -45,7 +44,6 @@ from="Fluid3DRight-Mesh" to="Fluid3DLeft-Mesh" constraint="consistent" /> - diff --git a/water-hammer/precice-config-1d-1d.xml b/water-hammer/precice-config-1d-1d.xml index b0362ad8c..0e8fc0a83 100644 --- a/water-hammer/precice-config-1d-1d.xml +++ b/water-hammer/precice-config-1d-1d.xml @@ -32,7 +32,6 @@ from="Fluid1DRight-Mesh" to="Fluid1DLeft-Mesh" constraint="consistent" /> - @@ -45,7 +44,6 @@ from="Fluid1DLeft-Mesh" to="Fluid1DRight-Mesh" constraint="consistent" /> - diff --git a/water-hammer/precice-config-1d-3d.xml b/water-hammer/precice-config-1d-3d.xml index 0518bf91e..87cd7ddd6 100644 --- a/water-hammer/precice-config-1d-3d.xml +++ b/water-hammer/precice-config-1d-3d.xml @@ -38,7 +38,6 @@ from="Fluid3DRight-Mesh" to="Fluid1DLeft-Mesh" constraint="consistent" /> - @@ -57,7 +56,6 @@ from="Fluid1DLeft-Mesh" to="Fluid3DRight-Mesh" constraint="consistent" /> - diff --git a/water-hammer/precice-config-3d-1d.xml b/water-hammer/precice-config-3d-1d.xml index 42f506424..464e45a52 100644 --- a/water-hammer/precice-config-3d-1d.xml +++ b/water-hammer/precice-config-3d-1d.xml @@ -38,7 +38,6 @@ from="Fluid3DLeft-Mesh" to="Fluid1DRight-Mesh" constraint="consistent" /> - @@ -57,7 +56,6 @@ from="Fluid1DRight-Mesh" to="Fluid3DLeft-Mesh" constraint="consistent" /> - diff --git a/water-hammer/precice-config-3d-3d.xml b/water-hammer/precice-config-3d-3d.xml index a96064f08..7c9ef2cc3 100644 --- a/water-hammer/precice-config-3d-3d.xml +++ b/water-hammer/precice-config-3d-3d.xml @@ -32,7 +32,6 @@ from="Fluid3DLeft-Mesh" to="Fluid3DRight-Mesh" constraint="consistent" /> - @@ -45,7 +44,6 @@ from="Fluid3DRight-Mesh" to="Fluid3DLeft-Mesh" constraint="consistent" /> - From 0f4166e26972a5584ebed7a8533133fa760ee075 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 28 May 2026 13:09:27 +0200 Subject: [PATCH 34/94] Update TUTORIALS_REF --- tools/tests/reference_versions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index 13e8f9f0c..a4cc0eea2 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -21,7 +21,7 @@ OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 PRECICE_REF: "v3.4.1" PYTHON_BINDINGS_REF: "v3.4.0" SU2_ADAPTER_REF: "5abe79b" # develop, May 27, 2026 -TUTORIALS_REF: "9cb7068" # develop, May 28, 2026 +TUTORIALS_REF: "e0b75cc" # develop, May 28, 2026 # Additional settings PRECICE_PRESET: "production-audit" \ No newline at end of file From cd46d7c96d9911a5f62fee6b68fd24e51d7d3c3e Mon Sep 17 00:00:00 2001 From: preCICE Tests VM Date: Thu, 28 May 2026 13:12:49 +0200 Subject: [PATCH 35/94] Add reference results for partitioned-pipe-multiscale --- .../reference-results/reference_results.metadata | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/partitioned-pipe-multiscale/reference-results/reference_results.metadata b/partitioned-pipe-multiscale/reference-results/reference_results.metadata index f5736e0a1..e0fe892db 100644 --- a/partitioned-pipe-multiscale/reference-results/reference_results.metadata +++ b/partitioned-pipe-multiscale/reference-results/reference_results.metadata @@ -11,7 +11,7 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid1d-left-nutils_fluid3d-right-openfoam | 2026-05-28 11:46:06 | 02f4fd383a7dccdab5a7abdfd5c0da77c76ffa9b3bb8674880a976b349af2747 | +| fluid1d-left-nutils_fluid3d-right-openfoam | 2026-05-28 13:11:52 | fb0a188b5ba0e4b6111a89a15e285816f1f9d855f88843624145f4d5cb229b6a | ## List of arguments used to generate the files @@ -32,7 +32,7 @@ We also include some information on the machine used to generate them | PRECICE_REF | v3.4.1 | | PYTHON_BINDINGS_REF | v3.4.0 | | SU2_ADAPTER_REF | 5abe79b | -| TUTORIALS_REF | 9cb7068 | +| TUTORIALS_REF | e0b75cc | | PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | From 084b108213354ba2576859d62de5b8c52de5df17 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 28 May 2026 13:32:31 +0200 Subject: [PATCH 36/94] Exclude elastic-tube-3d_fluid-openfoam_solid-fenics from the tests Too small values, expected to fail comparisons with the current tolerances. --- tools/tests/tests.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index 226e6e015..4c6817483 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -48,7 +48,7 @@ test_suites: - fluid-openfoam - solid-fenics max_time_windows: 1 - reference_result: ./elastic-tube-3d/reference-results/fluid-openfoam_solid-fenics.tar.gz + reference_result: ./elastic-tube-3d/reference-results/fluid-openfoam_solid-fenics.tar.gz # Too small values, expected to fail the comparisons. flow-over-heated-plate: tutorials: @@ -234,7 +234,7 @@ test_suites: - *elastic-tube-1d_fluid-cpp_solid-python - *elastic-tube-1d_fluid-python_solid-python - *elastic-tube-3d_fluid-openfoam_solid-calculix - - *elastic-tube-3d_fluid-openfoam_solid-fenics + # *elastic-tube-3d_fluid-openfoam_solid-fenics # excluded - too small values to compare - *flow-over-heated-plate_fluid-openfoam_solid-fenics - *flow-over-heated-plate_fluid-openfoam_solid-nutils - *flow-over-heated-plate_fluid-openfoam_solid-openfoam @@ -253,7 +253,7 @@ test_suites: - *perpendicular-flap_fluid-openfoam_solid-fenics - *perpendicular-flap_fluid-openfoam_solid-openfoam - *perpendicular-flap_fluid-su2_solid-fenics - # two-scale-heat-conduction_macro-dumux_micro-dumux excluded + # *two-scale-heat-conduction_macro-dumux_micro-dumux # excluded - too small values to compare - *quickstart_openfoam_cpp calculix-adapter: @@ -276,14 +276,14 @@ test_suites: fenics-adapter: tutorials: - - *elastic-tube-3d_fluid-openfoam_solid-fenics + - # *two-scale-heat-conduction_macro-dumux_micro-dumux # excluded - too small values to compare - *flow-over-heated-plate_fluid-openfoam_solid-fenics - *perpendicular-flap_fluid-openfoam_solid-fenics - *perpendicular-flap_fluid-su2_solid-fenics micro-manager: tutorials: - - *two-scale-heat-conduction_macro-dumux_micro-dumux + - *two-scale-heat-conduction_macro-dumux_micro-dumux # expected to fail, but only available test - still useful nutils-adapter: # Not a repository tutorials: @@ -293,7 +293,7 @@ test_suites: openfoam-adapter: tutorials: - *elastic-tube-3d_fluid-openfoam_solid-calculix - - *elastic-tube-3d_fluid-openfoam_solid-fenics + # *elastic-tube-3d_fluid-openfoam_solid-fenics # excluded - too small values to compare - *flow-over-heated-plate_fluid-openfoam_solid-fenics - *flow-over-heated-plate_fluid-openfoam_solid-nutils - *flow-over-heated-plate_fluid-openfoam_solid-openfoam From 895143f9bc9c90d8691d3cd980e58bfcb3cc9b8f Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 28 May 2026 13:47:12 +0200 Subject: [PATCH 37/94] Fix syntax in tests.yaml --- tools/tests/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index 4c6817483..4511a8c24 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -276,7 +276,7 @@ test_suites: fenics-adapter: tutorials: - - # *two-scale-heat-conduction_macro-dumux_micro-dumux # excluded - too small values to compare + # *two-scale-heat-conduction_macro-dumux_micro-dumux # excluded - too small values to compare - *flow-over-heated-plate_fluid-openfoam_solid-fenics - *perpendicular-flap_fluid-openfoam_solid-fenics - *perpendicular-flap_fluid-su2_solid-fenics From 816ffe9faa4c24ee98839638619e162afb5a7347 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 28 May 2026 13:55:19 +0200 Subject: [PATCH 38/94] Fix typo in tests.yaml (partitioned-pipe-multiscale reference_result) --- tools/tests/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index 4511a8c24..8ac956acf 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -159,7 +159,7 @@ test_suites: - fluid1d-left-nutils - fluid3d-right-openfoam max_time: 0.05 - reference_result: ./partitioned-pipe-multiscale/reference-results/fluid1d-left-nutils_fluid3d-right-openfoam + reference_result: ./partitioned-pipe-multiscale/reference-results/fluid1d-left-nutils_fluid3d-right-openfoam.tar.gz partitioned-pipe-two-phase: tutorials: From 61feacfbb91856484fb36c0d88667fde9981a34e Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 28 May 2026 14:00:45 +0200 Subject: [PATCH 39/94] Update reference versions --- tools/tests/reference_versions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index a4cc0eea2..60b2b4671 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -21,7 +21,7 @@ OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 PRECICE_REF: "v3.4.1" PYTHON_BINDINGS_REF: "v3.4.0" SU2_ADAPTER_REF: "5abe79b" # develop, May 27, 2026 -TUTORIALS_REF: "e0b75cc" # develop, May 28, 2026 +TUTORIALS_REF: "816ffe9" # develop, May 28, 2026 # Additional settings PRECICE_PRESET: "production-audit" \ No newline at end of file From 12f83263cc3a7915ad64a4125e8704491846c4fa Mon Sep 17 00:00:00 2001 From: preCICE Tests VM Date: Thu, 28 May 2026 14:02:52 +0200 Subject: [PATCH 40/94] Add reference results for partitioned-pipe-multiscale --- .../fluid1d-left-nutils_fluid3d-right-openfoam.tar.gz | 3 +++ .../reference-results/reference_results.metadata | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 partitioned-pipe-multiscale/reference-results/fluid1d-left-nutils_fluid3d-right-openfoam.tar.gz diff --git a/partitioned-pipe-multiscale/reference-results/fluid1d-left-nutils_fluid3d-right-openfoam.tar.gz b/partitioned-pipe-multiscale/reference-results/fluid1d-left-nutils_fluid3d-right-openfoam.tar.gz new file mode 100644 index 000000000..c61ce8698 --- /dev/null +++ b/partitioned-pipe-multiscale/reference-results/fluid1d-left-nutils_fluid3d-right-openfoam.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc40404e24756a6a6e913356965a67bbe84a1e7596fae90fead5fd4069677deb +size 18619 diff --git a/partitioned-pipe-multiscale/reference-results/reference_results.metadata b/partitioned-pipe-multiscale/reference-results/reference_results.metadata index e0fe892db..4442de921 100644 --- a/partitioned-pipe-multiscale/reference-results/reference_results.metadata +++ b/partitioned-pipe-multiscale/reference-results/reference_results.metadata @@ -11,7 +11,7 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid1d-left-nutils_fluid3d-right-openfoam | 2026-05-28 13:11:52 | fb0a188b5ba0e4b6111a89a15e285816f1f9d855f88843624145f4d5cb229b6a | +| fluid1d-left-nutils_fluid3d-right-openfoam.tar.gz | 2026-05-28 14:01:56 | bc40404e24756a6a6e913356965a67bbe84a1e7596fae90fead5fd4069677deb | ## List of arguments used to generate the files @@ -32,7 +32,7 @@ We also include some information on the machine used to generate them | PRECICE_REF | v3.4.1 | | PYTHON_BINDINGS_REF | v3.4.0 | | SU2_ADAPTER_REF | 5abe79b | -| TUTORIALS_REF | e0b75cc | +| TUTORIALS_REF | 816ffe9 | | PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | From 8246d02e15087697fc08603603ce3755fabfd76d Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 28 May 2026 15:37:28 +0200 Subject: [PATCH 41/94] Make partitioned-elastic-beam consistent and add to the system tests (#814) Co-authored-by: preCICE Tests VM --- changelog-entries/814.md | 1 + .../dirichlet-calculix/config.yml | 8 +- .../dirichlet-calculix/run.sh | 2 +- ...artitioned-elastic-beam-precice-config.png | Bin 70244 -> 66743 bytes partitioned-elastic-beam/metadata.yaml | 20 +++++ .../neumann-calculix/config.yml | 8 +- .../neumann-calculix/run.sh | 2 +- partitioned-elastic-beam/precice-config.xml | 59 +++++++-------- ...dirichlet-calculix_neumann-calculix.tar.gz | 3 + .../reference_results.metadata | 71 ++++++++++++++++++ tools/tests/reference_versions.yaml | 2 +- tools/tests/tests.yaml | 12 +++ 12 files changed, 147 insertions(+), 41 deletions(-) create mode 100644 changelog-entries/814.md create mode 100644 partitioned-elastic-beam/metadata.yaml create mode 100644 partitioned-elastic-beam/reference-results/dirichlet-calculix_neumann-calculix.tar.gz create mode 100644 partitioned-elastic-beam/reference-results/reference_results.metadata diff --git a/changelog-entries/814.md b/changelog-entries/814.md new file mode 100644 index 000000000..aa90298ef --- /dev/null +++ b/changelog-entries/814.md @@ -0,0 +1 @@ +- Renamed the data, meshes, and participants in the partitioned-elastic-beam tutorial [#814](https://github.com/precice/tutorials/pull/814) \ No newline at end of file diff --git a/partitioned-elastic-beam/dirichlet-calculix/config.yml b/partitioned-elastic-beam/dirichlet-calculix/config.yml index 6cd31537c..519234353 100644 --- a/partitioned-elastic-beam/dirichlet-calculix/config.yml +++ b/partitioned-elastic-beam/dirichlet-calculix/config.yml @@ -1,9 +1,9 @@ participants: - Calculix1: + Dirichlet: interfaces: - - nodes-mesh: Calculix-Mesh1 + - nodes-mesh: Dirichlet-Mesh patch: surface - read-data: [Displacement0] - write-data: [Force0] + read-data: [Displacement] + write-data: [Force] precice-config-file: ../precice-config.xml diff --git a/partitioned-elastic-beam/dirichlet-calculix/run.sh b/partitioned-elastic-beam/dirichlet-calculix/run.sh index 50487ec03..9b100c3c5 100755 --- a/partitioned-elastic-beam/dirichlet-calculix/run.sh +++ b/partitioned-elastic-beam/dirichlet-calculix/run.sh @@ -6,6 +6,6 @@ exec > >(tee --append "$LOGFILE") 2>&1 export OMP_NUM_THREADS=1 export CCX_NPROC_EQUATION_SOLVER=1 -ccx_preCICE -i beam1 -precice-participant Calculix1 +ccx_preCICE -i beam1 -precice-participant Dirichlet close_log diff --git a/partitioned-elastic-beam/images/tutorials-partitioned-elastic-beam-precice-config.png b/partitioned-elastic-beam/images/tutorials-partitioned-elastic-beam-precice-config.png index 24c552332332ff2b860d5e4268a80e4269ea22ab..0871903ccbbbe8972017585d501a26d025455350 100644 GIT binary patch literal 66743 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

T37A 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 GeEtjLl8z(* diff --git a/partitioned-elastic-beam/metadata.yaml b/partitioned-elastic-beam/metadata.yaml new file mode 100644 index 000000000..3d9788f88 --- /dev/null +++ b/partitioned-elastic-beam/metadata.yaml @@ -0,0 +1,20 @@ +name: Partitioned elastic beam +path: partitioned-elastic-beam # relative to git repo +url: https://precice.org/tutorials-partitioned-elastic-beam.html + +participants: + - Dirichlet + - Neumann + +cases: + dirichlet-calculix: + participant: Dirichlet + directory: ./dirichlet-calculix + run: ./run.sh + component: calculix-adapter + + neumann-calculix: + participant: Neumann + directory: ./neumann-calculix + run: ./run.sh + component: calculix-adapter \ No newline at end of file diff --git a/partitioned-elastic-beam/neumann-calculix/config.yml b/partitioned-elastic-beam/neumann-calculix/config.yml index 793f41626..ad7f1f320 100644 --- a/partitioned-elastic-beam/neumann-calculix/config.yml +++ b/partitioned-elastic-beam/neumann-calculix/config.yml @@ -1,9 +1,9 @@ participants: - Calculix2: + Neumann: interfaces: - - nodes-mesh: Calculix-Mesh2 + - nodes-mesh: Neumann-Mesh patch: surface - read-data: [Force0] - write-data: [Displacement0] + read-data: [Force] + write-data: [Displacement] precice-config-file: ../precice-config.xml diff --git a/partitioned-elastic-beam/neumann-calculix/run.sh b/partitioned-elastic-beam/neumann-calculix/run.sh index 7505fab51..49495abca 100755 --- a/partitioned-elastic-beam/neumann-calculix/run.sh +++ b/partitioned-elastic-beam/neumann-calculix/run.sh @@ -6,6 +6,6 @@ exec > >(tee --append "$LOGFILE") 2>&1 export OMP_NUM_THREADS=1 export CCX_NPROC_EQUATION_SOLVER=1 -ccx_preCICE -i beam2 -precice-participant Calculix2 +ccx_preCICE -i beam2 -precice-participant Neumann close_log diff --git a/partitioned-elastic-beam/precice-config.xml b/partitioned-elastic-beam/precice-config.xml index 5a15f12ad..4d242c2bc 100644 --- a/partitioned-elastic-beam/precice-config.xml +++ b/partitioned-elastic-beam/precice-config.xml @@ -9,57 +9,56 @@ - - + + - - - + + + - - - + + + - - - - - + + + + + - - - - + + + + - + - + - - + + - - - + + - - + + diff --git a/partitioned-elastic-beam/reference-results/dirichlet-calculix_neumann-calculix.tar.gz b/partitioned-elastic-beam/reference-results/dirichlet-calculix_neumann-calculix.tar.gz new file mode 100644 index 000000000..442c24679 --- /dev/null +++ b/partitioned-elastic-beam/reference-results/dirichlet-calculix_neumann-calculix.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d101821ef08ad7c0b2dae2b30d244ecac1b1c5d234ad01701ecf7567fff69fb3 +size 21541 diff --git a/partitioned-elastic-beam/reference-results/reference_results.metadata b/partitioned-elastic-beam/reference-results/reference_results.metadata new file mode 100644 index 000000000..e7e3fe1ee --- /dev/null +++ b/partitioned-elastic-beam/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| dirichlet-calculix_neumann-calculix.tar.gz | 2026-05-28 15:36:00 | d101821ef08ad7c0b2dae2b30d244ecac1b1c5d234ad01701ecf7567fff69fb3 | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 0b8bea0 | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index 60b2b4671..48182c164 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -21,7 +21,7 @@ OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 PRECICE_REF: "v3.4.1" PYTHON_BINDINGS_REF: "v3.4.0" SU2_ADAPTER_REF: "5abe79b" # develop, May 27, 2026 -TUTORIALS_REF: "816ffe9" # develop, May 28, 2026 +TUTORIALS_REF: "0b8bea0" # develop, May 28, 2026 # Additional settings PRECICE_PRESET: "production-audit" \ No newline at end of file diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index 8ac956acf..ac529c278 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -134,6 +134,16 @@ test_suites: max_time: 0.1 reference_result: ./multiple-perpendicular-flaps/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz + partitioned-elastic-beam: + tutorials: + - &partitioned-elastic-beam_dirichlet-calculix_neumann-calculix + path: partitioned-elastic-beam + case_combination: + - dirichlet-calculix + - neumann-calculix + max_time_windows: 10 + reference_result: ./partitioned-elastic-beam/reference-results/dirichlet-calculix_neumann-calculix.tar.gz + partitioned-pipe: tutorials: - &partitioned-pipe_fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam @@ -244,6 +254,7 @@ test_suites: - *heat-exchanger_fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam - *heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix - *multiple-perpendicular-flaps_fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii + - *partitioned-elastic-beam_dirichlet-calculix_neumann-calculix - *partitioned-pipe_fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam - *partitioned-pipe_fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam - *partitioned-pipe-multiscale_fluid1d-left-nutils_fluid3d-right-openfoam @@ -262,6 +273,7 @@ test_suites: - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix - *heat-exchanger_fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam - *heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix + - *partitioned-elastic-beam_dirichlet-calculix_neumann-calculix - *perpendicular-flap_fluid-openfoam_solid-calculix dealii-adapter: From abe58fca40b24075021ffcdc9f896b43ec2ad5d0 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 28 May 2026 17:48:59 +0200 Subject: [PATCH 42/94] Add partitioned heat conduction tests (#815) --- .../metadata.yaml | 20 +++++ .../dirichlet-fenics_neumann-fenics.tar.gz | 3 + .../reference_results.metadata | 71 ++++++++++++++++++ .../dirichlet-openfoam/run.sh | 6 ++ partitioned-heat-conduction/metadata.yaml | 68 +++++++++++++++++ .../neumann-openfoam/run.sh | 6 ++ .../dirichlet-fenics_neumann-fenics.tar.gz | 3 + .../dirichlet-nutils_neumann-nutils.tar.gz | 3 + ...dirichlet-openfoam_neumann-openfoam.tar.gz | 3 + .../reference_results.metadata | 73 +++++++++++++++++++ tools/tests/reference_versions.yaml | 2 +- tools/tests/tests.yaml | 41 +++++++++++ 12 files changed, 298 insertions(+), 1 deletion(-) create mode 100644 partitioned-heat-conduction-complex/metadata.yaml create mode 100644 partitioned-heat-conduction-complex/reference-results/dirichlet-fenics_neumann-fenics.tar.gz create mode 100644 partitioned-heat-conduction-complex/reference-results/reference_results.metadata create mode 100644 partitioned-heat-conduction/metadata.yaml create mode 100644 partitioned-heat-conduction/reference-results/dirichlet-fenics_neumann-fenics.tar.gz create mode 100644 partitioned-heat-conduction/reference-results/dirichlet-nutils_neumann-nutils.tar.gz create mode 100644 partitioned-heat-conduction/reference-results/dirichlet-openfoam_neumann-openfoam.tar.gz create mode 100644 partitioned-heat-conduction/reference-results/reference_results.metadata diff --git a/partitioned-heat-conduction-complex/metadata.yaml b/partitioned-heat-conduction-complex/metadata.yaml new file mode 100644 index 000000000..673a612b3 --- /dev/null +++ b/partitioned-heat-conduction-complex/metadata.yaml @@ -0,0 +1,20 @@ +name: Partitioned heat conduction complex +path: partitioned-heat-conduction-complex # relative to git repo +url: https://precice.org/tutorials-partitioned-heat-conduction-complex.html + +participants: + - Dirichlet + - Neumann + +cases: + dirichlet-fenics: + participant: Dirichlet + directory: ./dirichlet-fenics + run: ./run.sh + component: fenics-adapter + + neumann-fenics: + participant: Neumann + directory: ./neumann-fenics + run: ./run.sh + component: fenics-adapter diff --git a/partitioned-heat-conduction-complex/reference-results/dirichlet-fenics_neumann-fenics.tar.gz b/partitioned-heat-conduction-complex/reference-results/dirichlet-fenics_neumann-fenics.tar.gz new file mode 100644 index 000000000..c0b63f3b5 --- /dev/null +++ b/partitioned-heat-conduction-complex/reference-results/dirichlet-fenics_neumann-fenics.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:585f3f212d365a7a5183b37f0175a42ce98ec9f5c23b2ef1606bfb76199b4d24 +size 13001 diff --git a/partitioned-heat-conduction-complex/reference-results/reference_results.metadata b/partitioned-heat-conduction-complex/reference-results/reference_results.metadata new file mode 100644 index 000000000..8d475a966 --- /dev/null +++ b/partitioned-heat-conduction-complex/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| dirichlet-fenics_neumann-fenics.tar.gz | 2026-05-28 17:44:13 | 585f3f212d365a7a5183b37f0175a42ce98ec9f5c23b2ef1606bfb76199b4d24 | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 7a5a9c9 | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/partitioned-heat-conduction/dirichlet-openfoam/run.sh b/partitioned-heat-conduction/dirichlet-openfoam/run.sh index 8f55fbfa5..b9449bf62 100755 --- a/partitioned-heat-conduction/dirichlet-openfoam/run.sh +++ b/partitioned-heat-conduction/dirichlet-openfoam/run.sh @@ -4,6 +4,12 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 +if ! command -v heatTransfer > /dev/null 2>&1; then + echo "Building the heatTransfer OpenFOAM solver" + wclean ../solver-openfoam/ + wmake ../solver-openfoam/ +fi + blockMesh ../../tools/run-openfoam.sh "$@" diff --git a/partitioned-heat-conduction/metadata.yaml b/partitioned-heat-conduction/metadata.yaml new file mode 100644 index 000000000..9a655e4e8 --- /dev/null +++ b/partitioned-heat-conduction/metadata.yaml @@ -0,0 +1,68 @@ +name: Partitioned heat conduction +path: partitioned-heat-conduction # relative to git repo +url: https://precice.org/tutorials-partitioned-heat-conduction.html + +participants: + - Dirichlet + - Neumann + +cases: + dirichlet-fenics: + participant: Dirichlet + directory: ./dirichlet-fenics + run: ./run.sh + component: fenics-adapter + + # dirichlet-fenicsx: + # participant: Dirichlet + # directory: ./dirichlet-fenicsx + # run: ./run.sh + # component: fenicsx-adapter + + # dirichlet-gismo: + # participant: Dirichlet + # directory: ./dirichlet-gismo + # run: ./run.sh + # component: gismo-adapter + + dirichlet-nutils: + participant: Dirichlet + directory: ./dirichlet-nutils + run: ./run.sh + component: nutils-adapter + + dirichlet-openfoam: + participant: Dirichlet + directory: ./dirichlet-openfoam + run: ./run.sh + component: openfoam-adapter + + neumann-fenics: + participant: Neumann + directory: ./neumann-fenics + run: ./run.sh + component: fenics-adapter + + # neumann-fenicsx: + # participant: Neumann + # directory: ./neumann-fenicsx + # run: ./run.sh + # component: fenicsx-adapter + + # neumann-gismo: + # participant: Neumann + # directory: ./neumann-gismo + # run: ./run.sh + # component: gismo-adapter + + neumann-nutils: + participant: Neumann + directory: ./neumann-nutils + run: ./run.sh + component: nutils-adapter + + neumann-openfoam: + participant: Neumann + directory: ./neumann-openfoam + run: ./run.sh + component: openfoam-adapter \ No newline at end of file diff --git a/partitioned-heat-conduction/neumann-openfoam/run.sh b/partitioned-heat-conduction/neumann-openfoam/run.sh index 8f55fbfa5..b9449bf62 100755 --- a/partitioned-heat-conduction/neumann-openfoam/run.sh +++ b/partitioned-heat-conduction/neumann-openfoam/run.sh @@ -4,6 +4,12 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 +if ! command -v heatTransfer > /dev/null 2>&1; then + echo "Building the heatTransfer OpenFOAM solver" + wclean ../solver-openfoam/ + wmake ../solver-openfoam/ +fi + blockMesh ../../tools/run-openfoam.sh "$@" diff --git a/partitioned-heat-conduction/reference-results/dirichlet-fenics_neumann-fenics.tar.gz b/partitioned-heat-conduction/reference-results/dirichlet-fenics_neumann-fenics.tar.gz new file mode 100644 index 000000000..18880c0d8 --- /dev/null +++ b/partitioned-heat-conduction/reference-results/dirichlet-fenics_neumann-fenics.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d2123f020ee4cddf89fb05fdd6e7abeb8f16cb33cca4a74ca7cfafceabcd91b +size 2145 diff --git a/partitioned-heat-conduction/reference-results/dirichlet-nutils_neumann-nutils.tar.gz b/partitioned-heat-conduction/reference-results/dirichlet-nutils_neumann-nutils.tar.gz new file mode 100644 index 000000000..93dcba28a --- /dev/null +++ b/partitioned-heat-conduction/reference-results/dirichlet-nutils_neumann-nutils.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:031a3bcc9a9d6d55ea4715b29e0c1a939bf522f1bf9cce048625590ab9fa3792 +size 2761 diff --git a/partitioned-heat-conduction/reference-results/dirichlet-openfoam_neumann-openfoam.tar.gz b/partitioned-heat-conduction/reference-results/dirichlet-openfoam_neumann-openfoam.tar.gz new file mode 100644 index 000000000..ba9874a09 --- /dev/null +++ b/partitioned-heat-conduction/reference-results/dirichlet-openfoam_neumann-openfoam.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12a281982b1dc08e7fd9092dd5b30110ccc5f5db5b76594d60e7186b38d5fcbb +size 6964 diff --git a/partitioned-heat-conduction/reference-results/reference_results.metadata b/partitioned-heat-conduction/reference-results/reference_results.metadata new file mode 100644 index 000000000..fc2d101d8 --- /dev/null +++ b/partitioned-heat-conduction/reference-results/reference_results.metadata @@ -0,0 +1,73 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| dirichlet-openfoam_neumann-openfoam.tar.gz | 2026-05-28 17:44:13 | 12a281982b1dc08e7fd9092dd5b30110ccc5f5db5b76594d60e7186b38d5fcbb | +| dirichlet-nutils_neumann-nutils.tar.gz | 2026-05-28 17:44:13 | 031a3bcc9a9d6d55ea4715b29e0c1a939bf522f1bf9cce048625590ab9fa3792 | +| dirichlet-fenics_neumann-fenics.tar.gz | 2026-05-28 17:44:13 | 6d2123f020ee4cddf89fb05fdd6e7abeb8f16cb33cca4a74ca7cfafceabcd91b | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 7a5a9c9 | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index 48182c164..2f6609796 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -21,7 +21,7 @@ OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 PRECICE_REF: "v3.4.1" PYTHON_BINDINGS_REF: "v3.4.0" SU2_ADAPTER_REF: "5abe79b" # develop, May 27, 2026 -TUTORIALS_REF: "0b8bea0" # develop, May 28, 2026 +TUTORIALS_REF: "7a5a9c9" # develop, May 28, 2026 # Additional settings PRECICE_PRESET: "production-audit" \ No newline at end of file diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index ac529c278..8c9c25c69 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -144,6 +144,39 @@ test_suites: max_time_windows: 10 reference_result: ./partitioned-elastic-beam/reference-results/dirichlet-calculix_neumann-calculix.tar.gz + partitioned-heat-conduction: + tutorials: + - &partitioned-heat-conduction_dirichlet-fenics_neumann-fenics + path: partitioned-heat-conduction + case_combination: + - dirichlet-fenics + - neumann-fenics + max_time: 0.3 + reference_result: ./partitioned-heat-conduction/reference-results/dirichlet-fenics_neumann-fenics.tar.gz + - &partitioned-heat-conduction_dirichlet-nutils_neumann-nutils + path: partitioned-heat-conduction + case_combination: + - dirichlet-nutils + - neumann-nutils + max_time: 0.3 + reference_result: ./partitioned-heat-conduction/reference-results/dirichlet-nutils_neumann-nutils.tar.gz + - &partitioned-heat-conduction_dirichlet-openfoam_neumann-openfoam + path: partitioned-heat-conduction + case_combination: + - dirichlet-openfoam + - neumann-openfoam + max_time: 0.3 + reference_result: ./partitioned-heat-conduction/reference-results/dirichlet-openfoam_neumann-openfoam.tar.gz + + partitioned-heat-conduction-complex: + tutorials: + - &partitioned-heat-conduction-complex_dirichlet-fenics_neumann-fenics + path: partitioned-heat-conduction-complex + case_combination: + - dirichlet-fenics + - neumann-fenics + reference_result: ./partitioned-heat-conduction-complex/reference-results/dirichlet-fenics_neumann-fenics.tar.gz + partitioned-pipe: tutorials: - &partitioned-pipe_fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam @@ -255,6 +288,10 @@ test_suites: - *heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix - *multiple-perpendicular-flaps_fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii - *partitioned-elastic-beam_dirichlet-calculix_neumann-calculix + - *partitioned-heat-conduction_dirichlet-fenics_neumann-fenics + - *partitioned-heat-conduction_dirichlet-nutils_neumann-nutils + - *partitioned-heat-conduction_dirichlet-openfoam_neumann-openfoam + - *partitioned-heat-conduction-complex_dirichlet-fenics_neumann-fenics - *partitioned-pipe_fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam - *partitioned-pipe_fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam - *partitioned-pipe-multiscale_fluid1d-left-nutils_fluid3d-right-openfoam @@ -290,6 +327,8 @@ test_suites: tutorials: # *two-scale-heat-conduction_macro-dumux_micro-dumux # excluded - too small values to compare - *flow-over-heated-plate_fluid-openfoam_solid-fenics + - *partitioned-heat-conduction_dirichlet-fenics_neumann-fenics + - *partitioned-heat-conduction-complex_dirichlet-fenics_neumann-fenics - *perpendicular-flap_fluid-openfoam_solid-fenics - *perpendicular-flap_fluid-su2_solid-fenics @@ -300,6 +339,7 @@ test_suites: nutils-adapter: # Not a repository tutorials: - *flow-over-heated-plate_fluid-openfoam_solid-nutils + - *partitioned-heat-conduction_dirichlet-nutils_neumann-nutils - *partitioned-pipe-multiscale_fluid1d-left-nutils_fluid3d-right-openfoam openfoam-adapter: @@ -314,6 +354,7 @@ test_suites: - *heat-exchanger_fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam - *heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix - *multiple-perpendicular-flaps_fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii + - *partitioned-heat-conduction_dirichlet-openfoam_neumann-openfoam - *partitioned-pipe_fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam - *partitioned-pipe_fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam - *partitioned-pipe-multiscale_fluid1d-left-nutils_fluid3d-right-openfoam From d891386f24f6397acce1d74014400ccdcc9a9183 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Fri, 29 May 2026 10:22:16 +0200 Subject: [PATCH 43/94] Add channel transport tutorials to the system tests (#816) --- changelog-entries/816.md | 1 + .../fluid-fenics/fluid-config.json | 4 +- ...nnel-transport-reaction-precice-config.png | Bin 44484 -> 44649 bytes channel-transport-reaction/metadata.yaml | 20 +++++ channel-transport-reaction/precice-config.xml | 18 ++--- .../fluid-fenics_chemical-fenics.tar.gz | 3 + .../reference_results.metadata | 71 +++++++++++++++++ channel-transport/metadata.yaml | 26 +++++++ .../fluid-nutils_transport-nutils.tar.gz | 3 + .../fluid-openfoam_transport-nutils.tar.gz | 3 + .../reference_results.metadata | 72 ++++++++++++++++++ tools/tests/reference_versions.yaml | 2 +- tools/tests/tests.yaml | 36 ++++++++- 13 files changed, 246 insertions(+), 13 deletions(-) create mode 100644 changelog-entries/816.md create mode 100644 channel-transport-reaction/metadata.yaml create mode 100644 channel-transport-reaction/reference-results/fluid-fenics_chemical-fenics.tar.gz create mode 100644 channel-transport-reaction/reference-results/reference_results.metadata create mode 100644 channel-transport/metadata.yaml create mode 100644 channel-transport/reference-results/fluid-nutils_transport-nutils.tar.gz create mode 100644 channel-transport/reference-results/fluid-openfoam_transport-nutils.tar.gz create mode 100644 channel-transport/reference-results/reference_results.metadata diff --git a/changelog-entries/816.md b/changelog-entries/816.md new file mode 100644 index 000000000..973817a0c --- /dev/null +++ b/changelog-entries/816.md @@ -0,0 +1 @@ +- Renamed the `Flow` participant to `Fluid` in the channel-transport-reaction tutorial [#816](https://github.com/precice/tutorials/pull/816) \ No newline at end of file diff --git a/channel-transport-reaction/fluid-fenics/fluid-config.json b/channel-transport-reaction/fluid-fenics/fluid-config.json index f3a828ae3..7f186e509 100644 --- a/channel-transport-reaction/fluid-fenics/fluid-config.json +++ b/channel-transport-reaction/fluid-fenics/fluid-config.json @@ -1,8 +1,8 @@ { - "participant_name": "Flow", + "participant_name": "Fluid", "config_file_name": "../precice-config.xml", "interface": { - "coupling_mesh_name": "Flow-Mesh", + "coupling_mesh_name": "Fluid-Mesh", "write_data_name": "Velocity" } } diff --git a/channel-transport-reaction/images/tutorials-channel-transport-reaction-precice-config.png b/channel-transport-reaction/images/tutorials-channel-transport-reaction-precice-config.png index e401822ac72fb9bd71ae6d2d83f35d177e4206da..8d8d51f2e34802919dfb23c3e6ca4dbfc869834a 100644 GIT binary patch literal 44649 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%0AZYWUUMp*(#Lmdx)&r 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 - + @@ -19,28 +19,28 @@ - - - + + + - + - + - + - + diff --git a/channel-transport-reaction/reference-results/fluid-fenics_chemical-fenics.tar.gz b/channel-transport-reaction/reference-results/fluid-fenics_chemical-fenics.tar.gz new file mode 100644 index 000000000..f36a95625 --- /dev/null +++ b/channel-transport-reaction/reference-results/fluid-fenics_chemical-fenics.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f736db2462a64dbe5b978d05c0fa3fb46c883f909bf40c80f6710c16c93547a5 +size 1500169 diff --git a/channel-transport-reaction/reference-results/reference_results.metadata b/channel-transport-reaction/reference-results/reference_results.metadata new file mode 100644 index 000000000..55d741d19 --- /dev/null +++ b/channel-transport-reaction/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| fluid-fenics_chemical-fenics.tar.gz | 2026-05-29 09:34:06 | f736db2462a64dbe5b978d05c0fa3fb46c883f909bf40c80f6710c16c93547a5 | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | bf5323c | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/channel-transport/metadata.yaml b/channel-transport/metadata.yaml new file mode 100644 index 000000000..72588003e --- /dev/null +++ b/channel-transport/metadata.yaml @@ -0,0 +1,26 @@ +name: Channel transport +path: channel-transport +url: https://precice.org/tutorials-channel-transport.html + +participants: + - Fluid + - Transport + +cases: + fluid-openfoam: + participant: Fluid + directory: ./fluid-openfoam + run: ./run.sh + component: openfoam-adapter + + fluid-nutils: + participant: Fluid + directory: ./fluid-nutils + run: ./run.sh + component: nutils-adapter + + transport-nutils: + participant: Transport + directory: ./transport-nutils + run: ./run.sh + component: nutils-adapter \ No newline at end of file diff --git a/channel-transport/reference-results/fluid-nutils_transport-nutils.tar.gz b/channel-transport/reference-results/fluid-nutils_transport-nutils.tar.gz new file mode 100644 index 000000000..0ca0d040e --- /dev/null +++ b/channel-transport/reference-results/fluid-nutils_transport-nutils.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:88f14284c484db5e806a884bb6cddee4e583607cb71f01a3d6f367d6125a7aaa +size 1610585 diff --git a/channel-transport/reference-results/fluid-openfoam_transport-nutils.tar.gz b/channel-transport/reference-results/fluid-openfoam_transport-nutils.tar.gz new file mode 100644 index 000000000..cc103856d --- /dev/null +++ b/channel-transport/reference-results/fluid-openfoam_transport-nutils.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ba1c8e6b8f1d7363189ece66db2ab5f0a88398a770c79736fb121d4ea3ecf6e +size 1818514 diff --git a/channel-transport/reference-results/reference_results.metadata b/channel-transport/reference-results/reference_results.metadata new file mode 100644 index 000000000..936b235de --- /dev/null +++ b/channel-transport/reference-results/reference_results.metadata @@ -0,0 +1,72 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| fluid-nutils_transport-nutils.tar.gz | 2026-05-29 09:34:06 | 88f14284c484db5e806a884bb6cddee4e583607cb71f01a3d6f367d6125a7aaa | +| fluid-openfoam_transport-nutils.tar.gz | 2026-05-29 09:34:06 | 8ba1c8e6b8f1d7363189ece66db2ab5f0a88398a770c79736fb121d4ea3ecf6e | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | bf5323c | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index 2f6609796..9e30266d9 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -21,7 +21,7 @@ OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 PRECICE_REF: "v3.4.1" PYTHON_BINDINGS_REF: "v3.4.0" SU2_ADAPTER_REF: "5abe79b" # develop, May 27, 2026 -TUTORIALS_REF: "7a5a9c9" # develop, May 28, 2026 +TUTORIALS_REF: "bf5323c" # develop, May 28, 2026 # Additional settings PRECICE_PRESET: "production-audit" \ No newline at end of file diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index 8c9c25c69..8e87a2b8c 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -11,6 +11,33 @@ # Skipped components are still mentioned in a comment. test_suites: + channel-transport: + tutorials: + - &channel-transport_fluid-openfoam_transport-nutils + path: channel-transport + case_combination: + - fluid-openfoam + - transport-nutils + max_time: 0.1 + reference_result: ./channel-transport/reference-results/fluid-openfoam_transport-nutils.tar.gz + - &channel-transport_fluid-nutils_transport-nutils + path: channel-transport + case_combination: + - fluid-nutils + - transport-nutils + max_time: 0.05 + reference_result: ./channel-transport/reference-results/fluid-nutils_transport-nutils.tar.gz + + channel-transport-reaction: + tutorials: + - &channel-transport-reaction_fluid-fenics_chemical-fenics + path: channel-transport-reaction + case_combination: + - fluid-fenics + - chemical-fenics + max_time: 0.5 + reference_result: ./channel-transport-reaction/reference-results/fluid-fenics_chemical-fenics.tar.gz + elastic-tube-1d: tutorials: - &elastic-tube-1d_fluid-cpp_solid-cpp @@ -273,6 +300,9 @@ test_suites: release_test: tutorials: + - *channel-transport_fluid-openfoam_transport-nutils + - *channel-transport_fluid-nutils_transport-nutils + - *channel-transport-reaction_fluid-fenics_chemical-fenics - *elastic-tube-1d_fluid-cpp_solid-cpp - *elastic-tube-1d_fluid-cpp_solid-python - *elastic-tube-1d_fluid-python_solid-python @@ -325,12 +355,13 @@ test_suites: fenics-adapter: tutorials: - # *two-scale-heat-conduction_macro-dumux_micro-dumux # excluded - too small values to compare + - *channel-transport-reaction_fluid-fenics_chemical-fenics - *flow-over-heated-plate_fluid-openfoam_solid-fenics - *partitioned-heat-conduction_dirichlet-fenics_neumann-fenics - *partitioned-heat-conduction-complex_dirichlet-fenics_neumann-fenics - *perpendicular-flap_fluid-openfoam_solid-fenics - *perpendicular-flap_fluid-su2_solid-fenics + # *two-scale-heat-conduction_macro-dumux_micro-dumux # excluded - too small values to compare micro-manager: tutorials: @@ -338,12 +369,15 @@ test_suites: nutils-adapter: # Not a repository tutorials: + - *channel-transport_fluid-openfoam_transport-nutils + - *channel-transport_fluid-nutils_transport-nutils - *flow-over-heated-plate_fluid-openfoam_solid-nutils - *partitioned-heat-conduction_dirichlet-nutils_neumann-nutils - *partitioned-pipe-multiscale_fluid1d-left-nutils_fluid3d-right-openfoam openfoam-adapter: tutorials: + - *channel-transport_fluid-openfoam_transport-nutils - *elastic-tube-3d_fluid-openfoam_solid-calculix # *elastic-tube-3d_fluid-openfoam_solid-fenics # excluded - too small values to compare - *flow-over-heated-plate_fluid-openfoam_solid-fenics From ecdbe33cbd05da586db37648c713de45f23f3553 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Fri, 29 May 2026 11:56:47 +0200 Subject: [PATCH 44/94] Add extra and perpendicular-flap_fluid-nutils_solid-calculix test suites (#817) Co-authored-by: preCICE Tests VM --- .../generate_reference_results_manual.yml | 2 +- .../generate_reference_results_workflow.yml | 2 +- .../fluid-nutils_solid-calculix.tar.gz | 3 +++ .../reference_results.metadata | 8 ++------ tools/tests/reference_versions.yaml | 2 +- tools/tests/tests.yaml | 18 ++++++++++++++++-- 6 files changed, 24 insertions(+), 11 deletions(-) create mode 100644 perpendicular-flap/reference-results/fluid-nutils_solid-calculix.tar.gz diff --git a/.github/workflows/generate_reference_results_manual.yml b/.github/workflows/generate_reference_results_manual.yml index 6959b5dc3..3b5c6bf60 100644 --- a/.github/workflows/generate_reference_results_manual.yml +++ b/.github/workflows/generate_reference_results_manual.yml @@ -8,7 +8,7 @@ on: type: string commit_msg: description: 'Commit msg for commit that adds the reference results' - default: "Adding reference results" + default: "Add reference results" type: string suites: description: 'Comma-separated test suites to generate reference results for (leave empty for all)' diff --git a/.github/workflows/generate_reference_results_workflow.yml b/.github/workflows/generate_reference_results_workflow.yml index 2b2e98964..c95086d63 100644 --- a/.github/workflows/generate_reference_results_workflow.yml +++ b/.github/workflows/generate_reference_results_workflow.yml @@ -8,7 +8,7 @@ on: type: string commit_msg: description: 'Commit msg for commit that adds the reference results' - default: "Adding reference results" + default: "Add reference results" type: string suites: description: 'Comma-separated test suites to generate reference results for. If empty, all suites are generated.' diff --git a/perpendicular-flap/reference-results/fluid-nutils_solid-calculix.tar.gz b/perpendicular-flap/reference-results/fluid-nutils_solid-calculix.tar.gz new file mode 100644 index 000000000..9863c0e95 --- /dev/null +++ b/perpendicular-flap/reference-results/fluid-nutils_solid-calculix.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:19316047524c56e1675cae8e80014bc6f845f074ce8b225b880fa7a7e140e567 +size 103799 diff --git a/perpendicular-flap/reference-results/reference_results.metadata b/perpendicular-flap/reference-results/reference_results.metadata index 147de2d01..1e15b509b 100644 --- a/perpendicular-flap/reference-results/reference_results.metadata +++ b/perpendicular-flap/reference-results/reference_results.metadata @@ -11,11 +11,7 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-fenics.tar.gz | 2026-05-28 11:46:06 | 002d647077d0f4558bdf692c858a12d8078bf460dbb336bdba22389f8f54eac9 | -| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-28 11:46:06 | 347c9e1a521146c2855f17798edd9ca2aa1ed8e24b2f1412655c8260d860a96a | -| fluid-su2_solid-fenics.tar.gz | 2026-05-28 11:46:06 | 6f77d1458d64b243af525c09d62cfa7da217d6ea5d0924a769c73ef50e500757 | -| fluid-openfoam_solid-calculix.tar.gz | 2026-05-28 11:46:06 | 1d8b37c6aa705766f5a9013381a044cc825b807bfcfc06b26adaea88c4de8afb | -| fluid-openfoam_solid-dealii.tar.gz | 2026-05-28 11:46:06 | 562d713526dcd4d2daaa10b92fc09d01fcae88017d0d50b7dd78394c661410f9 | +| fluid-nutils_solid-calculix.tar.gz | 2026-05-29 11:36:41 | 19316047524c56e1675cae8e80014bc6f845f074ce8b225b880fa7a7e140e567 | ## List of arguments used to generate the files @@ -36,7 +32,7 @@ We also include some information on the machine used to generate them | PRECICE_REF | v3.4.1 | | PYTHON_BINDINGS_REF | v3.4.0 | | SU2_ADAPTER_REF | 5abe79b | -| TUTORIALS_REF | 9cb7068 | +| TUTORIALS_REF | 0fc4468 | | PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index 9e30266d9..e02efddc8 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -21,7 +21,7 @@ OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 PRECICE_REF: "v3.4.1" PYTHON_BINDINGS_REF: "v3.4.0" SU2_ADAPTER_REF: "5abe79b" # develop, May 27, 2026 -TUTORIALS_REF: "bf5323c" # develop, May 28, 2026 +TUTORIALS_REF: "0fc4468" # develop, May 29, 2026 # Additional settings PRECICE_PRESET: "production-audit" \ No newline at end of file diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index 8e87a2b8c..edfb9e8cd 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -276,6 +276,14 @@ test_suites: - solid-fenics max_time: 0.1 reference_result: ./perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz + - &perpendicular-flap_fluid-nutils_solid-calculix + path: perpendicular-flap + case_combination: + - fluid-nutils + - solid-calculix + max_time: 0.1 + timeout: 1200 + reference_result: ./perpendicular-flap/reference-results/fluid-nutils_solid-calculix.tar.gz two-scale-heat-conduction: tutorials: @@ -301,7 +309,6 @@ test_suites: release_test: tutorials: - *channel-transport_fluid-openfoam_transport-nutils - - *channel-transport_fluid-nutils_transport-nutils - *channel-transport-reaction_fluid-fenics_chemical-fenics - *elastic-tube-1d_fluid-cpp_solid-cpp - *elastic-tube-1d_fluid-cpp_solid-python @@ -319,7 +326,6 @@ test_suites: - *multiple-perpendicular-flaps_fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii - *partitioned-elastic-beam_dirichlet-calculix_neumann-calculix - *partitioned-heat-conduction_dirichlet-fenics_neumann-fenics - - *partitioned-heat-conduction_dirichlet-nutils_neumann-nutils - *partitioned-heat-conduction_dirichlet-openfoam_neumann-openfoam - *partitioned-heat-conduction-complex_dirichlet-fenics_neumann-fenics - *partitioned-pipe_fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam @@ -334,6 +340,13 @@ test_suites: # *two-scale-heat-conduction_macro-dumux_micro-dumux # excluded - too small values to compare - *quickstart_openfoam_cpp + # These test suites take longer to run. They are available, but not regularly executed. + extra: + tutorials: + # - *channel-transport_fluid-nutils_transport-nutils + # - *partitioned-heat-conduction_dirichlet-nutils_neumann-nutils + - *perpendicular-flap_fluid-nutils_solid-calculix + calculix-adapter: tutorials: - *elastic-tube-3d_fluid-openfoam_solid-calculix @@ -374,6 +387,7 @@ test_suites: - *flow-over-heated-plate_fluid-openfoam_solid-nutils - *partitioned-heat-conduction_dirichlet-nutils_neumann-nutils - *partitioned-pipe-multiscale_fluid1d-left-nutils_fluid3d-right-openfoam + - *perpendicular-flap_fluid-nutils_solid-calculix openfoam-adapter: tutorials: From 9b5f7ef2786029c2e6477ca9ac3a9c12173b7e5d Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Fri, 29 May 2026 14:19:10 +0200 Subject: [PATCH 45/94] Add turek-hron-fsi3 to the system tests (#818) Co-authored-by: preCICE Tests VM --- .../generate_reference_results_manual.yml | 2 +- .../generate_reference_results_workflow.yml | 2 +- .../tests/dockerfiles/ubuntu_2404/Dockerfile | 4 ++ tools/tests/reference_versions.yaml | 2 +- tools/tests/tests.yaml | 27 ++++++- turek-hron-fsi3/metadata.yaml | 34 +++++++++ .../fluid-nutils_solid-nutils.tar.gz | 3 + .../fluid-openfoam_solid-dealii.tar.gz | 3 + .../reference_results.metadata | 72 +++++++++++++++++++ 9 files changed, 144 insertions(+), 5 deletions(-) create mode 100644 turek-hron-fsi3/metadata.yaml create mode 100644 turek-hron-fsi3/reference-results/fluid-nutils_solid-nutils.tar.gz create mode 100644 turek-hron-fsi3/reference-results/fluid-openfoam_solid-dealii.tar.gz create mode 100644 turek-hron-fsi3/reference-results/reference_results.metadata diff --git a/.github/workflows/generate_reference_results_manual.yml b/.github/workflows/generate_reference_results_manual.yml index 3b5c6bf60..ec793cd18 100644 --- a/.github/workflows/generate_reference_results_manual.yml +++ b/.github/workflows/generate_reference_results_manual.yml @@ -17,7 +17,7 @@ on: type: string clean_docker: description: 'Clean Docker before running' - default: 'TRUE' + default: 'FALSE' type: choice options: - 'FALSE' diff --git a/.github/workflows/generate_reference_results_workflow.yml b/.github/workflows/generate_reference_results_workflow.yml index c95086d63..f1b080638 100644 --- a/.github/workflows/generate_reference_results_workflow.yml +++ b/.github/workflows/generate_reference_results_workflow.yml @@ -17,7 +17,7 @@ on: type: string clean_docker: description: 'Clean Docker before running' - default: 'TRUE' + default: 'FALSE' type: string log_level: description: 'Logging verbosity level used for the systemtests' diff --git a/tools/tests/dockerfiles/ubuntu_2404/Dockerfile b/tools/tests/dockerfiles/ubuntu_2404/Dockerfile index f9aabd4c4..c386c9ab9 100644 --- a/tools/tests/dockerfiles/ubuntu_2404/Dockerfile +++ b/tools/tests/dockerfiles/ubuntu_2404/Dockerfile @@ -128,6 +128,10 @@ RUN python3 -m venv --system-site-packages /home/precice/venv && \ FROM precice_dependecies AS nutils_adapter COPY --from=python_bindings /home/precice/.local /home/precice/.local +USER root +# The following are dependencies of gmsh, needed by the turek-hron-fsi3 nutils participant +RUN apt-get -qq update && \ +apt-get -qq install libglu1-mesa libxrender1 libxcursor1 libxft2 libxinerama1 USER precice # Installing nutils - There is no adapter RUN python3 -m venv /home/precice/venv && \ diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index e02efddc8..376c123bd 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -21,7 +21,7 @@ OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 PRECICE_REF: "v3.4.1" PYTHON_BINDINGS_REF: "v3.4.0" SU2_ADAPTER_REF: "5abe79b" # develop, May 27, 2026 -TUTORIALS_REF: "0fc4468" # develop, May 29, 2026 +TUTORIALS_REF: "33a2563" # other branch, May 29, 2026 # Additional settings PRECICE_PRESET: "production-audit" \ No newline at end of file diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index edfb9e8cd..6ff18fe0d 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -285,6 +285,24 @@ test_suites: timeout: 1200 reference_result: ./perpendicular-flap/reference-results/fluid-nutils_solid-calculix.tar.gz + turek-hron-fsi3: + tutorials: + - &turek-hron-fsi3_fluid-nutils_solid-nutils + path: turek-hron-fsi3 + case_combination: + - fluid-nutils + - solid-nutils + max_time: 0.003 + timeout: 1200 + reference_result: ./turek-hron-fsi3/reference-results/fluid-nutils_solid-nutils.tar.gz + - &turek-hron-fsi3_fluid-openfoam_solid-dealii + path: turek-hron-fsi3 + case_combination: + - fluid-openfoam + - solid-dealii + max_time: 0.003 + reference_result: ./turek-hron-fsi3/reference-results/fluid-openfoam_solid-dealii.tar.gz + two-scale-heat-conduction: tutorials: - &two-scale-heat-conduction_macro-dumux_micro-dumux @@ -337,15 +355,17 @@ test_suites: - *perpendicular-flap_fluid-openfoam_solid-fenics - *perpendicular-flap_fluid-openfoam_solid-openfoam - *perpendicular-flap_fluid-su2_solid-fenics + - *turek-hron-fsi3_fluid-openfoam_solid-dealii # *two-scale-heat-conduction_macro-dumux_micro-dumux # excluded - too small values to compare - *quickstart_openfoam_cpp # These test suites take longer to run. They are available, but not regularly executed. extra: tutorials: - # - *channel-transport_fluid-nutils_transport-nutils - # - *partitioned-heat-conduction_dirichlet-nutils_neumann-nutils + - *channel-transport_fluid-nutils_transport-nutils + - *partitioned-heat-conduction_dirichlet-nutils_neumann-nutils - *perpendicular-flap_fluid-nutils_solid-calculix + - *turek-hron-fsi3_fluid-nutils_solid-nutils calculix-adapter: tutorials: @@ -360,6 +380,7 @@ test_suites: tutorials: - *multiple-perpendicular-flaps_fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii - *perpendicular-flap_fluid-openfoam_solid-dealii + - *turek-hron-fsi3_fluid-openfoam_solid-dealii dumux-adapter: tutorials: @@ -388,6 +409,7 @@ test_suites: - *partitioned-heat-conduction_dirichlet-nutils_neumann-nutils - *partitioned-pipe-multiscale_fluid1d-left-nutils_fluid3d-right-openfoam - *perpendicular-flap_fluid-nutils_solid-calculix + - *turek-hron-fsi3_fluid-nutils_solid-nutils openfoam-adapter: tutorials: @@ -411,6 +433,7 @@ test_suites: - *perpendicular-flap_fluid-openfoam_solid-dealii - *perpendicular-flap_fluid-openfoam_solid-fenics - *perpendicular-flap_fluid-openfoam_solid-openfoam + - *turek-hron-fsi3_fluid-openfoam_solid-dealii - *quickstart_openfoam_cpp su2-adapter: diff --git a/turek-hron-fsi3/metadata.yaml b/turek-hron-fsi3/metadata.yaml new file mode 100644 index 000000000..1647e2120 --- /dev/null +++ b/turek-hron-fsi3/metadata.yaml @@ -0,0 +1,34 @@ +name: Turek-Hron FSI3 +path: turek-hron-fsi3 # relative to git repo +url: https://precice.org/tutorials-turek-hron-fsi3.html + +participants: + - Fluid + - Solid + +cases: + fluid-openfoam: + participant: Fluid + directory: ./fluid-openfoam + run: ./run.sh + component: openfoam-adapter + + fluid-nutils: + participant: Fluid + directory: ./fluid-nutils + run: ./run.sh + component: nutils-adapter + + solid-dealii: + participant: Solid + directory: ./solid-dealii + run: ./run.sh + component: dealii-adapter + + solid-nutils: + participant: Solid + directory: ./solid-nutils + run: ./run.sh + component: nutils-adapter + + diff --git a/turek-hron-fsi3/reference-results/fluid-nutils_solid-nutils.tar.gz b/turek-hron-fsi3/reference-results/fluid-nutils_solid-nutils.tar.gz new file mode 100644 index 000000000..85380e20f --- /dev/null +++ b/turek-hron-fsi3/reference-results/fluid-nutils_solid-nutils.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c50c0c5d35cf0808ad33ea068ba94b7eb1d9e1573c4a65991c25e98c21055fcd +size 3868 diff --git a/turek-hron-fsi3/reference-results/fluid-openfoam_solid-dealii.tar.gz b/turek-hron-fsi3/reference-results/fluid-openfoam_solid-dealii.tar.gz new file mode 100644 index 000000000..768dc47a0 --- /dev/null +++ b/turek-hron-fsi3/reference-results/fluid-openfoam_solid-dealii.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be0303d3b9113989cef36ec211acbecf37c09d8720160f15ef2a267c173eeb01 +size 36332 diff --git a/turek-hron-fsi3/reference-results/reference_results.metadata b/turek-hron-fsi3/reference-results/reference_results.metadata new file mode 100644 index 000000000..9bc293c98 --- /dev/null +++ b/turek-hron-fsi3/reference-results/reference_results.metadata @@ -0,0 +1,72 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| fluid-openfoam_solid-dealii.tar.gz | 2026-05-29 13:59:53 | be0303d3b9113989cef36ec211acbecf37c09d8720160f15ef2a267c173eeb01 | +| fluid-nutils_solid-nutils.tar.gz | 2026-05-29 13:59:53 | c50c0c5d35cf0808ad33ea068ba94b7eb1d9e1573c4a65991c25e98c21055fcd | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 33a2563 | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 From 9b1f28e4fbf08f9188cff270e345b4a38211e274 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Fri, 29 May 2026 14:22:54 +0200 Subject: [PATCH 46/94] tests.yml: Group together the expected to fail cases --- tools/tests/tests.yaml | 52 +++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index 6ff18fe0d..fc851e2bd 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -13,13 +13,6 @@ test_suites: channel-transport: tutorials: - - &channel-transport_fluid-openfoam_transport-nutils - path: channel-transport - case_combination: - - fluid-openfoam - - transport-nutils - max_time: 0.1 - reference_result: ./channel-transport/reference-results/fluid-openfoam_transport-nutils.tar.gz - &channel-transport_fluid-nutils_transport-nutils path: channel-transport case_combination: @@ -27,6 +20,13 @@ test_suites: - transport-nutils max_time: 0.05 reference_result: ./channel-transport/reference-results/fluid-nutils_transport-nutils.tar.gz + - &channel-transport_fluid-openfoam_transport-nutils + path: channel-transport + case_combination: + - fluid-openfoam + - transport-nutils + max_time: 0.1 + reference_result: ./channel-transport/reference-results/fluid-openfoam_transport-nutils.tar.gz channel-transport-reaction: tutorials: @@ -243,6 +243,14 @@ test_suites: perpendicular-flap: tutorials: + - &perpendicular-flap_fluid-nutils_solid-calculix + path: perpendicular-flap + case_combination: + - fluid-nutils + - solid-calculix + max_time: 0.1 + timeout: 1200 + reference_result: ./perpendicular-flap/reference-results/fluid-nutils_solid-calculix.tar.gz - &perpendicular-flap_fluid-openfoam_solid-calculix path: perpendicular-flap case_combination: @@ -276,14 +284,6 @@ test_suites: - solid-fenics max_time: 0.1 reference_result: ./perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz - - &perpendicular-flap_fluid-nutils_solid-calculix - path: perpendicular-flap - case_combination: - - fluid-nutils - - solid-calculix - max_time: 0.1 - timeout: 1200 - reference_result: ./perpendicular-flap/reference-results/fluid-nutils_solid-calculix.tar.gz turek-hron-fsi3: tutorials: @@ -332,7 +332,6 @@ test_suites: - *elastic-tube-1d_fluid-cpp_solid-python - *elastic-tube-1d_fluid-python_solid-python - *elastic-tube-3d_fluid-openfoam_solid-calculix - # *elastic-tube-3d_fluid-openfoam_solid-fenics # excluded - too small values to compare - *flow-over-heated-plate_fluid-openfoam_solid-fenics - *flow-over-heated-plate_fluid-openfoam_solid-nutils - *flow-over-heated-plate_fluid-openfoam_solid-openfoam @@ -355,9 +354,8 @@ test_suites: - *perpendicular-flap_fluid-openfoam_solid-fenics - *perpendicular-flap_fluid-openfoam_solid-openfoam - *perpendicular-flap_fluid-su2_solid-fenics - - *turek-hron-fsi3_fluid-openfoam_solid-dealii - # *two-scale-heat-conduction_macro-dumux_micro-dumux # excluded - too small values to compare - *quickstart_openfoam_cpp + - *turek-hron-fsi3_fluid-openfoam_solid-dealii # These test suites take longer to run. They are available, but not regularly executed. extra: @@ -367,6 +365,11 @@ test_suites: - *perpendicular-flap_fluid-nutils_solid-calculix - *turek-hron-fsi3_fluid-nutils_solid-nutils + expected-to-fail: + tutorials: + - *elastic-tube-3d_fluid-openfoam_solid-fenics # too small values to compare + - *two-scale-heat-conduction_macro-dumux_micro-dumux # too small values to compare + calculix-adapter: tutorials: - *elastic-tube-3d_fluid-openfoam_solid-calculix @@ -395,11 +398,16 @@ test_suites: - *partitioned-heat-conduction-complex_dirichlet-fenics_neumann-fenics - *perpendicular-flap_fluid-openfoam_solid-fenics - *perpendicular-flap_fluid-su2_solid-fenics - # *two-scale-heat-conduction_macro-dumux_micro-dumux # excluded - too small values to compare + + # Excluded: + # *two-scale-heat-conduction_macro-dumux_micro-dumux # too small values to compare micro-manager: tutorials: - - *two-scale-heat-conduction_macro-dumux_micro-dumux # expected to fail, but only available test - still useful + - *two-scale-heat-conduction_macro-dumux_micro-dumux + + # Excluded: + # *two-scale-heat-conduction_macro-dumux_micro-dumux # too small values to compare nutils-adapter: # Not a repository tutorials: @@ -415,7 +423,6 @@ test_suites: tutorials: - *channel-transport_fluid-openfoam_transport-nutils - *elastic-tube-3d_fluid-openfoam_solid-calculix - # *elastic-tube-3d_fluid-openfoam_solid-fenics # excluded - too small values to compare - *flow-over-heated-plate_fluid-openfoam_solid-fenics - *flow-over-heated-plate_fluid-openfoam_solid-nutils - *flow-over-heated-plate_fluid-openfoam_solid-openfoam @@ -436,6 +443,9 @@ test_suites: - *turek-hron-fsi3_fluid-openfoam_solid-dealii - *quickstart_openfoam_cpp + # Excluded: + # *elastic-tube-3d_fluid-openfoam_solid-fenics # too small values to compare + su2-adapter: tutorials: - *perpendicular-flap_fluid-su2_solid-fenics From eb685b80f00ebbe4735712becae15601d6572bb7 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Fri, 29 May 2026 17:12:16 +0200 Subject: [PATCH 47/94] Add optional inputs to the system-tests-latest-components.yml workflow (#819) --- .../workflows/system-tests-latest-components.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/system-tests-latest-components.yml b/.github/workflows/system-tests-latest-components.yml index 1f30e6097..a13375bd0 100644 --- a/.github/workflows/system-tests-latest-components.yml +++ b/.github/workflows/system-tests-latest-components.yml @@ -5,6 +5,17 @@ on: - cron: "0 4 * * *" timezone: "Europe/Berlin" workflow_dispatch: + inputs: + suites: + description: 'Comma-separated test suites to run' + default: 'release_test' + required: false + type: string + system_tests_branch: + description: 'Branch to take the system tests from' + default: 'develop' + required: true + type: string jobs: gather-refs: @@ -162,7 +173,7 @@ jobs: needs: gather-refs uses: precice/tutorials/.github/workflows/run_testsuite_workflow.yml@develop with: - suites: release_test + suites: ${{ inputs.suites }} build_args: "PLATFORM:ubuntu_2404,\ PRECICE_REF:${{ needs.gather-refs.outputs.ref-precice }},\ PYTHON_BINDINGS_REF:${{ needs.gather-refs.outputs.ref-python-bindings }},\ @@ -173,5 +184,5 @@ jobs: OPENFOAM_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-openfoam-adapter }},\ SU2_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-su2-adapter }},\ TUTORIALS_REF:${{ needs.gather-refs.outputs.ref-tutorials }}" - system_tests_branch: develop + system_tests_branch: ${{ inputs.system_tests_branch }} log_level: "INFO" From 78d64fdebfdc071e737c474f9e2f23ce2819435c Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Fri, 29 May 2026 17:41:47 +0200 Subject: [PATCH 48/94] Prune Docker networks before and after running each system test (#820) --- .../generate_reference_results_workflow.yml | 3 -- .github/workflows/run_testsuite_workflow.yml | 3 -- changelog-entries/820.md | 1 + tools/tests/systemtests/Systemtest.py | 31 +++++++++++++++++++ 4 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 changelog-entries/820.md diff --git a/.github/workflows/generate_reference_results_workflow.yml b/.github/workflows/generate_reference_results_workflow.yml index f1b080638..224fef9cd 100644 --- a/.github/workflows/generate_reference_results_workflow.yml +++ b/.github/workflows/generate_reference_results_workflow.yml @@ -104,6 +104,3 @@ jobs: runs/*/system-tests-stdout.log runs/*/system-tests-stderr.log runs/*/*/system-tests_*.log - - name: tidy up the docker - run: | - docker network prune -f diff --git a/.github/workflows/run_testsuite_workflow.yml b/.github/workflows/run_testsuite_workflow.yml index 085c5286e..18a913ed3 100644 --- a/.github/workflows/run_testsuite_workflow.yml +++ b/.github/workflows/run_testsuite_workflow.yml @@ -93,6 +93,3 @@ jobs: runs/*/system-tests-stdout.log runs/*/system-tests-stderr.log runs/*/*/system-tests_*.log - - name: tidy up the docker - run: | - docker network prune -f diff --git a/changelog-entries/820.md b/changelog-entries/820.md new file mode 100644 index 000000000..af1ddb10d --- /dev/null +++ b/changelog-entries/820.md @@ -0,0 +1 @@ +- The system tests now call `docker network prune` on the host before and after executing a test [#820](https://github.com/precice/tutorials/pull/820) \ No newline at end of file diff --git a/tools/tests/systemtests/Systemtest.py b/tools/tests/systemtests/Systemtest.py index f438eac03..7674fb485 100644 --- a/tools/tests/systemtests/Systemtest.py +++ b/tools/tests/systemtests/Systemtest.py @@ -399,6 +399,35 @@ def __unpack_reference_results(self) -> Tuple[bool, str]: logging.error(error_message) return False, error_message + def _cleanup_docker_networks(self): + """ + Prunes the unused Docker networks, since there is an upper limit on the number of custom networks defined. + """ + logging.debug(f"Deleting unused Docker networks...") + stdout_data = [] + stderr_data = [] + try: + # Execute docker-network-prune command + process = subprocess.Popen(['docker', + 'network', + 'prune', + '-f'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + start_new_session=True, + cwd=self.system_test_dir) + try: + stdout, stderr = process.communicate(timeout=self.timeout) + except KeyboardInterrupt as k: + process.kill() + raise KeyboardInterrupt from k + except Exception as e: + logging.critical( + f"Systemtest {self} could not prune the Docker networks. This might prevent tests from starting.") + stdout_data.extend(stdout.decode().splitlines()) + stderr_data.extend(stderr.decode().splitlines()) + process.poll() + def _run_field_compare(self): """ Executes the field comparison step after unpacking reference results. @@ -635,6 +664,7 @@ def run(self, run_directory: Path): std_out: List[str] = [] std_err: List[str] = [] + self._cleanup_docker_networks() docker_build_result = self._build_docker() std_out.extend(docker_build_result.stdout_data) std_err.extend(docker_build_result.stderr_data) @@ -682,6 +712,7 @@ def run(self, run_directory: Path): fieldcompare_time=fieldcompare_result.runtime) # self.__cleanup() + self._cleanup_docker_networks() self.__write_logs(std_out, std_err) return SystemtestResult( True, From 25cdcea66dacc9c1b334f4091a3e8359c51071a5 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Fri, 29 May 2026 17:44:31 +0200 Subject: [PATCH 49/94] CI: Archive main log files before run files --- .../generate_reference_results_workflow.yml | 15 ++++++++------- .github/workflows/run_testsuite_workflow.yml | 14 +++++++------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.github/workflows/generate_reference_results_workflow.yml b/.github/workflows/generate_reference_results_workflow.yml index 224fef9cd..ebadd7ce1 100644 --- a/.github/workflows/generate_reference_results_workflow.yml +++ b/.github/workflows/generate_reference_results_workflow.yml @@ -89,13 +89,6 @@ jobs: fi git commit -m "${{inputs.commit_msg}}" git push - - name: Archive run files - if: failure() - uses: actions/upload-artifact@v7 - with: - name: system_tests_run_${{ github.run_id }}_${{ github.run_attempt }}_reference_full - path: | - runs/* - name: Archive main log files uses: actions/upload-artifact@v7 with: @@ -104,3 +97,11 @@ jobs: runs/*/system-tests-stdout.log runs/*/system-tests-stderr.log runs/*/*/system-tests_*.log + - name: Archive run files + if: failure() + uses: actions/upload-artifact@v7 + with: + name: system_tests_run_${{ github.run_id }}_${{ github.run_attempt }}_reference_full + path: | + runs/* + diff --git a/.github/workflows/run_testsuite_workflow.yml b/.github/workflows/run_testsuite_workflow.yml index 18a913ed3..2342c5c50 100644 --- a/.github/workflows/run_testsuite_workflow.yml +++ b/.github/workflows/run_testsuite_workflow.yml @@ -78,13 +78,6 @@ jobs: cd tools/tests python systemtests.py --build_args=${{ inputs.build_args}} --suites=${{ inputs.suites}} --log-level=${{ inputs.log_level}} cd ../../ - - name: Archive run files - if: ${{ failure() || inputs.upload_artifacts == 'TRUE' }} - uses: actions/upload-artifact@v7 - with: - name: system_tests_run_${{ github.run_id }}_${{ github.run_attempt }}_full - path: | - runs/* - name: Archive main log files uses: actions/upload-artifact@v7 with: @@ -93,3 +86,10 @@ jobs: runs/*/system-tests-stdout.log runs/*/system-tests-stderr.log runs/*/*/system-tests_*.log + - name: Archive run files + if: ${{ failure() || inputs.upload_artifacts == 'TRUE' }} + uses: actions/upload-artifact@v7 + with: + name: system_tests_run_${{ github.run_id }}_${{ github.run_attempt }}_full + path: | + runs/* From 9af04467947be7e9a0a50604ecbe2ac5924b9738 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Fri, 29 May 2026 23:44:36 +0200 Subject: [PATCH 50/94] Handle race conditions in partitioned-heat-conduction OpenFOAM solver build --- .../dirichlet-openfoam/run.sh | 14 +++++++++++--- .../neumann-openfoam/run.sh | 14 +++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/partitioned-heat-conduction/dirichlet-openfoam/run.sh b/partitioned-heat-conduction/dirichlet-openfoam/run.sh index b9449bf62..4ed2f6ade 100755 --- a/partitioned-heat-conduction/dirichlet-openfoam/run.sh +++ b/partitioned-heat-conduction/dirichlet-openfoam/run.sh @@ -4,10 +4,18 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 +# Build the heatTransfer solver, if not found. +# Race condition: when running both participants with OpenFOAM, +# they both need the same solver to be built. +# The first one builds it, the second one waits. if ! command -v heatTransfer > /dev/null 2>&1; then - echo "Building the heatTransfer OpenFOAM solver" - wclean ../solver-openfoam/ - wmake ../solver-openfoam/ + if [ ! -d "../solver-openfoam/Make/${WM_OPTIONS}" ]; then + echo "Building the heatTransfer OpenFOAM solver" + wmake ../solver-openfoam/ + else + echo "The executable heatTransfer is not found, but the build directory ../solver-openfoam/Make/${WM_OPTIONS} was detected. A build is probably in progress, waiting 20s..." + sleep 20 + fi fi blockMesh diff --git a/partitioned-heat-conduction/neumann-openfoam/run.sh b/partitioned-heat-conduction/neumann-openfoam/run.sh index b9449bf62..4ed2f6ade 100755 --- a/partitioned-heat-conduction/neumann-openfoam/run.sh +++ b/partitioned-heat-conduction/neumann-openfoam/run.sh @@ -4,10 +4,18 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 +# Build the heatTransfer solver, if not found. +# Race condition: when running both participants with OpenFOAM, +# they both need the same solver to be built. +# The first one builds it, the second one waits. if ! command -v heatTransfer > /dev/null 2>&1; then - echo "Building the heatTransfer OpenFOAM solver" - wclean ../solver-openfoam/ - wmake ../solver-openfoam/ + if [ ! -d "../solver-openfoam/Make/${WM_OPTIONS}" ]; then + echo "Building the heatTransfer OpenFOAM solver" + wmake ../solver-openfoam/ + else + echo "The executable heatTransfer is not found, but the build directory ../solver-openfoam/Make/${WM_OPTIONS} was detected. A build is probably in progress, waiting 20s..." + sleep 20 + fi fi blockMesh From 1ceb92028e5aa0cc076d3c50a1508b0b70a5fb8e Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Sat, 30 May 2026 00:22:38 +0200 Subject: [PATCH 51/94] Increase wait time in partitioned-heat-conduction OpenFOAM solver build --- partitioned-heat-conduction/dirichlet-openfoam/run.sh | 7 +++++-- partitioned-heat-conduction/neumann-openfoam/run.sh | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/partitioned-heat-conduction/dirichlet-openfoam/run.sh b/partitioned-heat-conduction/dirichlet-openfoam/run.sh index 4ed2f6ade..e59b27ca3 100755 --- a/partitioned-heat-conduction/dirichlet-openfoam/run.sh +++ b/partitioned-heat-conduction/dirichlet-openfoam/run.sh @@ -13,8 +13,11 @@ if ! command -v heatTransfer > /dev/null 2>&1; then echo "Building the heatTransfer OpenFOAM solver" wmake ../solver-openfoam/ else - echo "The executable heatTransfer is not found, but the build directory ../solver-openfoam/Make/${WM_OPTIONS} was detected. A build is probably in progress, waiting 20s..." - sleep 20 + echo "The executable heatTransfer is not found, but the build directory ../solver-openfoam/Make/${WM_OPTIONS} was detected. A build is probably in progress, waiting 30s..." + sleep 30 + if ! command -v heatTransfer > /dev/null 2>&1; then + exit 1 + fi fi fi diff --git a/partitioned-heat-conduction/neumann-openfoam/run.sh b/partitioned-heat-conduction/neumann-openfoam/run.sh index 4ed2f6ade..e59b27ca3 100755 --- a/partitioned-heat-conduction/neumann-openfoam/run.sh +++ b/partitioned-heat-conduction/neumann-openfoam/run.sh @@ -13,8 +13,11 @@ if ! command -v heatTransfer > /dev/null 2>&1; then echo "Building the heatTransfer OpenFOAM solver" wmake ../solver-openfoam/ else - echo "The executable heatTransfer is not found, but the build directory ../solver-openfoam/Make/${WM_OPTIONS} was detected. A build is probably in progress, waiting 20s..." - sleep 20 + echo "The executable heatTransfer is not found, but the build directory ../solver-openfoam/Make/${WM_OPTIONS} was detected. A build is probably in progress, waiting 30s..." + sleep 30 + if ! command -v heatTransfer > /dev/null 2>&1; then + exit 1 + fi fi fi From a69d8cc194e4d184aa2d23fe7d379248517df58c Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Sat, 30 May 2026 00:29:06 +0200 Subject: [PATCH 52/94] Increase wait time in partitioned-heat-conduction OpenFOAM solver build to 60s --- partitioned-heat-conduction/dirichlet-openfoam/run.sh | 4 ++-- partitioned-heat-conduction/neumann-openfoam/run.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/partitioned-heat-conduction/dirichlet-openfoam/run.sh b/partitioned-heat-conduction/dirichlet-openfoam/run.sh index e59b27ca3..bd5dc9268 100755 --- a/partitioned-heat-conduction/dirichlet-openfoam/run.sh +++ b/partitioned-heat-conduction/dirichlet-openfoam/run.sh @@ -13,8 +13,8 @@ if ! command -v heatTransfer > /dev/null 2>&1; then echo "Building the heatTransfer OpenFOAM solver" wmake ../solver-openfoam/ else - echo "The executable heatTransfer is not found, but the build directory ../solver-openfoam/Make/${WM_OPTIONS} was detected. A build is probably in progress, waiting 30s..." - sleep 30 + echo "The executable heatTransfer is not found, but the build directory ../solver-openfoam/Make/${WM_OPTIONS} was detected. A build is probably in progress, waiting 60s..." + sleep 60 if ! command -v heatTransfer > /dev/null 2>&1; then exit 1 fi diff --git a/partitioned-heat-conduction/neumann-openfoam/run.sh b/partitioned-heat-conduction/neumann-openfoam/run.sh index e59b27ca3..bd5dc9268 100755 --- a/partitioned-heat-conduction/neumann-openfoam/run.sh +++ b/partitioned-heat-conduction/neumann-openfoam/run.sh @@ -13,8 +13,8 @@ if ! command -v heatTransfer > /dev/null 2>&1; then echo "Building the heatTransfer OpenFOAM solver" wmake ../solver-openfoam/ else - echo "The executable heatTransfer is not found, but the build directory ../solver-openfoam/Make/${WM_OPTIONS} was detected. A build is probably in progress, waiting 30s..." - sleep 30 + echo "The executable heatTransfer is not found, but the build directory ../solver-openfoam/Make/${WM_OPTIONS} was detected. A build is probably in progress, waiting 60s..." + sleep 60 if ! command -v heatTransfer > /dev/null 2>&1; then exit 1 fi From 89c55e8816cb0b32e43044bc5d1e55515ed5114d Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Sat, 30 May 2026 00:40:08 +0200 Subject: [PATCH 53/94] Increase wait time in partitioned-heat-conduction OpenFOAM solver build to 90s --- partitioned-heat-conduction/dirichlet-openfoam/run.sh | 4 ++-- partitioned-heat-conduction/neumann-openfoam/run.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/partitioned-heat-conduction/dirichlet-openfoam/run.sh b/partitioned-heat-conduction/dirichlet-openfoam/run.sh index bd5dc9268..ddccc1a38 100755 --- a/partitioned-heat-conduction/dirichlet-openfoam/run.sh +++ b/partitioned-heat-conduction/dirichlet-openfoam/run.sh @@ -13,8 +13,8 @@ if ! command -v heatTransfer > /dev/null 2>&1; then echo "Building the heatTransfer OpenFOAM solver" wmake ../solver-openfoam/ else - echo "The executable heatTransfer is not found, but the build directory ../solver-openfoam/Make/${WM_OPTIONS} was detected. A build is probably in progress, waiting 60s..." - sleep 60 + echo "The executable heatTransfer is not found, but the build directory ../solver-openfoam/Make/${WM_OPTIONS} was detected. A build is probably in progress, waiting 90s..." + sleep 90 if ! command -v heatTransfer > /dev/null 2>&1; then exit 1 fi diff --git a/partitioned-heat-conduction/neumann-openfoam/run.sh b/partitioned-heat-conduction/neumann-openfoam/run.sh index bd5dc9268..ddccc1a38 100755 --- a/partitioned-heat-conduction/neumann-openfoam/run.sh +++ b/partitioned-heat-conduction/neumann-openfoam/run.sh @@ -13,8 +13,8 @@ if ! command -v heatTransfer > /dev/null 2>&1; then echo "Building the heatTransfer OpenFOAM solver" wmake ../solver-openfoam/ else - echo "The executable heatTransfer is not found, but the build directory ../solver-openfoam/Make/${WM_OPTIONS} was detected. A build is probably in progress, waiting 60s..." - sleep 60 + echo "The executable heatTransfer is not found, but the build directory ../solver-openfoam/Make/${WM_OPTIONS} was detected. A build is probably in progress, waiting 90s..." + sleep 90 if ! command -v heatTransfer > /dev/null 2>&1; then exit 1 fi From 706d3ad3f36b82daf6b4f08eb296c7807b0f2cac Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Sat, 30 May 2026 08:36:22 +0200 Subject: [PATCH 54/94] CI: Archive main log files both in success and failure --- .github/workflows/generate_reference_results_workflow.yml | 1 + .github/workflows/run_testsuite_workflow.yml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/generate_reference_results_workflow.yml b/.github/workflows/generate_reference_results_workflow.yml index ebadd7ce1..78b64efd7 100644 --- a/.github/workflows/generate_reference_results_workflow.yml +++ b/.github/workflows/generate_reference_results_workflow.yml @@ -90,6 +90,7 @@ jobs: git commit -m "${{inputs.commit_msg}}" git push - name: Archive main log files + if: ${{ always() }} uses: actions/upload-artifact@v7 with: name: system_tests_run_${{ github.run_id }}_${{ github.run_attempt }}_reference_logs diff --git a/.github/workflows/run_testsuite_workflow.yml b/.github/workflows/run_testsuite_workflow.yml index 2342c5c50..62be03a8a 100644 --- a/.github/workflows/run_testsuite_workflow.yml +++ b/.github/workflows/run_testsuite_workflow.yml @@ -79,6 +79,7 @@ jobs: python systemtests.py --build_args=${{ inputs.build_args}} --suites=${{ inputs.suites}} --log-level=${{ inputs.log_level}} cd ../../ - name: Archive main log files + if: ${{ always() }} uses: actions/upload-artifact@v7 with: name: system_tests_run_${{ github.run_id }}_${{ github.run_attempt }}_logs @@ -87,7 +88,7 @@ jobs: runs/*/system-tests-stderr.log runs/*/*/system-tests_*.log - name: Archive run files - if: ${{ failure() || inputs.upload_artifacts == 'TRUE' }} + if: ${{ failure() || inputs.upload_artifacts == 'TRUE' }} uses: actions/upload-artifact@v7 with: name: system_tests_run_${{ github.run_id }}_${{ github.run_attempt }}_full From 00a0756eef881fc3f20cfb7c16203c92fd39e37a Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Sat, 30 May 2026 09:05:28 +0200 Subject: [PATCH 55/94] partitioned-heat-conduction: Build OpenFOAM solver in a local build directory Avoid race conditions in building, accept an overwrite of the final binary. --- .../dirichlet-openfoam/run.sh | 17 ++++------------- .../neumann-openfoam/run.sh | 17 ++++------------- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/partitioned-heat-conduction/dirichlet-openfoam/run.sh b/partitioned-heat-conduction/dirichlet-openfoam/run.sh index ddccc1a38..f53e6cd52 100755 --- a/partitioned-heat-conduction/dirichlet-openfoam/run.sh +++ b/partitioned-heat-conduction/dirichlet-openfoam/run.sh @@ -5,20 +5,11 @@ set -e -u exec > >(tee --append "$LOGFILE") 2>&1 # Build the heatTransfer solver, if not found. -# Race condition: when running both participants with OpenFOAM, -# they both need the same solver to be built. -# The first one builds it, the second one waits. if ! command -v heatTransfer > /dev/null 2>&1; then - if [ ! -d "../solver-openfoam/Make/${WM_OPTIONS}" ]; then - echo "Building the heatTransfer OpenFOAM solver" - wmake ../solver-openfoam/ - else - echo "The executable heatTransfer is not found, but the build directory ../solver-openfoam/Make/${WM_OPTIONS} was detected. A build is probably in progress, waiting 90s..." - sleep 90 - if ! command -v heatTransfer > /dev/null 2>&1; then - exit 1 - fi - fi + echo "Building ../solver-openfoam in a temporary build directory and installing to ${FOAM_USER_APPBIN}/heatTransfer..." + cp -r ../solver-openfoam ./_solver-openfoam-copy + wmake ./_solver-openfoam-copy + rm -r ./_solver-openfoam-copy fi blockMesh diff --git a/partitioned-heat-conduction/neumann-openfoam/run.sh b/partitioned-heat-conduction/neumann-openfoam/run.sh index ddccc1a38..f53e6cd52 100755 --- a/partitioned-heat-conduction/neumann-openfoam/run.sh +++ b/partitioned-heat-conduction/neumann-openfoam/run.sh @@ -5,20 +5,11 @@ set -e -u exec > >(tee --append "$LOGFILE") 2>&1 # Build the heatTransfer solver, if not found. -# Race condition: when running both participants with OpenFOAM, -# they both need the same solver to be built. -# The first one builds it, the second one waits. if ! command -v heatTransfer > /dev/null 2>&1; then - if [ ! -d "../solver-openfoam/Make/${WM_OPTIONS}" ]; then - echo "Building the heatTransfer OpenFOAM solver" - wmake ../solver-openfoam/ - else - echo "The executable heatTransfer is not found, but the build directory ../solver-openfoam/Make/${WM_OPTIONS} was detected. A build is probably in progress, waiting 90s..." - sleep 90 - if ! command -v heatTransfer > /dev/null 2>&1; then - exit 1 - fi - fi + echo "Building ../solver-openfoam in a temporary build directory and installing to ${FOAM_USER_APPBIN}/heatTransfer..." + cp -r ../solver-openfoam ./_solver-openfoam-copy + wmake ./_solver-openfoam-copy + rm -r ./_solver-openfoam-copy fi blockMesh From 722a662cc3d2fe7e809bb530cc96fdcef62a9565 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Sat, 30 May 2026 12:42:17 +0200 Subject: [PATCH 56/94] Update elastic-tube-1d Fortran cases and add them to the tests (#823) --- changelog-entries/823.md | 1 + .../fluid-fortran-module/.gitignore | 1 + .../fluid-fortran-module/CMakeLists.txt | 2 +- elastic-tube-1d/fluid-fortran-module/run.sh | 8 +-- .../fluid-fortran-module/src/FluidSolver.f90 | 2 +- .../fluid-fortran/src/FluidSolver.f90 | 2 +- elastic-tube-1d/metadata.yaml | 50 ++++++++++++++----- .../fluid-cpp_solid-cpp.tar.gz | 4 +- .../fluid-cpp_solid-python.tar.gz | 4 +- ...fortran-module_solid-fortran-module.tar.gz | 3 ++ .../fluid-fortran_solid-fortran.tar.gz | 3 ++ .../fluid-python_solid-python.tar.gz | 4 +- .../reference_results.metadata | 10 ++-- .../solid-fortran-module/.gitignore | 1 + .../solid-fortran-module/CMakeLists.txt | 2 +- elastic-tube-1d/solid-fortran-module/run.sh | 16 +++--- .../solid-fortran-module/src/SolidSolver.f90 | 2 +- .../solid-fortran/src/SolidSolver.f90 | 2 +- tools/tests/reference_versions.yaml | 2 +- tools/tests/tests.yaml | 18 +++++++ 20 files changed, 96 insertions(+), 41 deletions(-) create mode 100644 changelog-entries/823.md create mode 100644 elastic-tube-1d/fluid-fortran-module/.gitignore create mode 100644 elastic-tube-1d/reference-results/fluid-fortran-module_solid-fortran-module.tar.gz create mode 100644 elastic-tube-1d/reference-results/fluid-fortran_solid-fortran.tar.gz create mode 100644 elastic-tube-1d/solid-fortran-module/.gitignore diff --git a/changelog-entries/823.md b/changelog-entries/823.md new file mode 100644 index 000000000..f5c6e7e8e --- /dev/null +++ b/changelog-entries/823.md @@ -0,0 +1 @@ +- Updated the elastic-tube-1d fortran-module cases to download the fortran-module into `thirdparty/` [#823](https://github.com/precice/tutorials/pull/823) \ No newline at end of file diff --git a/elastic-tube-1d/fluid-fortran-module/.gitignore b/elastic-tube-1d/fluid-fortran-module/.gitignore new file mode 100644 index 000000000..1066763d1 --- /dev/null +++ b/elastic-tube-1d/fluid-fortran-module/.gitignore @@ -0,0 +1 @@ +thirdparty/ \ No newline at end of file diff --git a/elastic-tube-1d/fluid-fortran-module/CMakeLists.txt b/elastic-tube-1d/fluid-fortran-module/CMakeLists.txt index 706fd822f..2db65502e 100644 --- a/elastic-tube-1d/fluid-fortran-module/CMakeLists.txt +++ b/elastic-tube-1d/fluid-fortran-module/CMakeLists.txt @@ -20,7 +20,7 @@ add_executable(FluidSolver src/FluidComputeSolution.f90 src/utilities.f90 src/FluidSolver.f90 - src/precice.f90 + thirdparty/precice.f90 ) target_link_libraries(FluidSolver PRIVATE precice::precice) diff --git a/elastic-tube-1d/fluid-fortran-module/run.sh b/elastic-tube-1d/fluid-fortran-module/run.sh index 0b6d315c7..a7e3326cb 100755 --- a/elastic-tube-1d/fluid-fortran-module/run.sh +++ b/elastic-tube-1d/fluid-fortran-module/run.sh @@ -4,9 +4,11 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -f src/precice.f90 ]; then - echo "Fetching precice.f90 (Module for Fortran bindings of preCICE)..." - curl -o src/precice.f90 https://raw.githubusercontent.com/precice/fortran-module/master/precice.f90 +if [ ! -f thirdparty/precice.f90 ]; then + # Get the preCICE Fortran module. Switch the branch with ./run.sh . + DEFAULT_BRANCH="main" + echo "Fetching precice.f90 from https://github.com/precice/fortran-module/tree/${1:-$DEFAULT_BRANCH}..." + curl --create-dirs -o thirdparty/precice.f90 "https://raw.githubusercontent.com/precice/fortran-module/${1:-$DEFAULT_BRANCH}/precice.f90" fi if [ ! -d build ]; then diff --git a/elastic-tube-1d/fluid-fortran-module/src/FluidSolver.f90 b/elastic-tube-1d/fluid-fortran-module/src/FluidSolver.f90 index f2b4485f5..3087c09c7 100644 --- a/elastic-tube-1d/fluid-fortran-module/src/FluidSolver.f90 +++ b/elastic-tube-1d/fluid-fortran-module/src/FluidSolver.f90 @@ -101,7 +101,7 @@ program FluidSolver vertexIDs(i) = i - 1 end do - call precicef_set_vertices(meshName, chunkLength, grid, vertexIDs, len_trim(meshName)) + call precicef_set_mesh_vertices(meshName, chunkLength, grid, vertexIDs, len_trim(meshName)) ! Check if Initial Data is Required and Write if Necessary call precicef_requires_initial_data(bool) diff --git a/elastic-tube-1d/fluid-fortran/src/FluidSolver.f90 b/elastic-tube-1d/fluid-fortran/src/FluidSolver.f90 index 3df2f935b..5efb0bf2b 100644 --- a/elastic-tube-1d/fluid-fortran/src/FluidSolver.f90 +++ b/elastic-tube-1d/fluid-fortran/src/FluidSolver.f90 @@ -100,7 +100,7 @@ program FluidSolver vertexIDs(i) = i - 1 end do - call precicef_set_vertices(meshName, chunkLength, grid, vertexIDs) + call precicef_set_mesh_vertices(meshName, chunkLength, grid, vertexIDs) ! Check if Initial Data is Required and Write if Necessary call precicef_requires_initial_data(bool) diff --git a/elastic-tube-1d/metadata.yaml b/elastic-tube-1d/metadata.yaml index 79e66ab61..e8fcc30a8 100644 --- a/elastic-tube-1d/metadata.yaml +++ b/elastic-tube-1d/metadata.yaml @@ -10,32 +10,56 @@ cases: fluid-cpp: participant: Fluid directory: ./fluid-cpp - run: mkdir build && cmake -S . -B build && cmake --build build && ./run.sh + run: ./run.sh component: bare - solid-cpp: - participant: Solid - directory: ./solid-cpp - run: mkdir build && cmake -S . -B build && cmake --build build && ./run.sh + fluid-fortran: + participant: Fluid + directory: ./fluid-fortran + run: ./run.sh component: bare - + + fluid-fortran-module: + participant: Fluid + directory: ./fluid-fortran-module + run: ./run.sh develop + component: bare + fluid-python: participant: Fluid directory: ./fluid-python run: ./run.sh component: python-bindings - - solid-python: - participant: Solid - directory: ./solid-python - run: ./run.sh - component: python-bindings - + fluid-rust: participant: Fluid directory: ./fluid-rust run: ./run.sh component: bare + + solid-cpp: + participant: Solid + directory: ./solid-cpp + run: ./run.sh + component: bare + + solid-fortran: + participant: Solid + directory: ./solid-fortran + run: ./run.sh + component: bare + + solid-fortran-module: + participant: Solid + directory: ./solid-fortran-module + run: ./run.sh develop + component: bare + + solid-python: + participant: Solid + directory: ./solid-python + run: ./run.sh + component: python-bindings solid-rust: participant: Solid diff --git a/elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz b/elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz index b9fec481f..09b084538 100644 --- a/elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz +++ b/elastic-tube-1d/reference-results/fluid-cpp_solid-cpp.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6cdb7515613c0d6ef5f7e3d86c08e4d0dd933ccec1b41c5459c956d8b68c84e0 -size 250418 +oid sha256:96e14acd91881112e38f7e4445329c565178353b62c1bebbc8a72a63d03cc0e6 +size 250333 diff --git a/elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz b/elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz index 9d0f43a77..6131ee3d1 100644 --- a/elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz +++ b/elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a87326b2673b9a17ce94a9c280d4c76664a9465946a1b18e9fa118c70be51a6 -size 25983 +oid sha256:6c79542944d1d623b8a39110e8534b7297a1f5fe62348c00e4e34279e23ae25b +size 25969 diff --git a/elastic-tube-1d/reference-results/fluid-fortran-module_solid-fortran-module.tar.gz b/elastic-tube-1d/reference-results/fluid-fortran-module_solid-fortran-module.tar.gz new file mode 100644 index 000000000..429320763 --- /dev/null +++ b/elastic-tube-1d/reference-results/fluid-fortran-module_solid-fortran-module.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6cf76ce3df2836f842c3db9928ba3fe97af7e14c6244f45e5c672379481254af +size 250849 diff --git a/elastic-tube-1d/reference-results/fluid-fortran_solid-fortran.tar.gz b/elastic-tube-1d/reference-results/fluid-fortran_solid-fortran.tar.gz new file mode 100644 index 000000000..01e80c00e --- /dev/null +++ b/elastic-tube-1d/reference-results/fluid-fortran_solid-fortran.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53809112177efdeec4c89e697aaf411c6203dbfb96414c8a1213219cff68c0a6 +size 250700 diff --git a/elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz b/elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz index 3057f1567..f35239055 100644 --- a/elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz +++ b/elastic-tube-1d/reference-results/fluid-python_solid-python.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fcd3ac4ecceddbe7c429af155e50175f38998fad1449b117aa8c60ef7b1bff12 -size 250792 +oid sha256:662da5d26698496aa97afea77df1c3cc928faa4432a96a893fc91decea12adfd +size 250944 diff --git a/elastic-tube-1d/reference-results/reference_results.metadata b/elastic-tube-1d/reference-results/reference_results.metadata index 82a75f601..aa78ae01d 100644 --- a/elastic-tube-1d/reference-results/reference_results.metadata +++ b/elastic-tube-1d/reference-results/reference_results.metadata @@ -11,9 +11,11 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-cpp_solid-cpp.tar.gz | 2026-05-28 11:46:06 | 6cdb7515613c0d6ef5f7e3d86c08e4d0dd933ccec1b41c5459c956d8b68c84e0 | -| fluid-python_solid-python.tar.gz | 2026-05-28 11:46:06 | fcd3ac4ecceddbe7c429af155e50175f38998fad1449b117aa8c60ef7b1bff12 | -| fluid-cpp_solid-python.tar.gz | 2026-05-28 11:46:06 | 3a87326b2673b9a17ce94a9c280d4c76664a9465946a1b18e9fa118c70be51a6 | +| fluid-fortran-module_solid-fortran-module.tar.gz | 2026-05-30 12:38:08 | 6cf76ce3df2836f842c3db9928ba3fe97af7e14c6244f45e5c672379481254af | +| fluid-cpp_solid-python.tar.gz | 2026-05-30 12:38:08 | 6c79542944d1d623b8a39110e8534b7297a1f5fe62348c00e4e34279e23ae25b | +| fluid-fortran_solid-fortran.tar.gz | 2026-05-30 12:38:08 | 53809112177efdeec4c89e697aaf411c6203dbfb96414c8a1213219cff68c0a6 | +| fluid-cpp_solid-cpp.tar.gz | 2026-05-30 12:38:08 | 96e14acd91881112e38f7e4445329c565178353b62c1bebbc8a72a63d03cc0e6 | +| fluid-python_solid-python.tar.gz | 2026-05-30 12:38:08 | 662da5d26698496aa97afea77df1c3cc928faa4432a96a893fc91decea12adfd | ## List of arguments used to generate the files @@ -34,7 +36,7 @@ We also include some information on the machine used to generate them | PRECICE_REF | v3.4.1 | | PYTHON_BINDINGS_REF | v3.4.0 | | SU2_ADAPTER_REF | 5abe79b | -| TUTORIALS_REF | 9cb7068 | +| TUTORIALS_REF | f3b3bc2 | | PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | diff --git a/elastic-tube-1d/solid-fortran-module/.gitignore b/elastic-tube-1d/solid-fortran-module/.gitignore new file mode 100644 index 000000000..1066763d1 --- /dev/null +++ b/elastic-tube-1d/solid-fortran-module/.gitignore @@ -0,0 +1 @@ +thirdparty/ \ No newline at end of file diff --git a/elastic-tube-1d/solid-fortran-module/CMakeLists.txt b/elastic-tube-1d/solid-fortran-module/CMakeLists.txt index a1be8258f..2dd4005f7 100644 --- a/elastic-tube-1d/solid-fortran-module/CMakeLists.txt +++ b/elastic-tube-1d/solid-fortran-module/CMakeLists.txt @@ -18,7 +18,7 @@ find_package(precice 3 REQUIRED CONFIG) add_executable(SolidSolver src/SolidComputeSolution.f90 src/SolidSolver.f90 - src/precice.f90 + thirdparty/precice.f90 ) target_link_libraries(SolidSolver PRIVATE precice::precice) diff --git a/elastic-tube-1d/solid-fortran-module/run.sh b/elastic-tube-1d/solid-fortran-module/run.sh index 337bd8c1f..4e9ca3dff 100755 --- a/elastic-tube-1d/solid-fortran-module/run.sh +++ b/elastic-tube-1d/solid-fortran-module/run.sh @@ -1,20 +1,20 @@ #!/usr/bin/env bash set -e -u -. ../../tools/log.sh || true +. ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 || true -if [ ! -f src/precice.f90 ]; then - echo "Fetching precice.f90 (Module for Fortran bindings of preCICE)..." - curl -o src/precice.f90 https://raw.githubusercontent.com/precice/fortran-module/master/precice.f90 +if [ ! -f thirdparty/precice.f90 ]; then + # Get the preCICE Fortran module. Switch the branch with ./run.sh . + DEFAULT_BRANCH="main" + echo "Fetching precice.f90 from https://github.com/precice/fortran-module/tree/${1:-$DEFAULT_BRANCH}..." + curl --create-dirs -o thirdparty/precice.f90 "https://raw.githubusercontent.com/precice/fortran-module/${1:-$DEFAULT_BRANCH}/precice.f90" fi if [ ! -d build ]; then mkdir build - cd build - cmake .. - cmake --build . - cd .. + cmake -S . -B build + cmake --build build fi ./build/SolidSolver ../precice-config.xml diff --git a/elastic-tube-1d/solid-fortran-module/src/SolidSolver.f90 b/elastic-tube-1d/solid-fortran-module/src/SolidSolver.f90 index 86be2925f..a10174295 100644 --- a/elastic-tube-1d/solid-fortran-module/src/SolidSolver.f90 +++ b/elastic-tube-1d/solid-fortran-module/src/SolidSolver.f90 @@ -63,7 +63,7 @@ program SolidSolver vertexIDs(i) = i - 1 ! 0-based indexing here end do - call precicef_set_vertices(meshName, chunkLength, grid, vertexIDs, & + call precicef_set_mesh_vertices(meshName, chunkLength, grid, vertexIDs, & len_trim(meshName)) ! Check if initial data is required and write if necessary diff --git a/elastic-tube-1d/solid-fortran/src/SolidSolver.f90 b/elastic-tube-1d/solid-fortran/src/SolidSolver.f90 index 9de1b1d63..bbd5e4716 100644 --- a/elastic-tube-1d/solid-fortran/src/SolidSolver.f90 +++ b/elastic-tube-1d/solid-fortran/src/SolidSolver.f90 @@ -59,7 +59,7 @@ program SolidSolver vertexIDs(i) = i - 1 ! 0-based indexing here end do - call precicef_set_vertices(meshName, chunkLength, grid, vertexIDs) + call precicef_set_mesh_vertices(meshName, chunkLength, grid, vertexIDs) ! Check if initial data is required and write if necessary call precicef_requires_initial_data(bool) diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index 376c123bd..7e2aa8743 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -21,7 +21,7 @@ OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 PRECICE_REF: "v3.4.1" PYTHON_BINDINGS_REF: "v3.4.0" SU2_ADAPTER_REF: "5abe79b" # develop, May 27, 2026 -TUTORIALS_REF: "33a2563" # other branch, May 29, 2026 +TUTORIALS_REF: "f3b3bc2" # other branch, May 29, 2026 # Additional settings PRECICE_PRESET: "production-audit" \ No newline at end of file diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index fc851e2bd..a45bb119e 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -53,6 +53,18 @@ test_suites: - solid-python max_time: 0.1 reference_result: ./elastic-tube-1d/reference-results/fluid-cpp_solid-python.tar.gz + - &elastic-tube-1d_fluid-fortran_solid-fortran + path: elastic-tube-1d + case_combination: + - fluid-fortran + - solid-fortran + reference_result: ./elastic-tube-1d/reference-results/fluid-fortran_solid-fortran.tar.gz + - &elastic-tube-1d_fluid-fortran-module_solid-fortran-module + path: elastic-tube-1d + case_combination: + - fluid-fortran-module + - solid-fortran-module + reference_result: ./elastic-tube-1d/reference-results/fluid-fortran-module_solid-fortran-module.tar.gz - &elastic-tube-1d_fluid-python_solid-python path: elastic-tube-1d case_combination: @@ -330,6 +342,8 @@ test_suites: - *channel-transport-reaction_fluid-fenics_chemical-fenics - *elastic-tube-1d_fluid-cpp_solid-cpp - *elastic-tube-1d_fluid-cpp_solid-python + - *elastic-tube-1d_fluid-fortran_solid-fortran + - *elastic-tube-1d_fluid-fortran-module_solid-fortran-module - *elastic-tube-1d_fluid-python_solid-python - *elastic-tube-3d_fluid-openfoam_solid-calculix - *flow-over-heated-plate_fluid-openfoam_solid-fenics @@ -402,6 +416,10 @@ test_suites: # Excluded: # *two-scale-heat-conduction_macro-dumux_micro-dumux # too small values to compare + fortran-module: + tutorials: + - *elastic-tube-1d_fluid-fortran-module_solid-fortran-module + micro-manager: tutorials: - *two-scale-heat-conduction_macro-dumux_micro-dumux From 400f6fd94ea2e5c58c5674842736c40c994077eb Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Sun, 31 May 2026 02:10:02 +0200 Subject: [PATCH 57/94] Add more tests to the system tests (#824) Co-authored-by: preCICE Tests VM --- .../generate_reference_results_manual.yml | 24 +- .../generate_reference_results_workflow.yml | 35 +-- .github/workflows/run_testsuite_manual.yml | 31 ++- .github/workflows/run_testsuite_workflow.yml | 2 +- .../system-tests-latest-components.yml | 5 +- breaking-dam-2d/metadata.yaml | 23 ++ .../fluid-openfoam_solid-calculix.tar.gz | 3 + .../reference_results.metadata | 71 ++++++ channel-transport-particles/metadata.yaml | 26 +++ .../.gitignore | 3 +- .../metadata.yaml | 28 +++ ...ler-fmi_fluid-openfoam_solid-python.tar.gz | 3 + .../reference_results.metadata | 71 ++++++ .../fluid1-openfoam/system/controlDict | 2 +- .../fluid2-openfoam/system/controlDict | 2 +- .../metadata.yaml | 30 +++ ...foam_fluid2-openfoam_solid-openfoam.tar.gz | 3 + .../reference_results.metadata | 71 ++++++ .../solid-openfoam/system/controlDict | 2 +- .../metadata.yaml | 20 ++ flow-over-heated-plate/metadata.yaml | 20 +- .../fluid-su2_solid-openfoam.tar.gz | 3 + .../reference_results.metadata | 6 +- oscillator-overlap/metadata.yaml | 20 ++ .../mass-left-python_mass-right-python.tar.gz | 3 + .../reference_results.metadata | 71 ++++++ oscillator/mass-left-fmi/.gitignore | 2 + oscillator/mass-left-fmi/fmi-settings.json | 2 +- oscillator/mass-left-fmi/requirements.txt | 3 +- oscillator/mass-left-fmi/run.sh | 22 +- oscillator/mass-right-fmi/.gitignore | 2 + oscillator/mass-right-fmi/fmi-settings.json | 2 +- oscillator/mass-right-fmi/requirements.txt | 3 +- oscillator/mass-right-fmi/run.sh | 22 +- oscillator/metadata.yaml | 32 +++ .../mass-left-fmi_mass-right-fmi.tar.gz | 3 + .../mass-left-python_mass-right-python.tar.gz | 3 + .../reference_results.metadata | 72 ++++++ oscillator/solver-fmi/requirements.txt | 3 +- .../metadata.yaml | 20 ++ .../fluid1-openfoam_fluid2-openfoam.tar.gz | 3 + .../reference_results.metadata | 71 ++++++ .../metadata.yaml | 32 +++ .../dirichlet-nutils_neumann-nutils.tar.gz | 3 + .../reference_results.metadata | 71 ++++++ .../metadata.yaml | 20 ++ .../left-fenics_right-fenics.tar.gz | 3 + .../reference_results.metadata | 71 ++++++ partitioned-pipe-multiscale/metadata.yaml | 14 ++ perpendicular-flap-stress/metadata.yaml | 20 ++ perpendicular-flap/metadata.yaml | 24 +- .../fluid-fake_solid-fake.tar.gz | 3 + .../fluid-openfoam_solid-nutils.tar.gz | 3 + .../reference_results.metadata | 5 +- resonant-circuit/metadata.yaml | 45 ++++ .../capacitor-python_coil-python.tar.gz | 3 + .../reference_results.metadata | 71 ++++++ tools/tests/reference_versions.yaml | 2 +- tools/tests/systemtests/Systemtest.py | 2 + tools/tests/systemtests/TestSuite.py | 2 +- tools/tests/tests.yaml | 216 +++++++++++++++++- two-scale-heat-conduction/metadata.yaml | 12 + .../macro-dumux_micro-dumux.tar.gz | 4 +- .../reference_results.metadata | 30 +-- volume-coupled-diffusion/metadata.yaml | 20 ++ .../reference_results.metadata | 71 ++++++ .../source-fenics_drain-fenics.tar.gz | 3 + volume-coupled-flow/.gitignore | 1 + volume-coupled-flow/fluid-openfoam/clean.sh | 4 + volume-coupled-flow/fluid-openfoam/run.sh | 4 + .../fluid-openfoam/system/controlDict | 2 +- volume-coupled-flow/metadata.yaml | 20 ++ volume-coupled-flow/precice-config.xml | 10 +- .../fluid-openfoam_source-nutils.tar.gz | 3 + .../reference_results.metadata | 71 ++++++ .../source-nutils/requirements.txt | 4 + volume-coupled-flow/source-nutils/run.sh | 7 + volume-coupled-flow/source-nutils/source.py | 2 +- water-hammer/metadata.yaml | 34 +++ ...-left-nutils_fluid3d-right-openfoam.tar.gz | 3 + .../reference_results.metadata | 71 ++++++ 81 files changed, 1700 insertions(+), 129 deletions(-) create mode 100644 breaking-dam-2d/metadata.yaml create mode 100644 breaking-dam-2d/reference-results/fluid-openfoam_solid-calculix.tar.gz create mode 100644 breaking-dam-2d/reference-results/reference_results.metadata create mode 100644 channel-transport-particles/metadata.yaml create mode 100644 flow-around-controlled-moving-cylinder/metadata.yaml create mode 100644 flow-around-controlled-moving-cylinder/reference-results/controller-fmi_fluid-openfoam_solid-python.tar.gz create mode 100644 flow-around-controlled-moving-cylinder/reference-results/reference_results.metadata create mode 100644 flow-over-heated-plate-partitioned-flow/metadata.yaml create mode 100644 flow-over-heated-plate-partitioned-flow/reference-results/fluid1-openfoam_fluid2-openfoam_solid-openfoam.tar.gz create mode 100644 flow-over-heated-plate-partitioned-flow/reference-results/reference_results.metadata create mode 100644 flow-over-heated-plate-steady-state/metadata.yaml create mode 100644 flow-over-heated-plate/reference-results/fluid-su2_solid-openfoam.tar.gz create mode 100644 oscillator-overlap/metadata.yaml create mode 100644 oscillator-overlap/reference-results/mass-left-python_mass-right-python.tar.gz create mode 100644 oscillator-overlap/reference-results/reference_results.metadata create mode 100644 oscillator/mass-left-fmi/.gitignore create mode 100644 oscillator/mass-right-fmi/.gitignore create mode 100644 oscillator/metadata.yaml create mode 100644 oscillator/reference-results/mass-left-fmi_mass-right-fmi.tar.gz create mode 100644 oscillator/reference-results/mass-left-python_mass-right-python.tar.gz create mode 100644 oscillator/reference-results/reference_results.metadata create mode 100644 partitioned-backwards-facing-step/metadata.yaml create mode 100644 partitioned-backwards-facing-step/reference-results/fluid1-openfoam_fluid2-openfoam.tar.gz create mode 100644 partitioned-backwards-facing-step/reference-results/reference_results.metadata create mode 100644 partitioned-heat-conduction-direct/metadata.yaml create mode 100644 partitioned-heat-conduction-direct/reference-results/dirichlet-nutils_neumann-nutils.tar.gz create mode 100644 partitioned-heat-conduction-direct/reference-results/reference_results.metadata create mode 100644 partitioned-heat-conduction-overlap/metadata.yaml create mode 100644 partitioned-heat-conduction-overlap/reference-results/left-fenics_right-fenics.tar.gz create mode 100644 partitioned-heat-conduction-overlap/reference-results/reference_results.metadata create mode 100644 perpendicular-flap-stress/metadata.yaml create mode 100644 perpendicular-flap/reference-results/fluid-fake_solid-fake.tar.gz create mode 100644 perpendicular-flap/reference-results/fluid-openfoam_solid-nutils.tar.gz create mode 100644 resonant-circuit/metadata.yaml create mode 100644 resonant-circuit/reference-results/capacitor-python_coil-python.tar.gz create mode 100644 resonant-circuit/reference-results/reference_results.metadata create mode 100644 volume-coupled-diffusion/metadata.yaml create mode 100644 volume-coupled-diffusion/reference-results/reference_results.metadata create mode 100644 volume-coupled-diffusion/reference-results/source-fenics_drain-fenics.tar.gz create mode 100644 volume-coupled-flow/.gitignore create mode 100644 volume-coupled-flow/metadata.yaml create mode 100644 volume-coupled-flow/reference-results/fluid-openfoam_source-nutils.tar.gz create mode 100644 volume-coupled-flow/reference-results/reference_results.metadata create mode 100644 volume-coupled-flow/source-nutils/requirements.txt create mode 100644 water-hammer/metadata.yaml create mode 100644 water-hammer/reference-results/fluid1d-left-nutils_fluid3d-right-openfoam.tar.gz create mode 100644 water-hammer/reference-results/reference_results.metadata diff --git a/.github/workflows/generate_reference_results_manual.yml b/.github/workflows/generate_reference_results_manual.yml index ec793cd18..a2fcfd706 100644 --- a/.github/workflows/generate_reference_results_manual.yml +++ b/.github/workflows/generate_reference_results_manual.yml @@ -2,37 +2,33 @@ name: Generate reference results (manual) on: workflow_dispatch: inputs: + suites: + description: 'Test suites to execute (comma-separated, see tests.yaml)' + default: 'release_test' + required: true + type: string from_ref: - description: 'Use the systemtests + tutorial metadata + reference_version from this ref' + description: 'Git branch to use and commit to' required: true type: string commit_msg: - description: 'Commit msg for commit that adds the reference results' + description: 'Commit message' default: "Add reference results" type: string - suites: - description: 'Comma-separated test suites to generate reference results for (leave empty for all)' - default: '' - required: false - type: string clean_docker: - description: 'Clean Docker before running' + description: 'Run docker-system-prune before running tests' default: 'FALSE' type: choice options: - 'FALSE' - 'TRUE' log_level: - description: 'Logging verbosity level used for the systemtests' + description: 'Logging verbosity of systemtests.py' default: 'INFO' - required: true type: choice options: - - 'DEBUG' - 'INFO' - - 'WARNING' - - 'ERROR' - - 'CRITICAL' + - 'DEBUG' jobs: generate_reference_results_manual: diff --git a/.github/workflows/generate_reference_results_workflow.yml b/.github/workflows/generate_reference_results_workflow.yml index 78b64efd7..4665d8e2a 100644 --- a/.github/workflows/generate_reference_results_workflow.yml +++ b/.github/workflows/generate_reference_results_workflow.yml @@ -2,38 +2,41 @@ name: Generate reference results workflow on: workflow_call: inputs: - from_ref: - description: 'Use the systemtests + tutorial metadata + reference_version from this ref' + suites: + description: 'Test suites to execute (comma-separated, see tests.yaml)' required: true type: string + from_ref: + description: 'Git branch to use and commit to' + default: 'develop' + type: string commit_msg: - description: 'Commit msg for commit that adds the reference results' + description: 'Commit message' default: "Add reference results" type: string - suites: - description: 'Comma-separated test suites to generate reference results for. If empty, all suites are generated.' - default: '' - required: false - type: string clean_docker: - description: 'Clean Docker before running' + description: 'Run docker-system-prune before running tests' default: 'FALSE' type: string log_level: - description: 'Logging verbosity level used for the systemtests' + description: 'Logging verbosity of systemtests.py' required: true type: string jobs: generate_reference_results: runs-on: [self-hosted, linux, x64, precice-tests-vm] steps: - - name: Display a quick job summary + - name: Report the input values in the summary run: | - echo "Initiated by: ${{ github.actor }}" - echo "Running generate_reference_results.py --log-level ${{inputs.log_level}}" - echo "Using Ref: ${{ inputs.from_ref }}" - echo "Suites filter: ${{ inputs.suites || 'all (no filter)' }}" - echo "Commit message on success: ${{ inputs.commit_msg }}" + { + echo "- Initiated by: @${{ github.actor }}" + echo "- Test suites: \`${{ inputs.suites || 'all (no filter)' }}\`" + echo "- Git branch to commit to: \`${{ inputs.from_ref }}\`" + echo "- Commit message: \`${{ inputs.commit_msg }}\`" + echo "- Run \`docker-system-prune\` before running tests: \`${{ inputs.clean_docker }}\`" + echo "- Log level: \`${{ inputs.log_level }}\`" + echo "- Running \`generate_reference_results.py --suite ${{inputs.suites}} --log-level ${{inputs.log_level}}\`" + } >> "$GITHUB_STEP_SUMMARY" - name: Move LFS URL to local LFS server run: | /home/precice/runners_root/scripts/make_lfs_local.sh diff --git a/.github/workflows/run_testsuite_manual.yml b/.github/workflows/run_testsuite_manual.yml index 5d42ed138..0a9958000 100644 --- a/.github/workflows/run_testsuite_manual.yml +++ b/.github/workflows/run_testsuite_manual.yml @@ -3,36 +3,33 @@ on: workflow_dispatch: inputs: suites: - description: 'Comma seperated testsuites to execute' + description: 'Test suites to execute (comma-separated)' required: true + default: 'release_test' type: string build_args: - description: 'Build arguments, if not specified defaults will be taken' - required: false + description: 'Build arguments (override reference_versions.yaml)' + default: 'PRECICE_REF:v3.4.1,TUTORIALS_REF:develop' type: string system_tests_branch: - description: 'Branch to take the system tests from (tools/tests/)' + description: 'Git ref for tools/tests/' default: 'develop' - required: true type: string - log_level: - description: 'Logging verbosity level used for the systemtests' - default: 'INFO' - required: true - type: choice - options: - - 'DEBUG' - - 'INFO' - - 'WARNING' - - 'ERROR' - - 'CRITICAL' upload_artifacts: - description: 'Upload artifacts also on success (not only on failure)' + description: 'Upload the complete case files also on success (always true for failure)' default: 'TRUE' type: choice options: - 'FALSE' - 'TRUE' + log_level: + description: 'Logging verbosity of systemtests.py' + default: 'INFO' + type: choice + options: + - 'INFO' + - 'DEBUG' + jobs: run_testsuite_manual: diff --git a/.github/workflows/run_testsuite_workflow.yml b/.github/workflows/run_testsuite_workflow.yml index 62be03a8a..fde53977f 100644 --- a/.github/workflows/run_testsuite_workflow.yml +++ b/.github/workflows/run_testsuite_workflow.yml @@ -3,7 +3,7 @@ on: workflow_call: inputs: suites: - description: 'Comma seperated testsuites to execute' + description: 'Test suites to execute (comma-separated)' required: true type: string build_args: diff --git a/.github/workflows/system-tests-latest-components.yml b/.github/workflows/system-tests-latest-components.yml index a13375bd0..13abbc137 100644 --- a/.github/workflows/system-tests-latest-components.yml +++ b/.github/workflows/system-tests-latest-components.yml @@ -7,14 +7,13 @@ on: workflow_dispatch: inputs: suites: - description: 'Comma-separated test suites to run' + description: 'Test suites to execute (comma-separated)' default: 'release_test' - required: false + required: true type: string system_tests_branch: description: 'Branch to take the system tests from' default: 'develop' - required: true type: string jobs: diff --git a/breaking-dam-2d/metadata.yaml b/breaking-dam-2d/metadata.yaml new file mode 100644 index 000000000..786fc691f --- /dev/null +++ b/breaking-dam-2d/metadata.yaml @@ -0,0 +1,23 @@ +name: Breaking dam 2D +path: breaking-dam-2d # relative to git repo +url: https://precice.org/tutorials-breaking-dam-2d.html + +participants: + - Fluid + - Solid + +cases: + fluid-openfoam: + participant: Fluid + directory: ./fluid-openfoam + run: ./run.sh + component: openfoam-adapter + + solid-calculix: + participant: Solid + directory: ./solid-calculix + run: ./run.sh + component: calculix-adapter + + + diff --git a/breaking-dam-2d/reference-results/fluid-openfoam_solid-calculix.tar.gz b/breaking-dam-2d/reference-results/fluid-openfoam_solid-calculix.tar.gz new file mode 100644 index 000000000..14e88e4e9 --- /dev/null +++ b/breaking-dam-2d/reference-results/fluid-openfoam_solid-calculix.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f449e04e0eb4d0fc865f3635f32bf7a293fc47837bb67bd09ee6b525a26b7cf +size 992246 diff --git a/breaking-dam-2d/reference-results/reference_results.metadata b/breaking-dam-2d/reference-results/reference_results.metadata new file mode 100644 index 000000000..a182e9086 --- /dev/null +++ b/breaking-dam-2d/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| fluid-openfoam_solid-calculix.tar.gz | 2026-05-30 20:40:55 | 0f449e04e0eb4d0fc865f3635f32bf7a293fc47837bb67bd09ee6b525a26b7cf | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | more-tests | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/channel-transport-particles/metadata.yaml b/channel-transport-particles/metadata.yaml new file mode 100644 index 000000000..b350e23d6 --- /dev/null +++ b/channel-transport-particles/metadata.yaml @@ -0,0 +1,26 @@ +name: Channel transport particles +path: channel-transport-particles +url: https://precice.org/tutorials-channel-transport-particles.html + +participants: + - Fluid + - Particles + +cases: + fluid-nutils: + participant: Fluid + directory: ./fluid-nutils + run: ./run.sh + component: nutils-adapter + + fluid-openfoam: + participant: Fluid + directory: ./fluid-openfoam + run: ./run.sh + component: openfoam-adapter + + # particles-mercurydpm: + # participant: Particles + # directory: ./particles-mercurydpm + # run: ./run.sh + # component: mercurydpm-adapter \ No newline at end of file diff --git a/flow-around-controlled-moving-cylinder/.gitignore b/flow-around-controlled-moving-cylinder/.gitignore index 043bb5f18..016def67a 100644 --- a/flow-around-controlled-moving-cylinder/.gitignore +++ b/flow-around-controlled-moving-cylinder/.gitignore @@ -1,3 +1,4 @@ controller-fmi/PIDcontroller.fmu controller-fmi/output/ -fluid-openfoam/0/ # Since we start from 0.orig +# The fluid-openfoam case uses a 0.orig +fluid-openfoam/0/ diff --git a/flow-around-controlled-moving-cylinder/metadata.yaml b/flow-around-controlled-moving-cylinder/metadata.yaml new file mode 100644 index 000000000..1b95b1469 --- /dev/null +++ b/flow-around-controlled-moving-cylinder/metadata.yaml @@ -0,0 +1,28 @@ +name: Flow around a controlled moving cylinder +path: flow-around-controlled-moving-cylinder +url: https://precice.org/tutorials-flow-around-controlled-moving-cylinder.html + +participants: + - Controller + - Fluid + - Solid + +cases: + controller-fmi: + participant: Controller + directory: ./controller-fmi + run: ./run.sh + component: python-bindings + + fluid-openfoam: + participant: Fluid + directory: ./fluid-openfoam + run: ./run.sh + component: openfoam-adapter + + solid-python: + participant: Solid + directory: ./solid-python + run: ./run.sh + component: python-bindings + \ No newline at end of file diff --git a/flow-around-controlled-moving-cylinder/reference-results/controller-fmi_fluid-openfoam_solid-python.tar.gz b/flow-around-controlled-moving-cylinder/reference-results/controller-fmi_fluid-openfoam_solid-python.tar.gz new file mode 100644 index 000000000..11545f75b --- /dev/null +++ b/flow-around-controlled-moving-cylinder/reference-results/controller-fmi_fluid-openfoam_solid-python.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e948b0f82d011cf61a34c37f5ea7ae0f93c5fe67b0ab3ce49d49f7ba05e6fc2 +size 61863 diff --git a/flow-around-controlled-moving-cylinder/reference-results/reference_results.metadata b/flow-around-controlled-moving-cylinder/reference-results/reference_results.metadata new file mode 100644 index 000000000..e98228b1d --- /dev/null +++ b/flow-around-controlled-moving-cylinder/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| controller-fmi_fluid-openfoam_solid-python.tar.gz | 2026-05-30 15:34:58 | 8e948b0f82d011cf61a34c37f5ea7ae0f93c5fe67b0ab3ce49d49f7ba05e6fc2 | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | more-tests | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/flow-over-heated-plate-partitioned-flow/fluid1-openfoam/system/controlDict b/flow-over-heated-plate-partitioned-flow/fluid1-openfoam/system/controlDict index 592cdb75a..de0414779 100644 --- a/flow-over-heated-plate-partitioned-flow/fluid1-openfoam/system/controlDict +++ b/flow-over-heated-plate-partitioned-flow/fluid1-openfoam/system/controlDict @@ -20,7 +20,7 @@ deltaT 0.01; writeControl runTime; -writeInterval 0.2; +writeInterval 0.1; purgeWrite 0; diff --git a/flow-over-heated-plate-partitioned-flow/fluid2-openfoam/system/controlDict b/flow-over-heated-plate-partitioned-flow/fluid2-openfoam/system/controlDict index 592cdb75a..de0414779 100644 --- a/flow-over-heated-plate-partitioned-flow/fluid2-openfoam/system/controlDict +++ b/flow-over-heated-plate-partitioned-flow/fluid2-openfoam/system/controlDict @@ -20,7 +20,7 @@ deltaT 0.01; writeControl runTime; -writeInterval 0.2; +writeInterval 0.1; purgeWrite 0; diff --git a/flow-over-heated-plate-partitioned-flow/metadata.yaml b/flow-over-heated-plate-partitioned-flow/metadata.yaml new file mode 100644 index 000000000..ddd86c96a --- /dev/null +++ b/flow-over-heated-plate-partitioned-flow/metadata.yaml @@ -0,0 +1,30 @@ +name: Flow over heated plate partitioned flow +path: flow-over-heated-plate-partitioned-flow # relative to git repo +url: https://precice.org/tutorials-flow-over-heated-plate-partitioned-flow.html + +participants: + - Fluid1 + - Fluid2 + - Solid + +cases: + fluid1-openfoam: + participant: Fluid1 + directory: ./fluid1-openfoam + run: ./run.sh + component: openfoam-adapter + + fluid2-openfoam: + participant: Fluid2 + directory: ./fluid2-openfoam + run: ./run.sh + component: openfoam-adapter + + solid-openfoam: + participant: Solid + directory: ./solid-openfoam + run: ./run.sh + component: openfoam-adapter + + + diff --git a/flow-over-heated-plate-partitioned-flow/reference-results/fluid1-openfoam_fluid2-openfoam_solid-openfoam.tar.gz b/flow-over-heated-plate-partitioned-flow/reference-results/fluid1-openfoam_fluid2-openfoam_solid-openfoam.tar.gz new file mode 100644 index 000000000..d3cb5e476 --- /dev/null +++ b/flow-over-heated-plate-partitioned-flow/reference-results/fluid1-openfoam_fluid2-openfoam_solid-openfoam.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab86d97f4c78282972e1ff9c4c5603a762e2e0d53442b067e408beabcc7b563a +size 48427 diff --git a/flow-over-heated-plate-partitioned-flow/reference-results/reference_results.metadata b/flow-over-heated-plate-partitioned-flow/reference-results/reference_results.metadata new file mode 100644 index 000000000..5200a89d8 --- /dev/null +++ b/flow-over-heated-plate-partitioned-flow/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| fluid1-openfoam_fluid2-openfoam_solid-openfoam.tar.gz | 2026-05-30 22:01:39 | ab86d97f4c78282972e1ff9c4c5603a762e2e0d53442b067e408beabcc7b563a | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | more-tests | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/flow-over-heated-plate-partitioned-flow/solid-openfoam/system/controlDict b/flow-over-heated-plate-partitioned-flow/solid-openfoam/system/controlDict index 7cb0aee74..29dd66867 100644 --- a/flow-over-heated-plate-partitioned-flow/solid-openfoam/system/controlDict +++ b/flow-over-heated-plate-partitioned-flow/solid-openfoam/system/controlDict @@ -21,7 +21,7 @@ deltaT 0.01; writeControl runTime; -writeInterval 0.2; +writeInterval 0.1; purgeWrite 0; diff --git a/flow-over-heated-plate-steady-state/metadata.yaml b/flow-over-heated-plate-steady-state/metadata.yaml new file mode 100644 index 000000000..1bc65d444 --- /dev/null +++ b/flow-over-heated-plate-steady-state/metadata.yaml @@ -0,0 +1,20 @@ +name: Flow over heated plate steady state +path: flow-over-heated-plate-steady-state # relative to git repo +url: https://precice.org/tutorials-flow-over-heated-plate-steady-state.html + +participants: + - Fluid + - Solid + +cases: + fluid-openfoam: + participant: Fluid + directory: ./fluid-openfoam + run: ./run.sh + component: openfoam-adapter + + # solid-codeaster: + # participant: Solid + # directory: ./solid-codeaster + # run: ./run.sh + # component: codeaster-adapter diff --git a/flow-over-heated-plate/metadata.yaml b/flow-over-heated-plate/metadata.yaml index 53bd7e3ab..bc88eb463 100644 --- a/flow-over-heated-plate/metadata.yaml +++ b/flow-over-heated-plate/metadata.yaml @@ -13,12 +13,30 @@ cases: run: ./run.sh component: openfoam-adapter + fluid-su2: + participant: Fluid + directory: ./fluid-su2 + run: ./run.sh + component: su2-adapter + + # solid-dunefem: + # partitipant: Solid + # directory: ./solid-dunefem + # run: ./run.sh + # component: ? + solid-fenics: participant: Solid directory: ./solid-fenics run: ./run.sh component: fenics-adapter - + + # solid-fenicsx: + # participant: Solid + # directory: ./solid-fenicsx + # run: ./run.sh + # component: fenicsx-adapter + solid-nutils: participant: Solid directory: ./solid-nutils diff --git a/flow-over-heated-plate/reference-results/fluid-su2_solid-openfoam.tar.gz b/flow-over-heated-plate/reference-results/fluid-su2_solid-openfoam.tar.gz new file mode 100644 index 000000000..01ddc9a17 --- /dev/null +++ b/flow-over-heated-plate/reference-results/fluid-su2_solid-openfoam.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f09992b4c0a472dc1a978d5187c3e9c2335d298c93a79081fd30abbb941bfa8 +size 10704 diff --git a/flow-over-heated-plate/reference-results/reference_results.metadata b/flow-over-heated-plate/reference-results/reference_results.metadata index d0f0654ae..3fcdb8497 100644 --- a/flow-over-heated-plate/reference-results/reference_results.metadata +++ b/flow-over-heated-plate/reference-results/reference_results.metadata @@ -11,9 +11,7 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-openfoam_solid-openfoam.tar.gz | 2026-05-28 11:46:06 | 7a92099a21a3043486453f0ceb1700ce79febad10e6e7ca6852435f31f5a9f7e | -| fluid-openfoam_solid-fenics.tar.gz | 2026-05-28 11:46:06 | 38a2d7a489dc943abd52a9565e92ca43a747490654929629231ce856906e4723 | -| fluid-openfoam_solid-nutils.tar.gz | 2026-05-28 11:46:06 | e5c5243767d2b24c473a985e239152c72dcf040f49bc02c99f1175a0e7db1835 | +| fluid-su2_solid-openfoam.tar.gz | 2026-05-30 15:48:12 | 9f09992b4c0a472dc1a978d5187c3e9c2335d298c93a79081fd30abbb941bfa8 | ## List of arguments used to generate the files @@ -34,7 +32,7 @@ We also include some information on the machine used to generate them | PRECICE_REF | v3.4.1 | | PYTHON_BINDINGS_REF | v3.4.0 | | SU2_ADAPTER_REF | 5abe79b | -| TUTORIALS_REF | 9cb7068 | +| TUTORIALS_REF | more-tests | | PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | diff --git a/oscillator-overlap/metadata.yaml b/oscillator-overlap/metadata.yaml new file mode 100644 index 000000000..9c77c5d7b --- /dev/null +++ b/oscillator-overlap/metadata.yaml @@ -0,0 +1,20 @@ +name: Oscillator overlap +path: oscillator-overlap +url: https://precice.org/tutorials-oscillator-overlap.html + +participants: + - Mass-Left + - Mass-Right + +cases: + mass-left-python: + participant: Mass-Left + directory: ./mass-left-python + run: ./run.sh + component: python-bindings + + mass-right-python: + participant: Mass-Right + directory: ./mass-right-python + run: ./run.sh + component: python-bindings \ No newline at end of file diff --git a/oscillator-overlap/reference-results/mass-left-python_mass-right-python.tar.gz b/oscillator-overlap/reference-results/mass-left-python_mass-right-python.tar.gz new file mode 100644 index 000000000..ba2019f4c --- /dev/null +++ b/oscillator-overlap/reference-results/mass-left-python_mass-right-python.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c04f630a7507838e7cb4b97d5af0ee1cf65a7055291e664603be3b8037f53ac3 +size 16098 diff --git a/oscillator-overlap/reference-results/reference_results.metadata b/oscillator-overlap/reference-results/reference_results.metadata new file mode 100644 index 000000000..19dfb6793 --- /dev/null +++ b/oscillator-overlap/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| mass-left-python_mass-right-python.tar.gz | 2026-05-30 14:00:24 | c04f630a7507838e7cb4b97d5af0ee1cf65a7055291e664603be3b8037f53ac3 | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 89c82d5 | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/oscillator/mass-left-fmi/.gitignore b/oscillator/mass-left-fmi/.gitignore new file mode 100644 index 000000000..63adc8640 --- /dev/null +++ b/oscillator/mass-left-fmi/.gitignore @@ -0,0 +1,2 @@ +Oscillator.fmu +output \ No newline at end of file diff --git a/oscillator/mass-left-fmi/fmi-settings.json b/oscillator/mass-left-fmi/fmi-settings.json index dc4d752c2..1b7006286 100644 --- a/oscillator/mass-left-fmi/fmi-settings.json +++ b/oscillator/mass-left-fmi/fmi-settings.json @@ -10,7 +10,7 @@ "mass.a": -197.392088022 }, "simulation_params": { - "fmu_file_name": "../solver-fmi/Oscillator.fmu", + "fmu_file_name": "./Oscillator.fmu", "output_file_name": "./output/trajectory-Mass-Left.csv", "output": ["mass.u", "mass.v"], "fmu_read_data_names": ["force_in"], diff --git a/oscillator/mass-left-fmi/requirements.txt b/oscillator/mass-left-fmi/requirements.txt index 10f7ea9df..66239ca8f 100644 --- a/oscillator/mass-left-fmi/requirements.txt +++ b/oscillator/mass-left-fmi/requirements.txt @@ -1 +1,2 @@ -fmiprecice \ No newline at end of file +fmiprecice +pandas \ No newline at end of file diff --git a/oscillator/mass-left-fmi/run.sh b/oscillator/mass-left-fmi/run.sh index 2d8940011..2e6f83142 100755 --- a/oscillator/mass-left-fmi/run.sh +++ b/oscillator/mass-left-fmi/run.sh @@ -4,16 +4,18 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -f ../solver-fmi/Oscillator.fmu ]; then - cd ../solver-fmi/fmu - rm -rf build - mkdir build - cd build - # Both FMI_VERSION=3 and FMI_VERSION=2 are supported - cmake -DFMI_TYPE=CS -DFMI_VERSION=3 .. - make - cp ./Oscillator.fmu ../.. - cd ../../../mass-left-fmi +if [ ! -f Oscillator.fmu ]; then + echo "Building a copy of ../solver-fmi/fmu..." + ( + cp -r ../solver-fmi _solver-fmi-copy + cd _solver-fmi-copy/fmu + rm -rf build && mkdir build && cd build + # Both FMI_VERSION=3 and FMI_VERSION=2 are supported + cmake -DFMI_TYPE=CS -DFMI_VERSION=3 .. + make + cp ./Oscillator.fmu ../../.. + ) + rm -r _solver-fmi-copy fi if [ ! -v PRECICE_TUTORIALS_NO_VENV ] diff --git a/oscillator/mass-right-fmi/.gitignore b/oscillator/mass-right-fmi/.gitignore new file mode 100644 index 000000000..63adc8640 --- /dev/null +++ b/oscillator/mass-right-fmi/.gitignore @@ -0,0 +1,2 @@ +Oscillator.fmu +output \ No newline at end of file diff --git a/oscillator/mass-right-fmi/fmi-settings.json b/oscillator/mass-right-fmi/fmi-settings.json index fc59edc20..3fdb5875b 100644 --- a/oscillator/mass-right-fmi/fmi-settings.json +++ b/oscillator/mass-right-fmi/fmi-settings.json @@ -10,7 +10,7 @@ "mass.a": 157.913670417 }, "simulation_params": { - "fmu_file_name": "../solver-fmi/Oscillator.fmu", + "fmu_file_name": "./Oscillator.fmu", "output_file_name": "./output/trajectory-Mass-Right.csv", "output": ["mass.u", "mass.v"], "fmu_read_data_names": ["force_in"], diff --git a/oscillator/mass-right-fmi/requirements.txt b/oscillator/mass-right-fmi/requirements.txt index 10f7ea9df..66239ca8f 100644 --- a/oscillator/mass-right-fmi/requirements.txt +++ b/oscillator/mass-right-fmi/requirements.txt @@ -1 +1,2 @@ -fmiprecice \ No newline at end of file +fmiprecice +pandas \ No newline at end of file diff --git a/oscillator/mass-right-fmi/run.sh b/oscillator/mass-right-fmi/run.sh index 7cd85afd9..0d1d37574 100755 --- a/oscillator/mass-right-fmi/run.sh +++ b/oscillator/mass-right-fmi/run.sh @@ -4,16 +4,18 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -f ../solver-fmi/Oscillator.fmu ]; then - cd ../solver-fmi/fmu - rm -rf build - mkdir build - cd build - # Both FMI_VERSION=3 and FMI_VERSION=2 are supported - cmake -DFMI_TYPE=CS -DFMI_VERSION=3 .. - make - cp ./Oscillator.fmu ../.. - cd ../../../mass-right-fmi +if [ ! -f Oscillator.fmu ]; then + echo "Building a copy of ../solver-fmi/fmu..." + ( + cp -r ../solver-fmi _solver-fmi-copy + cd _solver-fmi-copy/fmu + rm -rf build && mkdir build && cd build + # Both FMI_VERSION=3 and FMI_VERSION=2 are supported + cmake -DFMI_TYPE=CS -DFMI_VERSION=3 .. + make + cp ./Oscillator.fmu ../../.. + ) + rm -r _solver-fmi-copy fi if [ ! -v PRECICE_TUTORIALS_NO_VENV ] diff --git a/oscillator/metadata.yaml b/oscillator/metadata.yaml new file mode 100644 index 000000000..3bff0dddd --- /dev/null +++ b/oscillator/metadata.yaml @@ -0,0 +1,32 @@ +name: Oscillator +path: oscillator +url: https://precice.org/tutorials-oscillator.html + +participants: + - Mass-Left + - Mass-Right + +cases: + mass-left-fmi: + participant: Mass-Left + directory: ./mass-left-fmi + run: ./run.sh + component: python-bindings + + mass-left-python: + participant: Mass-Left + directory: ./mass-left-python + run: ./run.sh + component: python-bindings + + mass-right-fmi: + participant: Mass-Right + directory: ./mass-right-fmi + run: ./run.sh + component: python-bindings + + mass-right-python: + participant: Mass-Right + directory: ./mass-right-python + run: ./run.sh + component: python-bindings \ No newline at end of file diff --git a/oscillator/reference-results/mass-left-fmi_mass-right-fmi.tar.gz b/oscillator/reference-results/mass-left-fmi_mass-right-fmi.tar.gz new file mode 100644 index 000000000..1c9cb4ca8 --- /dev/null +++ b/oscillator/reference-results/mass-left-fmi_mass-right-fmi.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0655f286163324887695c6a8159e44471a9fb181e66da721f8c09c815d46f9d8 +size 15953 diff --git a/oscillator/reference-results/mass-left-python_mass-right-python.tar.gz b/oscillator/reference-results/mass-left-python_mass-right-python.tar.gz new file mode 100644 index 000000000..57a77bf30 --- /dev/null +++ b/oscillator/reference-results/mass-left-python_mass-right-python.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ceea3e0b2fa91501aad438012010115882b6a9264769d496d6c3a44d3471b7a9 +size 16275 diff --git a/oscillator/reference-results/reference_results.metadata b/oscillator/reference-results/reference_results.metadata new file mode 100644 index 000000000..79eab4b47 --- /dev/null +++ b/oscillator/reference-results/reference_results.metadata @@ -0,0 +1,72 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| mass-left-fmi_mass-right-fmi.tar.gz | 2026-05-30 14:40:41 | 0655f286163324887695c6a8159e44471a9fb181e66da721f8c09c815d46f9d8 | +| mass-left-python_mass-right-python.tar.gz | 2026-05-30 14:40:41 | ceea3e0b2fa91501aad438012010115882b6a9264769d496d6c3a44d3471b7a9 | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 19f9523 | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/oscillator/solver-fmi/requirements.txt b/oscillator/solver-fmi/requirements.txt index 10f7ea9df..66239ca8f 100644 --- a/oscillator/solver-fmi/requirements.txt +++ b/oscillator/solver-fmi/requirements.txt @@ -1 +1,2 @@ -fmiprecice \ No newline at end of file +fmiprecice +pandas \ No newline at end of file diff --git a/partitioned-backwards-facing-step/metadata.yaml b/partitioned-backwards-facing-step/metadata.yaml new file mode 100644 index 000000000..982e273fd --- /dev/null +++ b/partitioned-backwards-facing-step/metadata.yaml @@ -0,0 +1,20 @@ +name: Partitioned backwards facing step +path: partitioned-backwards-facing-step # relative to git repo +url: https://precice.org/tutorials-partitioned-backwards-facing-step.html + +participants: + - Fluid1 + - Fluid2 + +cases: + fluid1-openfoam: + participant: Fluid1 + directory: ./fluid1-openfoam + run: ./run.sh + component: openfoam-adapter + + fluid2-openfoam: + participant: Fluid2 + directory: ./fluid2-openfoam + run: ./run.sh + component: openfoam-adapter diff --git a/partitioned-backwards-facing-step/reference-results/fluid1-openfoam_fluid2-openfoam.tar.gz b/partitioned-backwards-facing-step/reference-results/fluid1-openfoam_fluid2-openfoam.tar.gz new file mode 100644 index 000000000..3457c7ae4 --- /dev/null +++ b/partitioned-backwards-facing-step/reference-results/fluid1-openfoam_fluid2-openfoam.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c496201a6ec9533da240a31406513332da8aa95f6984ba93810980598b050e3 +size 199706 diff --git a/partitioned-backwards-facing-step/reference-results/reference_results.metadata b/partitioned-backwards-facing-step/reference-results/reference_results.metadata new file mode 100644 index 000000000..f468d73fa --- /dev/null +++ b/partitioned-backwards-facing-step/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| fluid1-openfoam_fluid2-openfoam.tar.gz | 2026-05-30 22:27:06 | 2c496201a6ec9533da240a31406513332da8aa95f6984ba93810980598b050e3 | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | more-tests | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/partitioned-heat-conduction-direct/metadata.yaml b/partitioned-heat-conduction-direct/metadata.yaml new file mode 100644 index 000000000..7bab6e103 --- /dev/null +++ b/partitioned-heat-conduction-direct/metadata.yaml @@ -0,0 +1,32 @@ +name: Partitioned heat conduction direct +path: partitioned-heat-conduction-direct # relative to git repo +url: https://precice.org/tutorials-partitioned-heat-conduction-direct.html + +participants: + - Dirichlet + - Neumann + +cases: + dirichlet-nutils: + participant: Dirichlet + directory: ./dirichlet-nutils + run: ./run.sh + component: nutils-adapter + + # dirichlet-gismo: + # participant: Dirichlet + # directory: ./dirichlet-gismo + # run: ./run.sh + # component: gismo-adapter + + neumann-nutils: + participant: Neumann + directory: ./neumann-nutils + run: ./run.sh + component: nutils-adapter + + # neumann-gismo: + # participant: Neumann + # directory: ./neumann-gismo + # run: ./run.sh + # component: gismo-adapter diff --git a/partitioned-heat-conduction-direct/reference-results/dirichlet-nutils_neumann-nutils.tar.gz b/partitioned-heat-conduction-direct/reference-results/dirichlet-nutils_neumann-nutils.tar.gz new file mode 100644 index 000000000..16f367108 --- /dev/null +++ b/partitioned-heat-conduction-direct/reference-results/dirichlet-nutils_neumann-nutils.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:404199906b36767490361dda293bb33e5a2d68dcce6d5aa74d4f0fd9a04f30fe +size 1981 diff --git a/partitioned-heat-conduction-direct/reference-results/reference_results.metadata b/partitioned-heat-conduction-direct/reference-results/reference_results.metadata new file mode 100644 index 000000000..31fcfacbf --- /dev/null +++ b/partitioned-heat-conduction-direct/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| dirichlet-nutils_neumann-nutils.tar.gz | 2026-05-30 14:00:24 | 404199906b36767490361dda293bb33e5a2d68dcce6d5aa74d4f0fd9a04f30fe | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 89c82d5 | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/partitioned-heat-conduction-overlap/metadata.yaml b/partitioned-heat-conduction-overlap/metadata.yaml new file mode 100644 index 000000000..2e5ea2404 --- /dev/null +++ b/partitioned-heat-conduction-overlap/metadata.yaml @@ -0,0 +1,20 @@ +name: Partitioned heat conduction overlap +path: partitioned-heat-conduction-overlap # relative to git repo +url: https://precice.org/tutorials-partitioned-heat-conduction-overlap.html + +participants: + - Left + - Right + +cases: + left-fenics: + participant: Left + directory: ./left-fenics + run: ./run.sh + component: fenics-adapter + + right-fenics: + participant: Right + directory: ./right-fenics + run: ./run.sh + component: fenics-adapter diff --git a/partitioned-heat-conduction-overlap/reference-results/left-fenics_right-fenics.tar.gz b/partitioned-heat-conduction-overlap/reference-results/left-fenics_right-fenics.tar.gz new file mode 100644 index 000000000..ad1581f87 --- /dev/null +++ b/partitioned-heat-conduction-overlap/reference-results/left-fenics_right-fenics.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:995fcea6e3ab2983fc33724d0efc8d316fd346f135aa7e0c97e5652f6e06859b +size 6871 diff --git a/partitioned-heat-conduction-overlap/reference-results/reference_results.metadata b/partitioned-heat-conduction-overlap/reference-results/reference_results.metadata new file mode 100644 index 000000000..bfef8bcc3 --- /dev/null +++ b/partitioned-heat-conduction-overlap/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| left-fenics_right-fenics.tar.gz | 2026-05-30 14:00:24 | 995fcea6e3ab2983fc33724d0efc8d316fd346f135aa7e0c97e5652f6e06859b | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 89c82d5 | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/partitioned-pipe-multiscale/metadata.yaml b/partitioned-pipe-multiscale/metadata.yaml index 666e91026..74ba8ae04 100644 --- a/partitioned-pipe-multiscale/metadata.yaml +++ b/partitioned-pipe-multiscale/metadata.yaml @@ -4,6 +4,8 @@ url: https://precice.org/tutorials-partitioned-pipe-multiscale.html participants: - Fluid1DLeft + # - Fluid1DRight + # - Fluid3DLeft - Fluid3DRight cases: @@ -13,6 +15,18 @@ cases: run: ./run.sh component: nutils-adapter + # fluid1d-right-nutils: + # participant: Fluid1DRight + # directory: ./fluid1d-right-nutils + # run: ./run.sh + # component: nutils-adapter + + # fluid3d-left-openfoam: + # participant: Fluid3DLeft + # directory: ./fluid3d-left-openfoam + # run: ./run.sh + # component: openfoam-adapter + fluid3d-right-openfoam: participant: Fluid3DRight directory: ./fluid3d-right-openfoam diff --git a/perpendicular-flap-stress/metadata.yaml b/perpendicular-flap-stress/metadata.yaml new file mode 100644 index 000000000..0c609a703 --- /dev/null +++ b/perpendicular-flap-stress/metadata.yaml @@ -0,0 +1,20 @@ +name: Perpendicular flap stress +path: perpendicular-flap-stress +url: https://precice.org/tutorials-perpendicular-flap-stress.html + +participants: + - Fluid + - Solid + +cases: + fluid-openfoam: + participant: Fluid + directory: ./fluid-openfoam + run: ./run.sh + component: openfoam-adapter + + # solid-gismo: + # participant: Solid + # directory: ./solid-gismo + # run: ./run.sh + # component: gismo-adapter diff --git a/perpendicular-flap/metadata.yaml b/perpendicular-flap/metadata.yaml index fd26b7d28..0cacb742e 100644 --- a/perpendicular-flap/metadata.yaml +++ b/perpendicular-flap/metadata.yaml @@ -7,6 +7,12 @@ participants: - Solid cases: + fluid-fake: + participant: Fluid + directory: ./fluid-fake + run: ./run.sh + component: bare + fluid-nutils: participant: Fluid directory: ./fluid-nutils @@ -42,13 +48,25 @@ cases: # directory: ./solid-dune # run: ./run.sh # component: dune-adapter - + + solid-fake: + participant: Solid + directory: ./solid-fake + run: ./run.sh + component: bare + solid-fenics: participant: Solid directory: ./solid-fenics run: ./run.sh component: fenics-adapter - + + solid-nutils: + participant: Solid + directory: ./solid-nutils + run: ./run.sh + component: nutils-adapter + solid-openfoam: participant: Solid directory: ./solid-openfoam @@ -60,5 +78,3 @@ cases: # directory: ./solid-solids4foam # run: ./run.sh # component: openfoam-adapter - - diff --git a/perpendicular-flap/reference-results/fluid-fake_solid-fake.tar.gz b/perpendicular-flap/reference-results/fluid-fake_solid-fake.tar.gz new file mode 100644 index 000000000..851131750 --- /dev/null +++ b/perpendicular-flap/reference-results/fluid-fake_solid-fake.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:292e859f538422389088f694de66988a5550d1700fe5881e4a8f0c2e349f7647 +size 17340 diff --git a/perpendicular-flap/reference-results/fluid-openfoam_solid-nutils.tar.gz b/perpendicular-flap/reference-results/fluid-openfoam_solid-nutils.tar.gz new file mode 100644 index 000000000..f775666ef --- /dev/null +++ b/perpendicular-flap/reference-results/fluid-openfoam_solid-nutils.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8b622b7fa5a6b56dcdbfa09f077d78a38ae565fa2e4e9d286fc08d1de985a1e +size 56534 diff --git a/perpendicular-flap/reference-results/reference_results.metadata b/perpendicular-flap/reference-results/reference_results.metadata index 1e15b509b..9e8b4aff6 100644 --- a/perpendicular-flap/reference-results/reference_results.metadata +++ b/perpendicular-flap/reference-results/reference_results.metadata @@ -11,7 +11,8 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-nutils_solid-calculix.tar.gz | 2026-05-29 11:36:41 | 19316047524c56e1675cae8e80014bc6f845f074ce8b225b880fa7a7e140e567 | +| fluid-openfoam_solid-nutils.tar.gz | 2026-05-31 00:45:02 | d8b622b7fa5a6b56dcdbfa09f077d78a38ae565fa2e4e9d286fc08d1de985a1e | +| fluid-fake_solid-fake.tar.gz | 2026-05-31 00:45:02 | 292e859f538422389088f694de66988a5550d1700fe5881e4a8f0c2e349f7647 | ## List of arguments used to generate the files @@ -32,7 +33,7 @@ We also include some information on the machine used to generate them | PRECICE_REF | v3.4.1 | | PYTHON_BINDINGS_REF | v3.4.0 | | SU2_ADAPTER_REF | 5abe79b | -| TUTORIALS_REF | 0fc4468 | +| TUTORIALS_REF | more-tests | | PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | diff --git a/resonant-circuit/metadata.yaml b/resonant-circuit/metadata.yaml new file mode 100644 index 000000000..c46c1adbf --- /dev/null +++ b/resonant-circuit/metadata.yaml @@ -0,0 +1,45 @@ +name: Resonant circuit +path: resonant-circuit +url: https://precice.org/tutorials-resonant-circuit.html + +participants: + - Capacitor + - Coil + +cases: + # capacitor-julia: + # participant: Capacitor + # directory: ./capacitor-julia + # run: ./run.sh + # component: julia-bindings + + # capacitor-matlab: + # participant: Capacitor + # directory: ./capacitor-matlab + # run: ./run.sh + # component: matlab-bindings + + capacitor-python: + participant: Capacitor + directory: ./capacitor-python + run: ./run.sh + component: python-bindings + + # coil-julia: + # participant: Coil + # directory: ./coil-julia + # run: ./run.sh + # component: julia-bindings + + # coil-matlab: + # participant: Coil + # directory: ./coil-matlab + # run: ./run.sh + # component: matlab-bindings + + coil-python: + participant: Coil + directory: ./coil-python + run: ./run.sh + component: python-bindings + \ No newline at end of file diff --git a/resonant-circuit/reference-results/capacitor-python_coil-python.tar.gz b/resonant-circuit/reference-results/capacitor-python_coil-python.tar.gz new file mode 100644 index 000000000..5623609a9 --- /dev/null +++ b/resonant-circuit/reference-results/capacitor-python_coil-python.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f86feb2df61aff94c0bada250a8574f65b671d88af0431683e19e2f1730991e +size 2625 diff --git a/resonant-circuit/reference-results/reference_results.metadata b/resonant-circuit/reference-results/reference_results.metadata new file mode 100644 index 000000000..7204326cb --- /dev/null +++ b/resonant-circuit/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| capacitor-python_coil-python.tar.gz | 2026-05-30 14:00:24 | 5f86feb2df61aff94c0bada250a8574f65b671d88af0431683e19e2f1730991e | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | 89c82d5 | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index 7e2aa8743..124975bc6 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -21,7 +21,7 @@ OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 PRECICE_REF: "v3.4.1" PYTHON_BINDINGS_REF: "v3.4.0" SU2_ADAPTER_REF: "5abe79b" # develop, May 27, 2026 -TUTORIALS_REF: "f3b3bc2" # other branch, May 29, 2026 +TUTORIALS_REF: "develop" # Additional settings PRECICE_PRESET: "production-audit" \ No newline at end of file diff --git a/tools/tests/systemtests/Systemtest.py b/tools/tests/systemtests/Systemtest.py index 7674fb485..de9fab864 100644 --- a/tools/tests/systemtests/Systemtest.py +++ b/tools/tests/systemtests/Systemtest.py @@ -730,6 +730,7 @@ def run_for_reference_results(self, run_directory: Path): self.__prepare_for_run(run_directory) std_out: List[str] = [] std_err: List[str] = [] + self._cleanup_docker_networks() docker_build_result = self._build_docker() std_out.extend(docker_build_result.stdout_data) std_err.extend(docker_build_result.stderr_data) @@ -760,6 +761,7 @@ def run_for_reference_results(self, run_directory: Path): solver_time=docker_run_result.runtime, fieldcompare_time=0) + self._cleanup_docker_networks() self.__write_logs(std_out, std_err) return SystemtestResult( True, diff --git a/tools/tests/systemtests/TestSuite.py b/tools/tests/systemtests/TestSuite.py index dff317ad9..a498ca4bd 100644 --- a/tools/tests/systemtests/TestSuite.py +++ b/tools/tests/systemtests/TestSuite.py @@ -93,7 +93,7 @@ def from_yaml(cls, path, parsed_tutorials: Tutorials): timeouts_of_tutorial[tutorial].append(timeout_value) else: raise Exception( - f"Could not find the following cases {tutorial_case['case-combination']} in the current metadata of tutorial {tutorial.name}") + f"Could not find the case combination {tutorial_case['case_combination']} in the current metadata of tutorial {tutorial.name}, or it does not define all necessary participants.") testsuites.append(TestSuite(test_suite_name, case_combinations_of_tutorial, reference_results_of_tutorial, max_times_of_tutorial, max_time_windows_of_tutorial, timeouts_of_tutorial)) diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index a45bb119e..ba58b775d 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -11,6 +11,16 @@ # Skipped components are still mentioned in a comment. test_suites: + breaking-dam-2d: + tutorials: + - &breaking-dam-2d_fluid-openfoam_solid-calculix + path: breaking-dam-2d + case_combination: + - fluid-openfoam + - solid-calculix + max_time: 0.2 + reference_result: ./breaking-dam-2d/reference-results/fluid-openfoam_solid-calculix.tar.gz + channel-transport: tutorials: - &channel-transport_fluid-nutils_transport-nutils @@ -89,6 +99,17 @@ test_suites: max_time_windows: 1 reference_result: ./elastic-tube-3d/reference-results/fluid-openfoam_solid-fenics.tar.gz # Too small values, expected to fail the comparisons. + flow-around-controlled-moving-cylinder: + tutorials: + - &flow-around-controlled-moving-cylinder_controller-fmi_fluid-openfoam_solid-python + path: flow-around-controlled-moving-cylinder + case_combination: + - controller-fmi + - fluid-openfoam + - solid-python + max_time: 0.01 + reference_result: ./flow-around-controlled-moving-cylinder/reference-results/controller-fmi_fluid-openfoam_solid-python.tar.gz + flow-over-heated-plate: tutorials: - &flow-over-heated-plate_fluid-openfoam_solid-fenics @@ -110,6 +131,13 @@ test_suites: - fluid-openfoam - solid-openfoam reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz + - &flow-over-heated-plate_fluid-su2_solid-openfoam + path: flow-over-heated-plate + case_combination: + - fluid-su2 + - solid-openfoam + max_time: 0.1 + reference_result: ./flow-over-heated-plate/reference-results/fluid-su2_solid-openfoam.tar.gz flow-over-heated-plate-nearest-projection: tutorials: @@ -121,6 +149,17 @@ test_suites: max_time: 0.1 reference_result: ./flow-over-heated-plate-nearest-projection/reference-results/fluid-openfoam_solid-openfoam.tar.gz + flow-over-heated-plate-partitioned-flow: + tutorials: + - &flow-over-heated-plate-partitioned-flow_fluid1-openfoam_fluid2-openfoam_solid-openfoam + path: flow-over-heated-plate-partitioned-flow + case_combination: + - fluid1-openfoam + - fluid2-openfoam + - solid-openfoam + max_time: 0.1 + reference_result: ./flow-over-heated-plate-partitioned-flow/reference-results/fluid1-openfoam_fluid2-openfoam_solid-openfoam.tar.gz + flow-over-heated-plate-two-meshes: tutorials: - &flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix @@ -173,6 +212,40 @@ test_suites: max_time: 0.1 reference_result: ./multiple-perpendicular-flaps/reference-results/fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii.tar.gz + oscillator: + tutorials: + - &oscillator_mass-left-fmi_mass-right-fmi + path: oscillator + case_combination: + - mass-left-fmi + - mass-right-fmi + reference_result: ./oscillator/reference-results/mass-left-fmi_mass-right-fmi.tar.gz + - &oscillator_mass-left-python_mass-right-python + path: oscillator + case_combination: + - mass-left-python + - mass-right-python + reference_result: ./oscillator/reference-results/mass-left-python_mass-right-python.tar.gz + + oscillator-overlap: + tutorials: + - &oscillator-overlap_mass-left-python_mass-right-python + path: oscillator-overlap + case_combination: + - mass-left-python + - mass-right-python + reference_result: ./oscillator-overlap/reference-results/mass-left-python_mass-right-python.tar.gz + + partitioned-backwards-facing-step: + tutorials: + - &partitioned-backwards-facing-step_fluid1-openfoam_fluid2-openfoam + path: partitioned-backwards-facing-step + case_combination: + - fluid1-openfoam + - fluid2-openfoam + max_time: 0.5 + reference_result: ./partitioned-backwards-facing-step/reference-results/fluid1-openfoam_fluid2-openfoam.tar.gz + partitioned-elastic-beam: tutorials: - &partitioned-elastic-beam_dirichlet-calculix_neumann-calculix @@ -216,6 +289,25 @@ test_suites: - neumann-fenics reference_result: ./partitioned-heat-conduction-complex/reference-results/dirichlet-fenics_neumann-fenics.tar.gz + partitioned-heat-conduction-direct: + tutorials: + - &partitioned-heat-conduction-direct_dirichlet-nutils_neumann-nutils + path: partitioned-heat-conduction-direct + case_combination: + - dirichlet-nutils + - neumann-nutils + max_time: 0.3 + reference_result: ./partitioned-heat-conduction-direct/reference-results/dirichlet-nutils_neumann-nutils.tar.gz + + partitioned-heat-conduction-overlap: + tutorials: + - &partitioned-heat-conduction-overlap_left-fenics_right-fenics + path: partitioned-heat-conduction-overlap + case_combination: + - left-fenics + - right-fenics + reference_result: ./partitioned-heat-conduction-overlap/reference-results/left-fenics_right-fenics.tar.gz + partitioned-pipe: tutorials: - &partitioned-pipe_fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam @@ -242,6 +334,7 @@ test_suites: - fluid3d-right-openfoam max_time: 0.05 reference_result: ./partitioned-pipe-multiscale/reference-results/fluid1d-left-nutils_fluid3d-right-openfoam.tar.gz + # More case combinations are possible, but they requite calling set-case.sh partitioned-pipe-two-phase: tutorials: @@ -255,6 +348,13 @@ test_suites: perpendicular-flap: tutorials: + - &perpendicular-flap_fluid-fake_solid-fake + path: perpendicular-flap + case_combination: + - fluid-fake + - solid-fake + max_time: 0.1 + reference_result: ./perpendicular-flap/reference-results/fluid-fake_solid-fake.tar.gz - &perpendicular-flap_fluid-nutils_solid-calculix path: perpendicular-flap case_combination: @@ -282,6 +382,13 @@ test_suites: - solid-fenics max_time: 0.1 reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-fenics.tar.gz + - &perpendicular-flap_fluid-openfoam_solid-nutils + path: perpendicular-flap + case_combination: + - fluid-openfoam + - solid-nutils + max_time: 0.1 + reference_result: ./perpendicular-flap/reference-results/fluid-openfoam_solid-nutils.tar.gz - &perpendicular-flap_fluid-openfoam_solid-openfoam path: perpendicular-flap case_combination: @@ -297,6 +404,24 @@ test_suites: max_time: 0.1 reference_result: ./perpendicular-flap/reference-results/fluid-su2_solid-fenics.tar.gz + quickstart: + tutorials: + - &quickstart_openfoam_cpp + path: quickstart + case_combination: + - fluid-openfoam + - solid-cpp + reference_result: ./quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz + + resonant-circuit: + tutorials: + - &resonant-circuit_capacitor-python_coil-python + path: resonant-circuit + case_combination: + - capacitor-python + - coil-python + reference_result: ./resonant-circuit/reference-results/capacitor-python_coil-python.tar.gz + turek-hron-fsi3: tutorials: - &turek-hron-fsi3_fluid-nutils_solid-nutils @@ -323,21 +448,51 @@ test_suites: - macro-dumux - micro-dumux reference_result: ./two-scale-heat-conduction/reference-results/macro-dumux_micro-dumux.tar.gz # Too small values, expected to fail the comparisons. + - &two-scale-heat-conduction_macro-nutils_micro-nutils + path: two-scale-heat-conduction + case_combination: + - macro-nutils + - micro-nutils + max_time: 0.05 + reference_result: ./two-scale-heat-conduction/reference-results/macro-nutils_micro-nutils.tar.gz # Too small values, expected to fail the comparisons. - quickstart: + volume-coupled-diffusion: tutorials: - - &quickstart_openfoam_cpp - path: quickstart + - &volume-coupled-diffusion_source-fenics_drain-fenics + path: volume-coupled-diffusion + case_combination: + - source-fenics + - drain-fenics + max_time: 1.0 + reference_result: ./volume-coupled-diffusion/reference-results/source-fenics_drain-fenics.tar.gz + + volume-coupled-flow: + tutorials: + - &volume-coupled-flow_fluid-openfoam_source-nutils + path: volume-coupled-flow case_combination: - fluid-openfoam - - solid-cpp - reference_result: ./quickstart/reference-results/fluid-openfoam_solid-cpp.tar.gz + - source-nutils + max_time: 0.05 + reference_result: ./volume-coupled-flow/reference-results/fluid-openfoam_source-nutils.tar.gz + + water-hammer: + tutorials: + - &water-hammer_fluid1d-left-nutils_fluid3d-right-openfoam + path: water-hammer + case_combination: + - fluid1d-left-nutils + - fluid3d-right-openfoam + max_time: 0.05 + reference_result: ./water-hammer/reference-results/fluid1d-left-nutils_fluid3d-right-openfoam.tar.gz + # More case combinations are possible, but they requite calling set-case.sh ##################################################################### ## Test suites referring to the test suites defined above release_test: tutorials: + - *breaking-dam-2d_fluid-openfoam_solid-calculix - *channel-transport_fluid-openfoam_transport-nutils - *channel-transport-reaction_fluid-fenics_chemical-fenics - *elastic-tube-1d_fluid-cpp_solid-cpp @@ -346,30 +501,44 @@ test_suites: - *elastic-tube-1d_fluid-fortran-module_solid-fortran-module - *elastic-tube-1d_fluid-python_solid-python - *elastic-tube-3d_fluid-openfoam_solid-calculix + - *flow-around-controlled-moving-cylinder_controller-fmi_fluid-openfoam_solid-python - *flow-over-heated-plate_fluid-openfoam_solid-fenics - *flow-over-heated-plate_fluid-openfoam_solid-nutils - *flow-over-heated-plate_fluid-openfoam_solid-openfoam + - *flow-over-heated-plate_fluid-su2_solid-openfoam - *flow-over-heated-plate-nearest-projection_fluid-openfoam_solid-openfoam + - *flow-over-heated-plate-partitioned-flow_fluid1-openfoam_fluid2-openfoam_solid-openfoam - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix - *free-flow-over-porous-media_free-flow-dumux_porous-media-dumux - *heat-exchanger_fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam - *heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix - *multiple-perpendicular-flaps_fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii + - *oscillator_mass-left-fmi_mass-right-fmi + - *oscillator_mass-left-python_mass-right-python + - *oscillator-overlap_mass-left-python_mass-right-python + - *partitioned-backwards-facing-step_fluid1-openfoam_fluid2-openfoam - *partitioned-elastic-beam_dirichlet-calculix_neumann-calculix - *partitioned-heat-conduction_dirichlet-fenics_neumann-fenics - *partitioned-heat-conduction_dirichlet-openfoam_neumann-openfoam - *partitioned-heat-conduction-complex_dirichlet-fenics_neumann-fenics + - *partitioned-heat-conduction-direct_dirichlet-nutils_neumann-nutils + - *partitioned-heat-conduction-overlap_left-fenics_right-fenics - *partitioned-pipe_fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam - *partitioned-pipe_fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam - *partitioned-pipe-multiscale_fluid1d-left-nutils_fluid3d-right-openfoam - *partitioned-pipe-two-phase_fluid1-openfoam_fluid2-openfoam + - *perpendicular-flap_fluid-fake_solid-fake - *perpendicular-flap_fluid-openfoam_solid-calculix - *perpendicular-flap_fluid-openfoam_solid-dealii - *perpendicular-flap_fluid-openfoam_solid-fenics - *perpendicular-flap_fluid-openfoam_solid-openfoam - *perpendicular-flap_fluid-su2_solid-fenics - *quickstart_openfoam_cpp + - *resonant-circuit_capacitor-python_coil-python - *turek-hron-fsi3_fluid-openfoam_solid-dealii + - *volume-coupled-diffusion_source-fenics_drain-fenics + - *volume-coupled-flow_fluid-openfoam_source-nutils + - *water-hammer_fluid1d-left-nutils_fluid3d-right-openfoam # These test suites take longer to run. They are available, but not regularly executed. extra: @@ -386,6 +555,7 @@ test_suites: calculix-adapter: tutorials: + - *breaking-dam-2d_fluid-openfoam_solid-calculix - *elastic-tube-3d_fluid-openfoam_solid-calculix - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix - *heat-exchanger_fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam @@ -410,11 +580,17 @@ test_suites: - *flow-over-heated-plate_fluid-openfoam_solid-fenics - *partitioned-heat-conduction_dirichlet-fenics_neumann-fenics - *partitioned-heat-conduction-complex_dirichlet-fenics_neumann-fenics + - *partitioned-heat-conduction-overlap_left-fenics_right-fenics - *perpendicular-flap_fluid-openfoam_solid-fenics - *perpendicular-flap_fluid-su2_solid-fenics + - *volume-coupled-diffusion_source-fenics_drain-fenics # Excluded: - # *two-scale-heat-conduction_macro-dumux_micro-dumux # too small values to compare + # *elastic-tube-3d_fluid-openfoam_solid-fenics # too small values to compare + + fmi-runner: + tutorials: + - *oscillator_mass-left-fmi_mass-right-fmi fortran-module: tutorials: @@ -422,10 +598,8 @@ test_suites: micro-manager: tutorials: - - *two-scale-heat-conduction_macro-dumux_micro-dumux - - # Excluded: - # *two-scale-heat-conduction_macro-dumux_micro-dumux # too small values to compare + - *two-scale-heat-conduction_macro-dumux_micro-dumux # too small values to compare + - *two-scale-heat-conduction_macro-nutils_micro-nutils # too small values to compare nutils-adapter: # Not a repository tutorials: @@ -433,22 +607,33 @@ test_suites: - *channel-transport_fluid-nutils_transport-nutils - *flow-over-heated-plate_fluid-openfoam_solid-nutils - *partitioned-heat-conduction_dirichlet-nutils_neumann-nutils + - *partitioned-heat-conduction-direct_dirichlet-nutils_neumann-nutils - *partitioned-pipe-multiscale_fluid1d-left-nutils_fluid3d-right-openfoam - *perpendicular-flap_fluid-nutils_solid-calculix - *turek-hron-fsi3_fluid-nutils_solid-nutils + - *volume-coupled-flow_fluid-openfoam_source-nutils + - *water-hammer_fluid1d-left-nutils_fluid3d-right-openfoam + + # Excluded: + # *two-scale-heat-conduction_macro-nutils_micro-nutils # too small values to compare openfoam-adapter: tutorials: + - *breaking-dam-2d_fluid-openfoam_solid-calculix - *channel-transport_fluid-openfoam_transport-nutils - *elastic-tube-3d_fluid-openfoam_solid-calculix + - *flow-around-controlled-moving-cylinder_controller-fmi_fluid-openfoam_solid-python - *flow-over-heated-plate_fluid-openfoam_solid-fenics - *flow-over-heated-plate_fluid-openfoam_solid-nutils - *flow-over-heated-plate_fluid-openfoam_solid-openfoam - *flow-over-heated-plate-nearest-projection_fluid-openfoam_solid-openfoam + - *flow-over-heated-plate-partitioned-flow_fluid1-openfoam_fluid2-openfoam_solid-openfoam - *flow-over-heated-plate-two-meshes_fluid-openfoam_solid-calculix + - *flow-over-heated-plate_fluid-su2_solid-openfoam - *heat-exchanger_fluid-inner-openfoam_solid-calculix_fluid-outer-openfoam - *heat-exchanger-simplified_fluid-top-openfoam_fluid-bottom-openfoam_solid-calculix - *multiple-perpendicular-flaps_fluid-openfoam_solid-upstream-dealii_solid-downstream-dealii + - *partitioned-backwards-facing-step_fluid1-openfoam_fluid2-openfoam - *partitioned-heat-conduction_dirichlet-openfoam_neumann-openfoam - *partitioned-pipe_fluid1-openfoam-pimplefoam_fluid2-openfoam-pimplefoam - *partitioned-pipe_fluid1-openfoam-sonicliquidfoam_fluid2-openfoam-sonicliquidfoam @@ -460,15 +645,22 @@ test_suites: - *perpendicular-flap_fluid-openfoam_solid-openfoam - *turek-hron-fsi3_fluid-openfoam_solid-dealii - *quickstart_openfoam_cpp + - *volume-coupled-flow_fluid-openfoam_source-nutils + - *water-hammer_fluid1d-left-nutils_fluid3d-right-openfoam # Excluded: # *elastic-tube-3d_fluid-openfoam_solid-fenics # too small values to compare su2-adapter: tutorials: + - *flow-over-heated-plate_fluid-su2_solid-openfoam - *perpendicular-flap_fluid-su2_solid-fenics -# A test suite used for the system tests development + # A test suite used for the system tests development system-tests-dev: tutorials: - - *elastic-tube-1d_fluid-cpp_solid-cpp \ No newline at end of file + - *elastic-tube-1d_fluid-cpp_solid-cpp + + selected: + tutorials: + - *two-scale-heat-conduction_macro-nutils_micro-nutils \ No newline at end of file diff --git a/two-scale-heat-conduction/metadata.yaml b/two-scale-heat-conduction/metadata.yaml index 75d465531..6c7c1dafb 100644 --- a/two-scale-heat-conduction/metadata.yaml +++ b/two-scale-heat-conduction/metadata.yaml @@ -12,9 +12,21 @@ cases: directory: ./macro-dumux run: ./run.sh -l /home/precice/dumux component: dumux-adapter + + macro-nutils: + participant: Macro + directory: ./macro-nutils + run: ./run.sh -l /home/precice/nutils + component: nutils-adapter micro-dumux: participant: Micro-Manager directory: ./micro-dumux run: source /home/precice/venv/bin/activate && ./run.sh -l /home/precice/dumux component: dumux-adapter + + micro-nutils: + participant: Micro-Manager + directory: ./micro-nutils + run: source /home/precice/venv/bin/activate && ./run.sh -l /home/precice/nutils + component: nutils-adapter \ No newline at end of file diff --git a/two-scale-heat-conduction/reference-results/macro-dumux_micro-dumux.tar.gz b/two-scale-heat-conduction/reference-results/macro-dumux_micro-dumux.tar.gz index 83a2a6c45..518abf212 100644 --- a/two-scale-heat-conduction/reference-results/macro-dumux_micro-dumux.tar.gz +++ b/two-scale-heat-conduction/reference-results/macro-dumux_micro-dumux.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b08f5a2e5cf0535bc7c08bbf9ddc9dbaa875363b2c030bc0ee25de654cb7e027 -size 4788 +oid sha256:446c1aee6ec3cabd76de63838ae3cd02668b38ad72b405d5b50b19b465ea5011 +size 2270 diff --git a/two-scale-heat-conduction/reference-results/reference_results.metadata b/two-scale-heat-conduction/reference-results/reference_results.metadata index b769e723a..585e72a3a 100644 --- a/two-scale-heat-conduction/reference-results/reference_results.metadata +++ b/two-scale-heat-conduction/reference-results/reference_results.metadata @@ -11,36 +11,36 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| macro-dumux_micro-dumux.tar.gz | 2026-05-11 07:45:27 | b08f5a2e5cf0535bc7c08bbf9ddc9dbaa875363b2c030bc0ee25de654cb7e027 | +| macro-dumux_micro-dumux.tar.gz | 2026-05-31 01:04:27 | 446c1aee6ec3cabd76de63838ae3cd02668b38ad72b405d5b50b19b465ea5011 | ## List of arguments used to generate the files | name | value | |------|------| -| PRECICE_REF | d199bb3 | -| PRECICE_PRESET | production-audit | -| OPENFOAM_EXECUTABLE | openfoam2312 | -| OPENFOAM_ADAPTER_REF | v1.3.1 | -| PYTHON_BINDINGS_REF | v3.2.0 | -| FENICS_ADAPTER_REF | v2.2.0 | -| TUTORIALS_REF | b18b3ef | | PLATFORM | ubuntu_2404 | | CALCULIX_VERSION | 2.20 | -| CALCULIX_ADAPTER_REF | v2.20.1 | -| SU2_VERSION | 7.5.1 | -| SU2_ADAPTER_REF | 64d4aff | -| DEALII_ADAPTER_REF | 02c5d18 | | DUNE_VERSION | 2.9 | | DUMUX_VERSION | 3.7 | -| DUMUX_ADAPTER_REF | 0e914bb | -| MICRO_MANAGER_REF | v0.8.0 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | more-tests | +| PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | ## Information about the machine ### uname -a -Linux precice-tests 5.15.0-177-generic #187-Ubuntu SMP Sat Apr 11 22:54:33 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux ### lscpu diff --git a/volume-coupled-diffusion/metadata.yaml b/volume-coupled-diffusion/metadata.yaml new file mode 100644 index 000000000..c80aeca3f --- /dev/null +++ b/volume-coupled-diffusion/metadata.yaml @@ -0,0 +1,20 @@ +name: Volume coupled diffusion +path: volume-coupled-diffusion # relative to git repo +url: https://precice.org/tutorials-volume-coupled-diffusion.html + +participants: + - Source + - Drain + +cases: + source-fenics: + participant: Source + directory: ./source-fenics + run: ./run.sh + component: fenics-adapter + + drain-fenics: + participant: Drain + directory: ./drain-fenics + run: ./run.sh + component: fenics-adapter diff --git a/volume-coupled-diffusion/reference-results/reference_results.metadata b/volume-coupled-diffusion/reference-results/reference_results.metadata new file mode 100644 index 000000000..8f6d0ed50 --- /dev/null +++ b/volume-coupled-diffusion/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| source-fenics_drain-fenics.tar.gz | 2026-05-31 00:09:19 | 5e6f023cd17fbd825c6deb2008a6c025d980dcf5a394b36a02c33f727bc14dd2 | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | more-tests | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/volume-coupled-diffusion/reference-results/source-fenics_drain-fenics.tar.gz b/volume-coupled-diffusion/reference-results/source-fenics_drain-fenics.tar.gz new file mode 100644 index 000000000..716ab7383 --- /dev/null +++ b/volume-coupled-diffusion/reference-results/source-fenics_drain-fenics.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e6f023cd17fbd825c6deb2008a6c025d980dcf5a394b36a02c33f727bc14dd2 +size 26670 diff --git a/volume-coupled-flow/.gitignore b/volume-coupled-flow/.gitignore new file mode 100644 index 000000000..0e6291ec5 --- /dev/null +++ b/volume-coupled-flow/.gitignore @@ -0,0 +1 @@ +fluid-participant-finished.log \ No newline at end of file diff --git a/volume-coupled-flow/fluid-openfoam/clean.sh b/volume-coupled-flow/fluid-openfoam/clean.sh index b64fc5101..01efca4ae 100755 --- a/volume-coupled-flow/fluid-openfoam/clean.sh +++ b/volume-coupled-flow/fluid-openfoam/clean.sh @@ -4,3 +4,7 @@ set -e -u . ../../tools/cleaning-tools.sh clean_openfoam . + +# Necessary signal file for the system tests, +# to keep the Source container running till the end +rm -f ../fluid-participant-finished.log \ No newline at end of file diff --git a/volume-coupled-flow/fluid-openfoam/run.sh b/volume-coupled-flow/fluid-openfoam/run.sh index ade8a7a98..93416a154 100755 --- a/volume-coupled-flow/fluid-openfoam/run.sh +++ b/volume-coupled-flow/fluid-openfoam/run.sh @@ -11,3 +11,7 @@ topoSet . ../../tools/openfoam-remove-empty-dirs.sh && openfoam_remove_empty_dirs close_log + +# Necessary signal file for the system tests, +# to keep the Source container running till the end +touch ../fluid-participant-finished.log diff --git a/volume-coupled-flow/fluid-openfoam/system/controlDict b/volume-coupled-flow/fluid-openfoam/system/controlDict index 65d2f9105..6c695213b 100644 --- a/volume-coupled-flow/fluid-openfoam/system/controlDict +++ b/volume-coupled-flow/fluid-openfoam/system/controlDict @@ -20,7 +20,7 @@ deltaT 0.005; writeControl runTime; -writeInterval 0.01; +writeInterval 0.1; purgeWrite 0; diff --git a/volume-coupled-flow/metadata.yaml b/volume-coupled-flow/metadata.yaml new file mode 100644 index 000000000..7382b5a22 --- /dev/null +++ b/volume-coupled-flow/metadata.yaml @@ -0,0 +1,20 @@ +name: Volume coupled flow +path: volume-coupled-flow # relative to git repo +url: https://precice.org/tutorials-volume-coupled-flow.html + +participants: + - Fluid + - Source + +cases: + fluid-openfoam: + participant: Fluid + directory: ./fluid-openfoam + run: ./run.sh + component: openfoam-adapter + + source-nutils: + participant: Source + directory: ./source-nutils + run: ./run.sh && inotifywait -e create,attrib -qq ../fluid-participant-finished.log + component: nutils-adapter diff --git a/volume-coupled-flow/precice-config.xml b/volume-coupled-flow/precice-config.xml index 5ce9631d5..e48a64360 100644 --- a/volume-coupled-flow/precice-config.xml +++ b/volume-coupled-flow/precice-config.xml @@ -21,7 +21,7 @@ - + - + - + - - + + diff --git a/volume-coupled-flow/reference-results/fluid-openfoam_source-nutils.tar.gz b/volume-coupled-flow/reference-results/fluid-openfoam_source-nutils.tar.gz new file mode 100644 index 000000000..08e5747f2 --- /dev/null +++ b/volume-coupled-flow/reference-results/fluid-openfoam_source-nutils.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8fbbd006cd747915ebb2143029630b445668d352707a137622e30bd5e11f9313 +size 82379 diff --git a/volume-coupled-flow/reference-results/reference_results.metadata b/volume-coupled-flow/reference-results/reference_results.metadata new file mode 100644 index 000000000..09128d30c --- /dev/null +++ b/volume-coupled-flow/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| fluid-openfoam_source-nutils.tar.gz | 2026-05-31 00:10:10 | 8fbbd006cd747915ebb2143029630b445668d352707a137622e30bd5e11f9313 | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | more-tests | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/volume-coupled-flow/source-nutils/requirements.txt b/volume-coupled-flow/source-nutils/requirements.txt new file mode 100644 index 000000000..7ff7b5aa1 --- /dev/null +++ b/volume-coupled-flow/source-nutils/requirements.txt @@ -0,0 +1,4 @@ +setuptools # required by nutils +nutils~=7.3 +numpy >1, <2 +pyprecice~=3.0 diff --git a/volume-coupled-flow/source-nutils/run.sh b/volume-coupled-flow/source-nutils/run.sh index 243603b18..6af3cce86 100755 --- a/volume-coupled-flow/source-nutils/run.sh +++ b/volume-coupled-flow/source-nutils/run.sh @@ -4,6 +4,13 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 +if [ ! -v PRECICE_TUTORIALS_NO_VENV ] +then + python3 -m venv .venv + . .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log +fi + python3 source.py close_log diff --git a/volume-coupled-flow/source-nutils/source.py b/volume-coupled-flow/source-nutils/source.py index 42baabbbf..3683a049e 100644 --- a/volume-coupled-flow/source-nutils/source.py +++ b/volume-coupled-flow/source-nutils/source.py @@ -30,7 +30,7 @@ def main(): ns.x = geom # preCICE setup - participant = precice.Participant("Source-Velocity", "../precice-config.xml", 0, 1) + participant = precice.Participant("Source", "../precice-config.xml", 0, 1) # define coupling mesh mesh_name = "Source-Mesh" diff --git a/water-hammer/metadata.yaml b/water-hammer/metadata.yaml new file mode 100644 index 000000000..0484cc7e1 --- /dev/null +++ b/water-hammer/metadata.yaml @@ -0,0 +1,34 @@ +name: Water hammer +path: water-hammer # relative to git repo +url: https://precice.org/tutorials-water-hammer.html + +participants: + - Fluid1DLeft + # - Fluid1DRight + # - Fluid3DLeft + - Fluid3DRight + +cases: + fluid1d-left-nutils: + participant: Fluid1DLeft + directory: ./fluid1d-left-nutils + run: ./run.sh + component: nutils-adapter + + # fluid1d-right-nutils: + # participant: Fluid1DRight + # directory: ./fluid1d-right-nutils + # run: ./run.sh + # component: nutils-adapter + + # fluid3d-left-openfoam: + # participant: Fluid3DLeft + # directory: ./fluid3d-left-openfoam + # run: ./run.sh + # component: openfoam-adapter + + fluid3d-right-openfoam: + participant: Fluid3DRight + directory: ./fluid3d-right-openfoam + run: ./run.sh + component: openfoam-adapter diff --git a/water-hammer/reference-results/fluid1d-left-nutils_fluid3d-right-openfoam.tar.gz b/water-hammer/reference-results/fluid1d-left-nutils_fluid3d-right-openfoam.tar.gz new file mode 100644 index 000000000..21eee0f3a --- /dev/null +++ b/water-hammer/reference-results/fluid1d-left-nutils_fluid3d-right-openfoam.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b3c96d4bcd10a6447e39b2918c94753cd9f1994cba89abf713b21869fc78c7dd +size 159892 diff --git a/water-hammer/reference-results/reference_results.metadata b/water-hammer/reference-results/reference_results.metadata new file mode 100644 index 000000000..8452887b4 --- /dev/null +++ b/water-hammer/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| fluid1d-left-nutils_fluid3d-right-openfoam.tar.gz | 2026-05-31 00:21:36 | b3c96d4bcd10a6447e39b2918c94753cd9f1994cba89abf713b21869fc78c7dd | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | more-tests | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 From d11e989e815dc93998664a3f8021c0b1c458fee9 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Sun, 31 May 2026 08:37:01 +0200 Subject: [PATCH 58/94] Do not try to create the venv if it already exists (#825) --- channel-transport-particles/fluid-nutils/run.sh | 4 +++- channel-transport-reaction/chemical-fenics/run.sh | 4 +++- channel-transport-reaction/fluid-fenics/run.sh | 4 +++- channel-transport/fluid-nutils/run.sh | 4 +++- channel-transport/transport-nutils/run.sh | 4 +++- elastic-tube-1d/fluid-python/run.sh | 4 +++- elastic-tube-1d/solid-python/run.sh | 4 +++- elastic-tube-3d/solid-fenics/run.sh | 4 +++- flow-around-controlled-moving-cylinder/controller-fmi/run.sh | 4 +++- flow-around-controlled-moving-cylinder/solid-python/run.sh | 4 +++- flow-over-heated-plate/fluid-su2/run.sh | 4 +++- flow-over-heated-plate/solid-dunefem/run.sh | 4 +++- flow-over-heated-plate/solid-fenics/run.sh | 4 +++- flow-over-heated-plate/solid-fenicsx/run.sh | 4 +++- flow-over-heated-plate/solid-nutils/run.sh | 4 +++- oscillator-overlap/mass-left-python/run.sh | 4 +++- oscillator-overlap/mass-right-python/run.sh | 4 +++- oscillator/mass-left-fmi/run.sh | 4 +++- oscillator/mass-left-python/run.sh | 4 +++- oscillator/mass-right-fmi/run.sh | 4 +++- oscillator/mass-right-python/run.sh | 4 +++- partitioned-heat-conduction-complex/dirichlet-fenics/run.sh | 4 +++- partitioned-heat-conduction-complex/neumann-fenics/run.sh | 4 +++- partitioned-heat-conduction-direct/dirichlet-nutils/run.sh | 4 +++- partitioned-heat-conduction-direct/neumann-nutils/run.sh | 4 +++- partitioned-heat-conduction-overlap/left-fenics/run.sh | 4 +++- partitioned-heat-conduction-overlap/right-fenics/run.sh | 4 +++- partitioned-heat-conduction/dirichlet-fenics/run.sh | 4 +++- partitioned-heat-conduction/dirichlet-fenicsx/run.sh | 4 +++- partitioned-heat-conduction/dirichlet-nutils/run.sh | 4 +++- partitioned-heat-conduction/neumann-fenics/run.sh | 4 +++- partitioned-heat-conduction/neumann-fenicsx/run.sh | 4 +++- partitioned-heat-conduction/neumann-nutils/run.sh | 4 +++- partitioned-pipe-multiscale/fluid1d-left-nutils/run.sh | 4 +++- partitioned-pipe-multiscale/fluid1d-right-nutils/run.sh | 4 +++- perpendicular-flap/fluid-fake/run.sh | 4 +++- perpendicular-flap/fluid-nutils/run.sh | 4 +++- perpendicular-flap/fluid-su2/run.sh | 4 +++- perpendicular-flap/solid-fake/run.sh | 4 +++- perpendicular-flap/solid-fenics/run.sh | 4 +++- perpendicular-flap/solid-nutils/run.sh | 4 +++- resonant-circuit/capacitor-python/run.sh | 4 +++- resonant-circuit/coil-python/run.sh | 4 +++- tools/visualize-configs.sh | 4 +++- turek-hron-fsi3/fluid-nutils/run.sh | 4 +++- turek-hron-fsi3/solid-nutils/run.sh | 4 +++- two-scale-heat-conduction/macro-nutils/run.sh | 4 +++- two-scale-heat-conduction/micro-nutils/run.sh | 4 +++- volume-coupled-diffusion/drain-fenics/run.sh | 4 +++- volume-coupled-diffusion/source-fenics/run.sh | 4 +++- volume-coupled-flow/source-nutils/run.sh | 4 +++- water-hammer/fluid1d-left-nutils/run.sh | 4 +++- water-hammer/fluid1d-right-nutils/run.sh | 4 +++- 53 files changed, 159 insertions(+), 53 deletions(-) diff --git a/channel-transport-particles/fluid-nutils/run.sh b/channel-transport-particles/fluid-nutils/run.sh index 641f4f465..7a46ac17e 100755 --- a/channel-transport-particles/fluid-nutils/run.sh +++ b/channel-transport-particles/fluid-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/channel-transport-reaction/chemical-fenics/run.sh b/channel-transport-reaction/chemical-fenics/run.sh index e0927a968..9409a4a4e 100755 --- a/channel-transport-reaction/chemical-fenics/run.sh +++ b/channel-transport-reaction/chemical-fenics/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv --system-site-package .venv + if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/channel-transport-reaction/fluid-fenics/run.sh b/channel-transport-reaction/fluid-fenics/run.sh index ff60931b0..b2b440be5 100755 --- a/channel-transport-reaction/fluid-fenics/run.sh +++ b/channel-transport-reaction/fluid-fenics/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv --system-site-package .venv + if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/channel-transport/fluid-nutils/run.sh b/channel-transport/fluid-nutils/run.sh index 641f4f465..7a46ac17e 100755 --- a/channel-transport/fluid-nutils/run.sh +++ b/channel-transport/fluid-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/channel-transport/transport-nutils/run.sh b/channel-transport/transport-nutils/run.sh index c36a5bf49..4f07bdeab 100755 --- a/channel-transport/transport-nutils/run.sh +++ b/channel-transport/transport-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/elastic-tube-1d/fluid-python/run.sh b/elastic-tube-1d/fluid-python/run.sh index 381bf0c1b..3c05487b6 100755 --- a/elastic-tube-1d/fluid-python/run.sh +++ b/elastic-tube-1d/fluid-python/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/elastic-tube-1d/solid-python/run.sh b/elastic-tube-1d/solid-python/run.sh index 60fba521e..06a0c2aaa 100755 --- a/elastic-tube-1d/solid-python/run.sh +++ b/elastic-tube-1d/solid-python/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/elastic-tube-3d/solid-fenics/run.sh b/elastic-tube-3d/solid-fenics/run.sh index a06dd46bb..5e22a5d84 100755 --- a/elastic-tube-3d/solid-fenics/run.sh +++ b/elastic-tube-3d/solid-fenics/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv --system-site-package .venv + if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/flow-around-controlled-moving-cylinder/controller-fmi/run.sh b/flow-around-controlled-moving-cylinder/controller-fmi/run.sh index 57be1215c..c5da7b900 100755 --- a/flow-around-controlled-moving-cylinder/controller-fmi/run.sh +++ b/flow-around-controlled-moving-cylinder/controller-fmi/run.sh @@ -17,7 +17,9 @@ fi if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/flow-around-controlled-moving-cylinder/solid-python/run.sh b/flow-around-controlled-moving-cylinder/solid-python/run.sh index 0b6c1f894..89027ba47 100755 --- a/flow-around-controlled-moving-cylinder/solid-python/run.sh +++ b/flow-around-controlled-moving-cylinder/solid-python/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/flow-over-heated-plate/fluid-su2/run.sh b/flow-over-heated-plate/fluid-su2/run.sh index db3ab8b76..20d439355 100755 --- a/flow-over-heated-plate/fluid-su2/run.sh +++ b/flow-over-heated-plate/fluid-su2/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv --system-site-package .venv + if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/flow-over-heated-plate/solid-dunefem/run.sh b/flow-over-heated-plate/solid-dunefem/run.sh index 3c0c73f5c..01049fa9e 100755 --- a/flow-over-heated-plate/solid-dunefem/run.sh +++ b/flow-over-heated-plate/solid-dunefem/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/flow-over-heated-plate/solid-fenics/run.sh b/flow-over-heated-plate/solid-fenics/run.sh index 3a0977996..649dca1aa 100755 --- a/flow-over-heated-plate/solid-fenics/run.sh +++ b/flow-over-heated-plate/solid-fenics/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv --system-site-package .venv + if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/flow-over-heated-plate/solid-fenicsx/run.sh b/flow-over-heated-plate/solid-fenicsx/run.sh index a95847662..01793a3e7 100755 --- a/flow-over-heated-plate/solid-fenicsx/run.sh +++ b/flow-over-heated-plate/solid-fenicsx/run.sh @@ -1,7 +1,9 @@ #!/bin/sh set -e -u -python3 -m venv --system-site-packages .venv +if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r requirements.txt diff --git a/flow-over-heated-plate/solid-nutils/run.sh b/flow-over-heated-plate/solid-nutils/run.sh index 3c0c73f5c..01049fa9e 100755 --- a/flow-over-heated-plate/solid-nutils/run.sh +++ b/flow-over-heated-plate/solid-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/oscillator-overlap/mass-left-python/run.sh b/oscillator-overlap/mass-left-python/run.sh index 5a9c18658..361b2d2a2 100755 --- a/oscillator-overlap/mass-left-python/run.sh +++ b/oscillator-overlap/mass-left-python/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r ../solver-python/requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/oscillator-overlap/mass-right-python/run.sh b/oscillator-overlap/mass-right-python/run.sh index be993c49f..82ab74754 100755 --- a/oscillator-overlap/mass-right-python/run.sh +++ b/oscillator-overlap/mass-right-python/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r ../solver-python/requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/oscillator/mass-left-fmi/run.sh b/oscillator/mass-left-fmi/run.sh index 2e6f83142..6a7699269 100755 --- a/oscillator/mass-left-fmi/run.sh +++ b/oscillator/mass-left-fmi/run.sh @@ -20,7 +20,9 @@ fi if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/oscillator/mass-left-python/run.sh b/oscillator/mass-left-python/run.sh index 5a9c18658..361b2d2a2 100755 --- a/oscillator/mass-left-python/run.sh +++ b/oscillator/mass-left-python/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r ../solver-python/requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/oscillator/mass-right-fmi/run.sh b/oscillator/mass-right-fmi/run.sh index 0d1d37574..94294b07b 100755 --- a/oscillator/mass-right-fmi/run.sh +++ b/oscillator/mass-right-fmi/run.sh @@ -20,7 +20,9 @@ fi if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/oscillator/mass-right-python/run.sh b/oscillator/mass-right-python/run.sh index be993c49f..82ab74754 100755 --- a/oscillator/mass-right-python/run.sh +++ b/oscillator/mass-right-python/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r ../solver-python/requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/partitioned-heat-conduction-complex/dirichlet-fenics/run.sh b/partitioned-heat-conduction-complex/dirichlet-fenics/run.sh index feccfc7b7..7f2251819 100755 --- a/partitioned-heat-conduction-complex/dirichlet-fenics/run.sh +++ b/partitioned-heat-conduction-complex/dirichlet-fenics/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv --system-site-packages .venv + if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r ../solver-fenics/requirements.txt fi diff --git a/partitioned-heat-conduction-complex/neumann-fenics/run.sh b/partitioned-heat-conduction-complex/neumann-fenics/run.sh index e505b7711..4c82b9962 100755 --- a/partitioned-heat-conduction-complex/neumann-fenics/run.sh +++ b/partitioned-heat-conduction-complex/neumann-fenics/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv --system-site-packages .venv + if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r ../solver-fenics/requirements.txt fi diff --git a/partitioned-heat-conduction-direct/dirichlet-nutils/run.sh b/partitioned-heat-conduction-direct/dirichlet-nutils/run.sh index 2d3ff34db..01f0bd50a 100755 --- a/partitioned-heat-conduction-direct/dirichlet-nutils/run.sh +++ b/partitioned-heat-conduction-direct/dirichlet-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/partitioned-heat-conduction-direct/neumann-nutils/run.sh b/partitioned-heat-conduction-direct/neumann-nutils/run.sh index 54734b5f9..8fab51f89 100755 --- a/partitioned-heat-conduction-direct/neumann-nutils/run.sh +++ b/partitioned-heat-conduction-direct/neumann-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/partitioned-heat-conduction-overlap/left-fenics/run.sh b/partitioned-heat-conduction-overlap/left-fenics/run.sh index b19c1f5d9..af99ead39 100755 --- a/partitioned-heat-conduction-overlap/left-fenics/run.sh +++ b/partitioned-heat-conduction-overlap/left-fenics/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv --system-site-packages .venv + if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r ../solver-fenics/requirements.txt fi diff --git a/partitioned-heat-conduction-overlap/right-fenics/run.sh b/partitioned-heat-conduction-overlap/right-fenics/run.sh index b8f73ac22..dfbabbdfe 100755 --- a/partitioned-heat-conduction-overlap/right-fenics/run.sh +++ b/partitioned-heat-conduction-overlap/right-fenics/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv --system-site-packages .venv + if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r ../solver-fenics/requirements.txt fi diff --git a/partitioned-heat-conduction/dirichlet-fenics/run.sh b/partitioned-heat-conduction/dirichlet-fenics/run.sh index 880f966a2..288101292 100755 --- a/partitioned-heat-conduction/dirichlet-fenics/run.sh +++ b/partitioned-heat-conduction/dirichlet-fenics/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv --system-site-packages .venv + if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r ../solver-fenics/requirements.txt fi diff --git a/partitioned-heat-conduction/dirichlet-fenicsx/run.sh b/partitioned-heat-conduction/dirichlet-fenicsx/run.sh index cc7202ffb..81dc8e08c 100755 --- a/partitioned-heat-conduction/dirichlet-fenicsx/run.sh +++ b/partitioned-heat-conduction/dirichlet-fenicsx/run.sh @@ -1,7 +1,9 @@ #!/bin/sh set -e -u -python3 -m venv --system-site-packages .venv +if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r ../solver-fenicsx/requirements.txt diff --git a/partitioned-heat-conduction/dirichlet-nutils/run.sh b/partitioned-heat-conduction/dirichlet-nutils/run.sh index 2d3ff34db..01f0bd50a 100755 --- a/partitioned-heat-conduction/dirichlet-nutils/run.sh +++ b/partitioned-heat-conduction/dirichlet-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/partitioned-heat-conduction/neumann-fenics/run.sh b/partitioned-heat-conduction/neumann-fenics/run.sh index cc7a42c41..d71a3122f 100755 --- a/partitioned-heat-conduction/neumann-fenics/run.sh +++ b/partitioned-heat-conduction/neumann-fenics/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv --system-site-packages .venv + if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r ../solver-fenics/requirements.txt fi diff --git a/partitioned-heat-conduction/neumann-fenicsx/run.sh b/partitioned-heat-conduction/neumann-fenicsx/run.sh index b676e0c94..c352157be 100755 --- a/partitioned-heat-conduction/neumann-fenicsx/run.sh +++ b/partitioned-heat-conduction/neumann-fenicsx/run.sh @@ -1,7 +1,9 @@ #!/bin/sh set -e -u -python3 -m venv --system-site-packages .venv +if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r ../solver-fenicsx/requirements.txt python3 ../solver-fenicsx/heat.py Neumann diff --git a/partitioned-heat-conduction/neumann-nutils/run.sh b/partitioned-heat-conduction/neumann-nutils/run.sh index 54734b5f9..8fab51f89 100755 --- a/partitioned-heat-conduction/neumann-nutils/run.sh +++ b/partitioned-heat-conduction/neumann-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/partitioned-pipe-multiscale/fluid1d-left-nutils/run.sh b/partitioned-pipe-multiscale/fluid1d-left-nutils/run.sh index fb474e9a8..74f5dff15 100755 --- a/partitioned-pipe-multiscale/fluid1d-left-nutils/run.sh +++ b/partitioned-pipe-multiscale/fluid1d-left-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/partitioned-pipe-multiscale/fluid1d-right-nutils/run.sh b/partitioned-pipe-multiscale/fluid1d-right-nutils/run.sh index 0593f1eeb..da8cfab83 100755 --- a/partitioned-pipe-multiscale/fluid1d-right-nutils/run.sh +++ b/partitioned-pipe-multiscale/fluid1d-right-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/perpendicular-flap/fluid-fake/run.sh b/perpendicular-flap/fluid-fake/run.sh index 57ae6f24f..e33d1259b 100755 --- a/perpendicular-flap/fluid-fake/run.sh +++ b/perpendicular-flap/fluid-fake/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/perpendicular-flap/fluid-nutils/run.sh b/perpendicular-flap/fluid-nutils/run.sh index f68b56661..aedb2e255 100755 --- a/perpendicular-flap/fluid-nutils/run.sh +++ b/perpendicular-flap/fluid-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/perpendicular-flap/fluid-su2/run.sh b/perpendicular-flap/fluid-su2/run.sh index e6c7a0fcb..590a46e0f 100755 --- a/perpendicular-flap/fluid-su2/run.sh +++ b/perpendicular-flap/fluid-su2/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv --system-site-packages .venv + if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/perpendicular-flap/solid-fake/run.sh b/perpendicular-flap/solid-fake/run.sh index 3bd2c8542..e93deb140 100755 --- a/perpendicular-flap/solid-fake/run.sh +++ b/perpendicular-flap/solid-fake/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/perpendicular-flap/solid-fenics/run.sh b/perpendicular-flap/solid-fenics/run.sh index 310a8fb12..649dca1aa 100755 --- a/perpendicular-flap/solid-fenics/run.sh +++ b/perpendicular-flap/solid-fenics/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv --system-site-packages .venv + if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/perpendicular-flap/solid-nutils/run.sh b/perpendicular-flap/solid-nutils/run.sh index 1b0420d63..4a74159a6 100755 --- a/perpendicular-flap/solid-nutils/run.sh +++ b/perpendicular-flap/solid-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/resonant-circuit/capacitor-python/run.sh b/resonant-circuit/capacitor-python/run.sh index 042124adc..ee4ecc84a 100755 --- a/resonant-circuit/capacitor-python/run.sh +++ b/resonant-circuit/capacitor-python/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/resonant-circuit/coil-python/run.sh b/resonant-circuit/coil-python/run.sh index 248944c23..e816881c2 100755 --- a/resonant-circuit/coil-python/run.sh +++ b/resonant-circuit/coil-python/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/tools/visualize-configs.sh b/tools/visualize-configs.sh index bf430eb66..a02b72895 100755 --- a/tools/visualize-configs.sh +++ b/tools/visualize-configs.sh @@ -30,7 +30,9 @@ visualize_config(){ export -f visualize_config -python3 -m venv .venv +if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install precice-config-visualizer diff --git a/turek-hron-fsi3/fluid-nutils/run.sh b/turek-hron-fsi3/fluid-nutils/run.sh index d0c4c71f9..1c473f01f 100755 --- a/turek-hron-fsi3/fluid-nutils/run.sh +++ b/turek-hron-fsi3/fluid-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/turek-hron-fsi3/solid-nutils/run.sh b/turek-hron-fsi3/solid-nutils/run.sh index 2942ab3ed..c83b503d5 100755 --- a/turek-hron-fsi3/solid-nutils/run.sh +++ b/turek-hron-fsi3/solid-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/two-scale-heat-conduction/macro-nutils/run.sh b/two-scale-heat-conduction/macro-nutils/run.sh index 3cf21c216..f6bd9f6b8 100755 --- a/two-scale-heat-conduction/macro-nutils/run.sh +++ b/two-scale-heat-conduction/macro-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/two-scale-heat-conduction/micro-nutils/run.sh b/two-scale-heat-conduction/micro-nutils/run.sh index 4e57595db..b0e7f8250 100755 --- a/two-scale-heat-conduction/micro-nutils/run.sh +++ b/two-scale-heat-conduction/micro-nutils/run.sh @@ -8,7 +8,9 @@ usage() { echo "Usage: cmd [-s] [-p n]" 1>&2; exit 1; } if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/volume-coupled-diffusion/drain-fenics/run.sh b/volume-coupled-diffusion/drain-fenics/run.sh index c519019d6..705626ca6 100755 --- a/volume-coupled-diffusion/drain-fenics/run.sh +++ b/volume-coupled-diffusion/drain-fenics/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv --system-site-packages .venv + if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r ../solver-fenics/requirements.txt fi diff --git a/volume-coupled-diffusion/source-fenics/run.sh b/volume-coupled-diffusion/source-fenics/run.sh index 76b168162..61fc2fd90 100755 --- a/volume-coupled-diffusion/source-fenics/run.sh +++ b/volume-coupled-diffusion/source-fenics/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv --system-site-packages .venv + if [ ! -d .venv ]; then + python3 -m venv --system-site-packages .venv + fi . .venv/bin/activate pip install -r ../solver-fenics/requirements.txt fi diff --git a/volume-coupled-flow/source-nutils/run.sh b/volume-coupled-flow/source-nutils/run.sh index 6af3cce86..95c4fd803 100755 --- a/volume-coupled-flow/source-nutils/run.sh +++ b/volume-coupled-flow/source-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/water-hammer/fluid1d-left-nutils/run.sh b/water-hammer/fluid1d-left-nutils/run.sh index fb474e9a8..74f5dff15 100755 --- a/water-hammer/fluid1d-left-nutils/run.sh +++ b/water-hammer/fluid1d-left-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi diff --git a/water-hammer/fluid1d-right-nutils/run.sh b/water-hammer/fluid1d-right-nutils/run.sh index 0593f1eeb..da8cfab83 100755 --- a/water-hammer/fluid1d-right-nutils/run.sh +++ b/water-hammer/fluid1d-right-nutils/run.sh @@ -6,7 +6,9 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - python3 -m venv .venv + if [ ! -d .venv ]; then + python3 -m venv .venv + fi . .venv/bin/activate pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi From f5d3c80c4d243910e8be022e5a0c3843493d8697 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Mon, 1 Jun 2026 08:51:16 +0200 Subject: [PATCH 59/94] System tests workflows: Fix defaults --- .github/workflows/run_testsuite_manual.yml | 2 +- .github/workflows/run_testsuite_workflow.yml | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/run_testsuite_manual.yml b/.github/workflows/run_testsuite_manual.yml index 0a9958000..2eb4521ca 100644 --- a/.github/workflows/run_testsuite_manual.yml +++ b/.github/workflows/run_testsuite_manual.yml @@ -8,7 +8,7 @@ on: default: 'release_test' type: string build_args: - description: 'Build arguments (override reference_versions.yaml)' + description: 'Build arguments (override component defaults)' default: 'PRECICE_REF:v3.4.1,TUTORIALS_REF:develop' type: string system_tests_branch: diff --git a/.github/workflows/run_testsuite_workflow.yml b/.github/workflows/run_testsuite_workflow.yml index fde53977f..f93b32e8b 100644 --- a/.github/workflows/run_testsuite_workflow.yml +++ b/.github/workflows/run_testsuite_workflow.yml @@ -4,20 +4,18 @@ on: inputs: suites: description: 'Test suites to execute (comma-separated)' - required: true + default: 'release_test' type: string build_args: description: 'Build arguments' - required: false type: string system_tests_branch: description: 'Branch to take the system tests from' - required: true + default: 'develop' type: string log_level: description: 'Logging verbosity level used for the systemtests' default: 'INFO' - required: false type: string upload_artifacts: description: 'TRUE or FALSE (FALSE -> Upload only when the workflow fails)' From 0409fdbfe6311afa3d251732bdc2bb9b6df80a34 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Mon, 1 Jun 2026 11:09:33 +0200 Subject: [PATCH 60/94] Move uni-directional tests handling to run.sh --- volume-coupled-flow/metadata.yaml | 2 +- volume-coupled-flow/source-nutils/run.sh | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/volume-coupled-flow/metadata.yaml b/volume-coupled-flow/metadata.yaml index 7382b5a22..959340385 100644 --- a/volume-coupled-flow/metadata.yaml +++ b/volume-coupled-flow/metadata.yaml @@ -16,5 +16,5 @@ cases: source-nutils: participant: Source directory: ./source-nutils - run: ./run.sh && inotifywait -e create,attrib -qq ../fluid-participant-finished.log + run: ./run.sh component: nutils-adapter diff --git a/volume-coupled-flow/source-nutils/run.sh b/volume-coupled-flow/source-nutils/run.sh index 95c4fd803..084a325c7 100755 --- a/volume-coupled-flow/source-nutils/run.sh +++ b/volume-coupled-flow/source-nutils/run.sh @@ -15,4 +15,8 @@ fi python3 source.py +# System tests: Keep the container and the respective network alive till the end. +echo "Waiting for the Fluid participant to finish..." +inotifywait -e create,attrib -qq ../fluid-participant-finished.log + close_log From 4ee1b1311d1c1c46b6c3c10419d89fdf0b6720c4 Mon Sep 17 00:00:00 2001 From: joargu Date: Mon, 1 Jun 2026 12:00:42 +0200 Subject: [PATCH 61/94] Add wolf-sheep-soil-creep tutorial (#793) --- changelog-entries/793.md | 1 + tools/tests/tests.yaml | 11 ++ wolf-sheep-soil-creep/README.md | 58 +++++++++ wolf-sheep-soil-creep/clean-tutorial.sh | 1 + ...soil-creep-erosion_deposition_patterns.png | Bin 0 -> 68346 bytes ...orials-wolf-sheep-soil-creep-grass_map.png | Bin 0 -> 62934 bytes ...s-wolf-sheep-soil-creep-precice-config.png | Bin 0 -> 101189 bytes ...s-wolf-sheep-soil-creep-soil_thickness.png | Bin 0 -> 61912 bytes wolf-sheep-soil-creep/metadata.yaml | 20 +++ wolf-sheep-soil-creep/precice-config.xml | 69 ++++++++++ .../soil-creep-landlab/.gitignore | 1 + .../soil-creep-landlab/clean.sh | 9 ++ .../soil-creep-landlab/requirements.txt | 4 + .../soil-creep-landlab/run.sh | 23 ++++ .../soil-creep-landlab/soil_creep.py | 122 ++++++++++++++++++ .../wolf-sheep-grass-mesa/clean.sh | 7 + .../wolf-sheep-grass-mesa/requirements.txt | 5 + .../wolf-sheep-grass-mesa/run.sh | 22 ++++ .../wolf-sheep-grass-mesa/wolf_sheep_grass.py | 97 ++++++++++++++ 19 files changed, 450 insertions(+) create mode 100644 changelog-entries/793.md create mode 100644 wolf-sheep-soil-creep/README.md create mode 120000 wolf-sheep-soil-creep/clean-tutorial.sh create mode 100644 wolf-sheep-soil-creep/images/tutorials-wolf-sheep-soil-creep-erosion_deposition_patterns.png create mode 100644 wolf-sheep-soil-creep/images/tutorials-wolf-sheep-soil-creep-grass_map.png create mode 100644 wolf-sheep-soil-creep/images/tutorials-wolf-sheep-soil-creep-precice-config.png create mode 100644 wolf-sheep-soil-creep/images/tutorials-wolf-sheep-soil-creep-soil_thickness.png create mode 100644 wolf-sheep-soil-creep/metadata.yaml create mode 100644 wolf-sheep-soil-creep/precice-config.xml create mode 100644 wolf-sheep-soil-creep/soil-creep-landlab/.gitignore create mode 100755 wolf-sheep-soil-creep/soil-creep-landlab/clean.sh create mode 100644 wolf-sheep-soil-creep/soil-creep-landlab/requirements.txt create mode 100755 wolf-sheep-soil-creep/soil-creep-landlab/run.sh create mode 100644 wolf-sheep-soil-creep/soil-creep-landlab/soil_creep.py create mode 100755 wolf-sheep-soil-creep/wolf-sheep-grass-mesa/clean.sh create mode 100644 wolf-sheep-soil-creep/wolf-sheep-grass-mesa/requirements.txt create mode 100755 wolf-sheep-soil-creep/wolf-sheep-grass-mesa/run.sh create mode 100644 wolf-sheep-soil-creep/wolf-sheep-grass-mesa/wolf_sheep_grass.py diff --git a/changelog-entries/793.md b/changelog-entries/793.md new file mode 100644 index 000000000..7dc2bfa6d --- /dev/null +++ b/changelog-entries/793.md @@ -0,0 +1 @@ +- Added the `wolf-sheep-soil-creep` tutorial [#793](https://github.com/precice/tutorials/pull/793) \ No newline at end of file diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index ba58b775d..1a497d904 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -487,6 +487,16 @@ test_suites: reference_result: ./water-hammer/reference-results/fluid1d-left-nutils_fluid3d-right-openfoam.tar.gz # More case combinations are possible, but they requite calling set-case.sh + wolf-sheep-soil-creep: + tutorials: + - &wolf-sheep-soil-creep_soil-creep-landlab_wolf-sheep-grass-mesa + path: wolf-sheep-soil-creep + case_combination: + - soil-creep-landlab + - wolf-sheep-grass-mesa + max_time: 20 + reference_result: ./wolf-sheep-soil-creep/reference-results/soil-creep-landlab_wolf-sheep-grass-mesa.tar.gz + ##################################################################### ## Test suites referring to the test suites defined above @@ -539,6 +549,7 @@ test_suites: - *volume-coupled-diffusion_source-fenics_drain-fenics - *volume-coupled-flow_fluid-openfoam_source-nutils - *water-hammer_fluid1d-left-nutils_fluid3d-right-openfoam + - *wolf-sheep-soil-creep_soil-creep-landlab_wolf-sheep-grass-mesa # These test suites take longer to run. They are available, but not regularly executed. extra: diff --git a/wolf-sheep-soil-creep/README.md b/wolf-sheep-soil-creep/README.md new file mode 100644 index 000000000..b084c7285 --- /dev/null +++ b/wolf-sheep-soil-creep/README.md @@ -0,0 +1,58 @@ +--- +title: Wolf-sheep-grass model with soil creep +permalink: tutorials-wolf-sheep-soil-creep.html +keywords: MESA, Landlab, wolf-sheep-grass, soil creep, ABM, agent-based modeling +summary: Example of bi-directional ABM-PDE coupling via preCICE. +--- + +{% note %} +Get the [case files of this tutorial](https://github.com/precice/tutorials/tree/develop/wolf-sheep-soil-creep), as continuously rendered here, or see the [latest released version](https://github.com/precice/tutorials/tree/master/wolf-sheep-soil-creep) (if there is already one). Read how in the [tutorials introduction](https://precice.org/tutorials.html). +{% endnote %} + +## Setup + +This tutorial is based on Landlab's [Wolf-Sheep-Grass Model with Soil Creep](https://landlab.csdms.io/tutorials/agent_based_modeling/wolf_sheep/wolf_sheep_with_soil_creep.html) example, which couples the [Wolf-Sheep model from Mesa](https://mesa.readthedocs.io/latest/examples/advanced/wolf_sheep.html) with a Landlab soil creep model in a single notebook-based workflow. The [source notebook](https://github.com/landlab/landlab/blob/master/docs/source/tutorials/agent_based_modeling/wolf_sheep/wolf_sheep_with_soil_creep.ipynb) is licensed under [MIT](https://landlab.csdms.io/about/license.html). + +The purpose of this tutorial is to demonstrate a simple two-way coupling between an agent-based model (ABM) and a grid-based continuum model via preCICE. The example extends the canonical wolf-sheep-grass ABM with a simple soil-creep feedback on a shared sloping raster grid. When sheep eat grass, the soil beneath the damaged grass becomes more mobile, increasing the local soil transport efficiency and affecting downslope soil transport. The feedback also acts in the other direction: soil thickness affects grass growth by preventing grass from growing where the soil becomes too thin. + +In this implementation, the Mesa wolf-sheep-grass model and the Landlab soil creep model are two separate preCICE participants, exchanging grass cover and soil depth during the simulation. The Mesa participant extracts the current grass map from the wolf-sheep-grass model and sends it to the Landlab participant. The Landlab participant uses this grass map to adapt the soil creep coefficient, evolves the soil thickness, and sends the updated soil depth back to the Mesa participant, where it is used to limit grass growth. This example is intentionally simple and should be understood as a demonstration case for coupling ABM and PDE-style models via preCICE, rather than as a physically calibrated landscape evolution model. + +## Configuration + +preCICE configuration (image generated using the [precice-config-visualizer](https://precice.org/tooling-config-visualization.html)): + +![preCICE configuration visualization](images/tutorials-wolf-sheep-soil-creep-precice-config.png) + +## Available solvers + +Soil-Creep PDE participant: + +* Landlab. Numerical modeling of Earth surface dynamics. For more information, have a look at the [Landlab documentation](https://landlab.csdms.io/index.html). + +Wolf-Sheep-Grass ABM participant: + +* MESA. Agent-based modeling (or ABM) framework. For more information have a look at the [MESA documentation](https://mesa.readthedocs.io/latest/index.html) + +## Running the simulation + +Open two separate terminals and start the soil creep and wolf sheep participants by calling the respective run scripts `run.sh` located in each of the participants' directories. For example: + +```bash +cd soil-creep-landlab +./run.sh +``` + +and + +```bash +cd wolf-sheep-grass-mesa +./run.sh +``` + +## Post-processing + +The soil-creep participant generates Python-based visualizations, which are written to `soil-creep-landlab/output`. The following examples were generated by setting `rng=42` in the `WolfSheepScenario` for the wolf-sheep-grass participant. + +![erosion/deposition patterns](images/tutorials-wolf-sheep-soil-creep-erosion_deposition_patterns.png) +![soil thickness](images/tutorials-wolf-sheep-soil-creep-soil_thickness.png) +![grass map](images/tutorials-wolf-sheep-soil-creep-grass_map.png) diff --git a/wolf-sheep-soil-creep/clean-tutorial.sh b/wolf-sheep-soil-creep/clean-tutorial.sh new file mode 120000 index 000000000..4713f5092 --- /dev/null +++ b/wolf-sheep-soil-creep/clean-tutorial.sh @@ -0,0 +1 @@ +../tools/clean-tutorial-base.sh \ No newline at end of file diff --git a/wolf-sheep-soil-creep/images/tutorials-wolf-sheep-soil-creep-erosion_deposition_patterns.png b/wolf-sheep-soil-creep/images/tutorials-wolf-sheep-soil-creep-erosion_deposition_patterns.png new file mode 100644 index 0000000000000000000000000000000000000000..718cabee7d9ebebf3609d7eb24c79ecb5b4c9b8a GIT binary patch literal 68346 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 literal 0 HcmV?d00001 diff --git a/wolf-sheep-soil-creep/images/tutorials-wolf-sheep-soil-creep-grass_map.png b/wolf-sheep-soil-creep/images/tutorials-wolf-sheep-soil-creep-grass_map.png new file mode 100644 index 0000000000000000000000000000000000000000..4fae85a0c2ffc11deb600b1ca18426fc3652ed1e GIT binary patch literal 62934 zcmdqJcTm*X)-{Ye>Wqkypdg^*pnwEXf@DNNp+TU@S->UW~@P7g}5^6Sz7KS!+!}(Jw}U9H-wswDSU$cy^^m|;H5n%C)T?71dfD30 zus6s$z`1vMVpfDo8UF2p2O%T-aEYju{pZ$yAIT^Eh-~0Gft>VTVuya`*!mIwkKc(6 z?6w09GamY=egd23a{QXj9Ju#fTVQPMWv`-$}S)t}EiJ#{Rn$8iix|Grhn3HP$!kwbP!%1cwJxUwCG4bv^3Li;KiQ(Od(2=%3SVYg}_+i zabj{`QcjP?yA@nkvx*muj{TW@+cA}t{-`%QpF^J?t;5{ATw|xd*lkz4l2gSwe~W*r zV74zlA(^$KXud_WTv1M#3jWPsDx;h`cLySJS?^5Tf=Wf((AgW!Ti>SGNA%Ii>r36+ICb)u_k5IG`YW-g#UR+ zFwwX()UGFJbtx`%Yca?I z0qYOPq^i@xoWFnmxWbx{c+9*b({d3#J2e-s;Rd6Nl3MS*_Y;xULp77C?1t6BR8z&9 zuIjxZQm@aI;#m{}MxCdY{G|T;Vi{kYzQFwoMf(?9`@}kwrv5n?m_i~ISzfX3`IARp;^5Cs?4L^mW+hqP& z^3tD-8}gYGG{s92iK!nC%wrW472!*dI{Pg928p<=T=MlQy&n;P9rG+^pK_RYW@~?|!);h&MYr>A>XE1$zCMr?GMF=m8)N0x=2P6f zJv}}5 z_olFAQRdn(*G@%Yzogd)j9n)N2{~AeXExsAa$cJLn4PTszAVaDgm!&(F{L$}-Nqz; zC$e~CLU`H7+P|04Sx{!%jx;bO+&6jV9;X<4OJeN!`CD4?tf9dIc9R8F{rWRXTh#}j z-xL<5rD20#vXu!J5q{8Vo?}1guX>;}K-YEIemLT({+|?lW-qy_bfqqvHpK~q8*CVi zz883!5@)lOxSze@GLRfkD`%MbNX|(qJ3s&B@!>MV5c8JXi^kPFtuLtAH7J#nqAwat zxvY%D&U`)}PTA6!_d5s0wXMn~mJvu>JC)U{GXv>`axP;A^ui zr>8U5TAeC(t6!1j>!yTA*B-Yt2`}E5Pw^6>nZL2~@Z3fcj8BE%9JyZ}#U)bi<;Dk4EWAdsN%ro#^N?Wg^Wyah3;2D(^v5>iC~xYMW=%0pi<9by z-xLt!hdnPOKZ6X!r5O(Kg-Sqj@y1FUE>PQZ!DZ>|)BaCqeP2MDrpzX|6wfDF42p12 z-`e`#b(DBDPJGNH1e(_EPLtTs0CwB?rGDRXcxpxm3>B+gx2>vKS}8S`G9f!evr&yjp@7nsZzq-Eoh>V95Xkok!4B+N!BsD;Csadbh1wH-P2mos0|a& zxNZ1amiMzsCL8-U=;A)ZqIEPCe{MT?h?wfSO2Qe(Jgxmr;&NL>$NkjLTgZdV>h#vp zAp7(%Y%5j8$PGhZG3RrDD`!vtU?h{i{WCgcG)U!Pu?{Ub`d^&jr8L1gtA>W@cDIA zQ`X(qL=E^5@dg~4nc$o(q z4iHG2PdB~~6&aL8^&4gRym#%{cgJ@uDMyFe{va*4aZpX@?&pLNhf5E}+w39rd1lkQ z%+=Q)ftsHvx79x4t`0Z2?>o$J3Uab@rg87|X15!LNYWoCeLHR* z@xXme1WX_^4>Yir-L|bf(*9d-gFhaDxx6^=T8?_&Pf8I_@H~ZH@eZ%_S2mHyv3D9v zCg*(%j+{q*7670g*s8!dPBQ}3#d5X90h&{lKdmeNaJt)iVsKd>pxCaoKn9#F3{Aj>c=>1B+iw0U%KK zz+{~dcfJ&2I^KZcG=Dh*AvW#rsr`U;OjChppuUV@H<|oCRyrp#?gFRDyz}Q|gO${# znuv<0?7uvu_@2?KaX0D8_fKbOXmhI=>!L>k9v>2jGjeSV@p3XY31KdD#2sT z%CRp@Shxv>bRmRy?ia>kl7AVk zq47)6aLz!{2+r*#4DuCOVIB*_Hgc7pI!-Xl8w~K8a@$o+OP9jucR%P%GG6!Q>oLwuBmJS6`R^RnHzBNB zN{V6t`UwO-Dsow+PKsqIi;)d!*$LF;Af3dM4ghaGY3p)K8qXbq4>UetI>vw5`dig7 zEKJmk{pkH@mHwVYO(c^G7dj&6Eu8!5(mo!za9wd4a4%b`c`22u8UAk%Py+AJUn4kW z=BMbz#96eYU%w)KA&C-&apnx^IX|qybCy)|^`6QwtY#g3l@8tKbwx`EdT9S3I=p$Q zNq-mTT}KGI#l8?a<$Rts_*2(&IiZ=7-O2*1lkY8}yh+bUW_s4fWFI;~CwR@&^a|6{ zFZl?4|AZ5nB6aY5&6R!ea!(GPJeE?ppb;8NCHTdC4`8+URC9#mEnLPlF~axIhZ$S3 zR+v4!)SRBSzpm@W$;;=yRRv{ySeWTAr%8%+k%Wvo#xGzqd_?>G1MR+W2Y5AQ(iF69 zX%(yq*RMfb4fPew)_9?A>=5MVHUnSbNX?vkS(B^vHyBH&To>Qxu6?gGSQu~jm>~LA zqNs;-xP?6BHJq)xKV$=bJi%kyq`ycf_`obNyTdfAh}VAlOYX+XSm5xG%S={22E9`1vxR73g7)9ze3Pd$k(NQo`G5mT?7AUOEE@bkruPX(+>seJ0i&+$$Yg zM|jAdD2H)zfGjXN(KYne`c^g@5keLo9TEcW4@|J>M8B`7&q!meQK2TbIG95_|BhmU zIgkKpT~?LNolB(ScUn%IhE@XlATRN)B4j*@4)oT)L9s35B}T+dXjKphn174Lww9gj z<>LT{x1_A&?x^(t449#ZWVWR`N`-46C&bGD-ii7LbnydcU#%3nMS{9w>%_RYPXX!j z3P+W_h4;ms|4)EpZc{Y?$Nb+M^W~HO4R9nqpQS$)m4d=KS?zdMR(&0V!@EbH{}XWj z0aV#%ulz#Vh55K&p{o;`yssPt4TR)>fQ63*cD%!5E+CX1=l$cuNNCOdtG^)5%=+er&TNCbC2$JC-^b} z)z8V|%?(73^Z^U_98x5c2(QOQDD&pyKvASE_BcWRDjZz({_J)q7|*CB!))d7^qo zq!MxKS&WYfYe%v{mvn>+_`WM+i5Y!9f|Ja@pQoDL%Qn%Gnzdgtta%$WZ+8+JWfl6@F~2&k&$niLiHWgEu8+D=j(7>gkMu*jtclmv z$TU{KT6k=SznXry+lIqiNdA`XxWLl4{MMeV%CK_!v(L;pW|r5c%`4$0loIx|^OxVGidOx~yv>bvWitfK~G$=PbA==}k)ozc6gM9E0C( zG2fLa2h{7*SlwEWGiBc%+WLF|>ywb_gw4#ckF4{X1GY6-32eIn7QHmn`X zTETQ`tNSum`f+%FyeZ+_AVqUrL6QIeJiOcahWqI2^KyucEQ9iR0ysi%*bW3~(555g z*!|wlH)a^VwRi8@H$F@F&bZbVh}NZ*)?$Vm3=VfrSyyq@D{Xhqr+-DQmOX@9hWuIK zLv*}sF>>KLxqL1wbI)I$y}`RwDE&tTG++#>T9y(*vsIV9y6mF7c|D<`i4@I-PF=4I z@TS>?xI6V{e$ixu&@9z5+`SC^5hCLPBdme*!$`{96j0iiE)uW-I)dgRohm% z3mDI|GTOV>4RV25PIqUXpXLWl2>|yugrf^KH&(M2)}+qJIQpXMPNMe++boz^#{6hP zO4-XZ*CHup-(KNDA-uU<>*krAW7^D`U@Ya&=kM>&1>_&1RP|dD)jeDq90hpyGMW@W zSpeWm8OCT+ca^+*3LxMMkZ~f8WGl%91|;8whM;{RlU-IYpeGi=Bx!Y=!C_KRl1a

Yue5n)F!iH~{47w{Tn4RH^oR3;$IB|TKro1(o_*iD&H-?Q6j$zs?8-i7 zxnG~C|G6Lv-%Z{B=I9`F6KjO}GQ49szz%ka&YCOzFP8clH&1lu+iKq4tO*q~YK+!) zT^^L^WOQBn%J$CW=2^)XpBNO?{2nC?252U?tYdTiVq`I^gh8N7Em3pf(Ygo%Sng7v zSBBU*c0`mrFSHveRBWGaZq3L05Btc8u5*uhSOK+HZdbh8-G<1((=lA#aF8X&E~FG# zy@gETB|I7Z`j#hjwQjG5;w>0m<`ljzM>IV@N#Ah;* z0#~rmareiEF>%Ny;|fr>!v_3R_$ywi{BY-t8 zoOXt=*S#~075*m6+x{EGY}yBrB+ZB6SEM!q1St`Eh1a13)+Wi!?nRV-?I;*I|`0K?sQ$2;oE)YlX;7fe9P3fq3CG3&@I2e7+xL~R6wZa* zG=uUodb=(KC8qDt?Y+Nhu?*W7Dy*WcTsr;rPXi?CjnC#3uP03xxNUCK19##P2I*|w zB1(Cp$$z^zNXvkLr{y$}RS)UK%%!Km!4jI^7TViQ2hT%4T)YGapS%;Kki_Bk3?uGn z*jrJ72jgl#x4Et~hEo@A0J&cVJ(AYXUmrkq@d&%1y%=Iqq^>pvC>V*1fn-9D8m;C?b~K0cu(op5 zM@FC!IN~pDCvWd>P)QPIxlmVOGuFZ${5bSM2TKp|-?`)ImFc4|4~wo{bP5abP`{g$ zMe35E!)B(ftaaO%Wiwm;UdfqZQWj?4_uNdX!O@ng>fP1H`We-{n!^szRfT<=RaQ}F zLgY9zZmS%)9}6rB=K$(?R`*8mp@ifbU`eh(YZ00E2BLmJO}M$OFW#V`P)bkMU=&&# zP{7@upY^$-|4>B4M0hVJix6g&O?q@4>#_zk1|7D1Z#$F@eMmIMu=lE$T|X0Z_`>6241er_}E6CWRg}MbN?HJtht^`t?=Rt?D;J`@UFfKm_1Z0nnvDm zvn%Ielo?P^cxDq!{=}x>mt)7In}0Rohdhz-We`5GHfAbV(C*$Tpy1lq&8s{EM;8;5 zr!)_EH#Fe4i>5I*%MnE873`Vihmx4_C0Zk1MXJz@dI*y6J?FXl8(l?6>L*Y+kbvk>wB>A>K|1$WichUCe(lLHm^cf!f+)@uqi- z_)hwX28vN)45K(R({2<_m1H+ucZRkB3Rb6abG&kP^y$G$&SI_MBA_j=F)D-E?waNSTR6$8~Vvpf{uI+9I6$7>dp zfD0(qw(Iuad;YV+4SFg_$H}(Ekn0Vb*+#gC?X%~F<%Uj(x;l>o#fVYjYHZssHS*#l z^xHtd=%G|=2|Oz-)MBrj8yHTqjXQy!q^H0EIWYqi8r5z*lpLdo)eWfoK9>DuRJ#M9 z!f`BslcmjVb3J49?WlDD*exufXx;b`y26;RSF4$1iCGs>q=UyH)wmP-5(W#ve)?`T z!}U@95I~$A*}yVLKr0a$QLy|iq}@iJ<+=%U_J{-{?_NY1_$LnH$Mf_~5U3@PhL0ln z5ebRbqjeLi^pCSrm*@>N4KOo#AN*pn;!SGa@@Gv31{BaWZ>|lA`T~lgYKgrSN{iB? zC;;rNTRZ{cK6LkT&+xY2dA&DYiz9zH()cE$^Yqn)Hy{17S~M=OUbyS~E5foV#T$-# zy&z5Ut;~%m+6(WMfb>2^-iRl#G;XZ+Zc4J;ZExG-KI(Qc3{L+zcA^ZC(S+4>i?#dAbwx8Zc9x=b2 zynrf~iZ@q_uS{e%rr&2$eiAKq*5ew~Kkr(RRkMe)Ykf4u&&;#i&(StO-=Jk*gt`Em zV${1iB_=4)%T$K*GzQ9(X^Q1|m`ruv@{>kmzO*Q`+p^#u(Xg(2p%4Cd!}C-D^8- zKsu?+dxG;@!X3T^NicK*Wy%s=+gA%QwrShr)Un$8nM2Lk^J_N3|ojdOBgr>fxz<(RC9#D4O{JoC2BAhXsdXc$R{mxOQkt^Q zgyuk+QNNg0FG*L53|QVt2Hv9q;3;X&uLV~L@;tF7T7Ytnv6+-uKkCMEBp>h2?V%DJ zZejV$U_+bpY;`|8bV4JS3ETV#G!VWSH3A97od_$M-TR}dlAsYBH5DtmZi31jiy7vR zN|2#x-cMv}7Wy1T5aAsqVaj4`Rj><}1){K#FUt`}NNabyhd`TW{YimcGrMZzw3{9_ zSIN2~6MN3ayAf4_C;8#ttG z-N3j~MhMi&|4_sdgjAy_h*;DQ<2oBGMOW&5)4xeULNKmNL+d4GAa(fz+g;8ftO7^t zA0Oe{J9-T56t(O&eXmHD4QR!vMGh0ijr7*F{GbWvJTi=ruP9>>v%Ul+&a5b}Nl3)w z7h=9azc*jaqc`9BK(T)Ta5*Qn=v(kzLzjEht*cBEs_fOe_V7hp$F7iCUo`>U&6U;+ z)3=my(W23E>o|aAD)!JeZ8{+h#DnY}qKE z2r){7ele<=__-AzzY9Liq6H?C7w`c~7~6fRKcrc&-z+pNTCTPzL#~0Rr>t(Cc`0?7 zmL6ILa^YG?D1p@=D53f2ZEb?A^}|+Uqt=J z*FYA?b+VCg{xCMlmzHS?|=CaIS>1IA#h8i zdRdI1EP{WDqqwB37Ej2;ILykPRn&1K4#a4T+!{bjDWJ=A_jBw{<7qcO7t68(pWBpz zI2TmeI1A^nFX;F<0JpxH4;bmlx@(qMFNqv5We@i=oIu{Y!eT0P`0E{m|4iXf(RG=Z z2qU$V?hUYAfw|zCNGBvi`--1`OjH?N*E$w~30j^0^Kb?s27fSy$%7gg5^Lmzv;--n z3=)fO%IsG_vQ83X3Ca1Up+4bmn@7(|4qV#uJ4n|;Yvac*`+TJIQ@kz*cnK5BrCn~d z^Vp6yMb3lj)jYDayciB^=OK<{T9n$*VZYt5T z9(2+GS^foN;{-dx|TuE6oo&2(u?)w2XaR?;Qm zZrp3TpGk~4nFDr2{H;xc6cUMZVb#1?NezhB)UBt7=#l#8F%Q6`1ucr)5Ghr@GqK-T z9wLz);9)(#ntWw`B8<;MCv@ZDCY()G)KCOnuDKG$Y$N5OjqP8Brkl^JZV*U?7f6$< z5!ObHQ&$=3NO-NJ=V=)}0@KFr&)T-l%9cBrJedULf)h}lEjnh$hV#dWF$ z0#}GSDS1gTrG+~2b2eWS4vXV(iTR`P5_VC^5uf_zrh1EwU7F$)Yz165)`vtHNpC~w ze`DbD957r;oHUbgicX47d``2Lsfu0m!P~$_csxQNrQ19I)@Iffl$~hage7o%;}j0r zbSAwX|71Ud{b_I|W{caWL+LKJaGecVf*3X^d3My}J`-9dx4@>cK~(+#8N^I$1V=#U z!BDXvb(z~=AcA~vxbt_Z`tD&kK*@GSpmuA&Gr?*Lt&V4S36Y)=pXAA1igjQc(O`B1 z-;^alq1Qh2f@Of3PsYBrl16q?jcl#dS*Z>qB$-9|2(Wf8CcmGo(2$8f8H6yQSs@fH z`Jc7ErTjJmtt@;F!XU^a*P;t`@C}$3kI{!L0De`|6JE#0%#xy4UPA=P3;Sle3>AnZ~he-@kvnO^W=K{S&fin_HhOB z(@B(zj?p8$Zd9z+oDC2Igmfw^`mKGaoGhi$ttzs(rObh98-NZ+Vokq3UO z;0riJ?*@7qBFJYDA^q_1UDnq4dsuudbd8b5>!@!+7{p8HT%jveHY#%j;wBA9QGL7J zn?YE>Nv4-8miH)_TQvqYKt@#`zI#5@1`C4RIr96HpbU=tE@J>~yeNQZuYmMO>Hm}N~)1ueo zlR$>GgiYpHFhUC{mbVnoC@m4h=Iz2qv`~zZVG#m`U}|?tZEqtEus*r4>gvLT0W{PX zWHfo*rlznNw{vJmo1qypm7TyYDJXUr0i%%>hU#_}Z{8+kXajK7K8Jk82^l{XLH;O! z-{cdotUx=sL&d?fCvYG!qsG&9eMQ3Mrm!{yW$&GSna_8uMhJmrkk+UXkV*tQf`p49 z_Ovy$2{hRRH#}$FfL~`4Urz1B9Pa{ou)j9?QTLY^AJYNv|gvOsx}p0w~eA1^drcJ&L-vvkDKnWV!DEahb_dkRY3@?X_9e%MX$qj z52&$TxIpew)@av4zA7R_+}N{mXXDpL*t0e))_{vOBdrqfyfGWBh?#%<;(~H&bi6;L zU_nq`Ld`L|DuJ|B7~3GWH$Z)1L|nk*7uz4v2t^?Br1!VL-GSVl4L}hGCLN|1;6|bA z5td@O`g~I0qqWB=N)XBEtP??LT*FYOl8_wI94c|n)w9e0(TkHy} zHf`I-mCS)NE^V$eUb=T`71djw9X;E(zSvtE!x|!BYufUH*@H_v-{v`T)VuH6qfdZN zV>H~ZhJWZzWjd*s1=gyD_5yfgtlQPj0|Pt%U<9TaP}Zg6L>td-G_v~b*yf&1CKUjH zk=Ftdv)YT13lrkMn^zwz7ar+?)P-E|7-!u6=0T(L49PF*Dc^$|#&06Dqvx1LS%4Tl zCYLb}4jik$5#jIy+eo!hY^tB@XHsk#cd)XurjLA_glcZK9=^F|^!EP8UnEw?i#Inc zzkfbo4*Gt&{2;P|A{u5j?8-c=92Eybvgv(~Mp*-qK-~iN7G_)w4e5}RS!KccQit^d zvrb+R#;S*X|AI0%TAr!8#|F+s319JYbsHqt3^7BbzZW&HD%H*@?GU|U$%tK31OKQ2 zglA0$<^`6;AXMvd$I3zIq?wn<%#};rAi!!c{ zlz8hnN}PGjxON&D;1i!#LIuna`9f!wdKH}q^(&1#nuoh2a2E+};D1dgu-p}x0reIB ziT$!8NaT9X=|Z;2&&L;PS`_ji!;%@oXD?`ZScA#8Ntu@+Fxr80QEx~`_K0v1m*DDd zsx3u>EDwfYWEbABhOEqwwI)s`Zu6y|WEUs9$!ozg(f#)y7SlGf19~-AvCBqs0kbyM z`|+l>Ywm`Nd5N^&jqwRqMZCeh;j(0#I=i{|Qc>x$55Ch%e(V9Rgla^Zaoy|6MFLL$ zY^|s-;_219E^S*I#qL3vz$k~$r!Y78iLs|0>)xUBgFGYkz&{Ige;zfdl-?neKQ+I)!4Ne6fEW-KODqFW zd=%Nqv)L%NAGVGSo{{QfVDK54`Ma1%fz+VQC2aJzjQmNvc~jh5x78SAMM*@&A+ycc} z4UnY)1cxuO)FWB~*epYtHy6)zrmVCfn%i-HlnU)W0Ln5n<%RU?@z*thHHDBc%0L7; zvA!~Yx5*B>^IW6AaQOlmA%tpF8IU)A=9<8k_Cavf#{2{%r*={~L4FX(aqG1aIX6#! z>u)7!+5;62>LMjNeyclLfU0JYq6Jk(`)3{Gt}KC6*BLhz{bSM08##CeJ-&UdyLQ+c zol~b;WS&|A{xZ~DQw=6+4!i+!Qb-3?_b)>b#tm}=uv%%899*ud!XeUN3{>Xd zo&XX(WxOKtr=fNuU_}dfipAYSp}lD1g<}{lA?wy(E1Llsidy_7WdTap7~3$u9{aQR zK&c!TG9ijsn6D~pLOe!sZ7Xlm-J3-=98a0F$p_7wl~qa3*;>Wn@36_jxA+I~Z}=>F zj)nI_>Gu&{XgdonkmfM?Y_)}JMvF^0e89(97*P?Vef{IqbGj{8vau19ALJpg8q5nk zhmaOVJL$o&i1a8BgZj;SAwL;*Ht-{;f+QyDwoQOMUI1Yh6T~=w-FN6zTin#0O3C#R z(lUv)pMM7blLww)X$JC-6sxj#EXHIa0+m{eC%qt^|VP z7&0&Lns?m!Dh<;cQP8o=u7vLR=qci%OtfR~?aWBM+_PQMn38{8S3O5D+?XYnhy4db z{*TnHtmK-7e@wDqZAbOL9N66+UpS_!pzw-RVE=m)8!s4O_st;#Ki&TxE%-2}|3O@t z!XeXa>Q*U-palPgp~XYS_JbQ1=VV`RChhsIQnTqa)GI?~d}Uh$#FE@&ocK$52Lrl* zw=zIV@Z6lk=KA8;oTK8e+qr7A8eBR0(BF8-6OvQ-n6?Ki{ys#}=tZh=_yW&!q7x09 zs^GFG|EIOv*@S_=h8Z@L{+}%44_jGdOKLy1yU}QcKX~@BdWXb7@j@0jnFLFgTpy+Wj^Nm zb{T5||9?jbnyhPrRoy|!f1Bso{t*}-aZTg|8IJ!xV2D4)EB-~m{PoqLL+aWl(mjE$ zD*@Kj0NUPqK+0++1b=;$#b7qIFzxBjXkpkS|F$l5gjb|7WhD54eJ-OMl^0+<$Ws=9 z_CTiJq1l=+VL=AqD088nqj|hfy8|T<&4DCPOZb5#z}BcD0v83$DiD2jJ65;Iw0T*W zNx*)&UoSy}yNZby_}q!G04i@P-RaR~-gGpO&Ro}E46HT)?9XhA+YW#0(>5kzFd%4p z@Z_cFo*Gf;i#7HHwj^nm1FavZoDO0x3v#yX6&*?I01)2~Y>+R?8Q@gIS{Q7(S*@6C zSayMZ;yDtTfWQjxBtZyJ=*Txp0y>Qw~Moy-eY) zcw5NZq~2y@sXq!dC9ky`j&Lk}p;Yi9f15otIR{c)AjSIT^t!&kN~>Wz*~P!IFrhgm zx@GDf4FO$-f^#!-=i z&?;Q9+Ztycg@n}9@oodEl8q7iyLZ>4H!7+8=X)RLo#DRDX+qlr+sA;Wpn1KpD^VMB z^wuD0BHMx&ATdFh3n@|>q6Z&$OJKoD#w_SIH&~A6zjS~a={b3Q1xNa9_tHElhW&v2 ze32lu(jj#|loH9zS}^Ehdq@{7=*M9w((Ka1(2>$-NU&l6BXy+UtA_F?FR`_V)U8j^ zEd~Ys8mQwO2#TV~0Mac0Fl~?OJ(b-*jDSN#FqGNrAev|3=YW?rJ0^7w zeHAh5>CWB_UJe)q8F!es%Tb=)%gK`?2b1Q=lwuFcVMc$&%&HCy*aTLj*NASeMVdpA zv&w`SaOqN@qSE67xf#D84s^IH&w5hS0OpLj5Z4{-N16>A&5FeYIbYT0Xa&atR9H*k z4XHg?=>=>)8KT|uP8aDL5p04X)eCS(f_A3vioxofi`hz1j)WJr!Y!CLP&2(Vhj1x! z@U^w%So9*9b#&cmXMCj;k{Jy+M{gn65u#zXJ-=d>pP^)k=5arAq3&QDA{H^;UDqPO zn{}xMhz_kk8G#0)-d>fbEF<>oWS_2C=sjBT;&iqqeL zPh=Mq5zYofU4=&-;iTZVHwJwmp|K{$UgxS#Xaxn;aW!a!291S#LDXMChhR(uoTSgL z9Tw(xU9QwEvjcx&;zvRU@U%g)JOQo>yWF3^w2MLxt$X^A+Z5Tmd-niwh<`~3inJ77 zMG5IZ{dRF??bZGClxUXsARI5`uMwZNf}k{XGj@Yx+2UxXs07OPJIl3kBT+9n-9+O~ z2^wumKK$1%H~o0!kEF5ZHduEmbfk^-7y3VDY=zdldnn4<-*B`*qD zG@51>p9Z4k1;JzidnvtYh&EEkWCe3*C#8$Y(9BGIw&E>*rdz`EP0TzaG*hTWLOxRw z$RHmxja$Lqvb!~v4Vhu~ehe*@nmrmrNg5Fu)s|REPL!Fn&eOVac>#=z)M1O`?IhnT zlgI)Z$N+p<0>_;ZtR0n)0ukNw01)`N@AR1UxtJ%AYz>i}hwV0TSqQ3FkUO^-GsCfdZgzXpq8`3fOEf!tVnnx-4B;IzIDms12+g|4INx@9lYksf4o zIjMb%T(%R$iy+|`_W=-tJ(1HPs3r$G3tXWV%-hH>IirRl4boFN3`q4FOT$>oIHL4a z>iuYsHnX+nqs#^RT0I`opkSE<7h0xib9{XnX%XtQ5MSQTCe3nM>M|R;gQU{ACMmmv z6&cDQy*r<=cUYas$@ujVU>j@=+Kap8D_S(8?08y z3}|eM%x;L`^vWiTk)HCMC5$>UJIVZkS($VgZ6fBqwL`eYZGB7^*$Y8o*~{^~j!-28 z-sv>BnuUrd2vL#!@b(E=C)d*vo8@u$kSlC@A!_q|BCELNYd<@Y)12C=&F%KP+4RrH~5mu7;v|t z1Co%PNFW>|1~-VHIep_17268cM&yb?v!PzW)C6~63X-1z6lpSf3+;;cWcT|UVTfqC z0(Z2eo7DWnSd^DzL$nBos*VHh^_UcCLF8{?4+qTATg}^bve+#DaoF_;G|Dwe7Ba!j zCgt>YRYj^6tp9Fkil)sKS%?rhnq3Hue;TC1W{0=PFUOmt+Jh~v0OQqLcBvGd!t~qB z>%YcGzvbQl%D|{{4g3m6VVTW&mA4X`_j%=9#D;KzE*mKtT&uXbvEC1M7H^bLg2Bb^ z&FX{JnU2cB#>%r)XzZ6|UHGX?S7RiQ43CbpnlnfMd?{5`34Q$Rj`vPs!U;DTJH>Z?k6 z8nWf1`E|^wmQa2q{Xk4n6>|O7@0kCZz~AxOH~o#*`;SIa8XNvio3g`+Y5@jUiun%) zWwyr_jCZ@pR}g3r1>PjN-GSiW#)dh@PZyx8lwf0jIOfTijNm+T#=q#$drD7oo3iJ0 zU6S_SzP3Q@Gk~88lKm2?hbVaa!RPFQ-C?(auggWjpPaaK=O$7a(BMmXs@|WGf6X-z zio;UaUhkV@-4*9WnckGm?r{-81LPv%f}1m#VV6m3`fi{qgfC@!4S@eMNWdH+`fThb zpk8D*QCIw+h~yh!Xzn;5t%?9(s6`RA3kdBFRJ!8|kn2cqR96zs>f2OHN{}dakl+c( z=NSzDl_D*ObO8mf^1C+~7^fMLpCTLY_r0*6^kUqv^VTdOVH`}(^vLSQYu-%ibSO

El`Gyk*-7n18}%R=LHaJOiENh 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 literal 0 HcmV?d00001 diff --git a/wolf-sheep-soil-creep/images/tutorials-wolf-sheep-soil-creep-soil_thickness.png b/wolf-sheep-soil-creep/images/tutorials-wolf-sheep-soil-creep-soil_thickness.png new file mode 100644 index 0000000000000000000000000000000000000000..0bb5d880109f914719e9473244ae7c32f7a64dbd GIT binary patch literal 61912 zcmd432UJyA)-{USXq6O3Km`G_l0|YhAp#;fCk4q#vSi8<0|=-j$p~D+C0xlFOa#f{ zl9S{Nk~6%u?}h6A{@1U%`i=3A@zoeTilQ&)o^$rvYt1$1T>F*GeKGQb)CWmONXW(S z-j*XF*>6umLVDv{CQJx?$qb|fUT zx5aPYP5l%i4<@b@Xz4QB;&)xAKfor?%G56vEpdoA;J&WeSU!#fc~+%ox6|l!<+M8 zXbC^KpFTQG_<>~b<>Lc{pU#Lq6CnPI@7(|HOTFc8rXv|_9{lb%@c95eGtO<=I?`)t zs#Dqa9(sT*)3Vp6W@`mz{9ALn?K($$ikuY76_VnlBk3#(XYXYGW8Z;8uMg4kD>$8e z!}Nt#MS`KNUDsErO9cMb9b+?*l2bNaFf+Zya{ZuPi>7@ym8FH2^K7Y#LAWhe=(h^W zrgY+q{+Bm*uWG*}!(toy{e!VpXUs$Swv^(vagP-{r_IIIECF}D3this@Wn1p%V=#L zoLX_#`;K`arcE6cVbl)4fq$%S4-wYwdJWU2J;T#zjROk4m4C5X3&cfAu*AqQsC!0T zY2DKtYxNgPDMSngW36#{d4DY~(Y!+mbOC>7#bc$ih?o7;@4r<*aYHtBA6ATbuO$CA z#vF;Eh0m2s^k)&3@t<%vmdpE#0E{5sMP8IL`A>%bRky^&wwTBufeZPMi}B~yX^iV2 zKIsEXiET~dvc&wIWzk4fs0=)JrPB5~SK?o7zdAxlxNFoW?O*Z#5Ai`YPD5j+&{n{a zqcF*5Y)}(HE6SDesa0uV*}SYeSoRTzevM~z2$$|1iuTL$@poiv7+2~v-)5P$vU+08 z)|X@-2{|skjvj9=a-wLTt717J+8kp&awYu`=~lQykI0tn)L9aln4L%GV%z*+EW4$W zg#uAmjhhhD|2ED1?+@HUIe!V!F-3WZ+;Q3eYmr;MIV1Ov|F{iDiQg|XAc2v~CT>3Q zzq9!Y@$qq2ii|z2Xk~;L|Dp~wSQ9t*Fo!Wd;mK0Ffo}DC_Spt8dePouml(J0t+Y}q z_1m4fCdAE)SrP51sp02C=e>?fZ#q{iM zOyCxJF~1nE^PA_7#pV9|LN<_APN)83UdUPQi+nyDT@qC z!PP;(*&e4EmZkRr%36Gf_(!97*G6M9J{gD${rt2qZF8xUZBa#jba6P;f^~70Gi)b) z8qUhSPv72JFBe{)%&315U;K1(jF{ZrcKInI!#F23Tq}U2iU(QcC65teW}XUZ3JfKQfv(5|Omx>`>{? zU^)8XMcK1okN4*e_?QHyH55J*sJTK)GZJpEMKv00lha~5*_PIEi?GcMtu9A8TZw-3 za~zLNY%hivDOO-NRoVICFg=~vY0KMr>6bm2oF>!63SkXYr6bB(;-w?oY+E&TfByKa zii=dB_;@BmzowGW`9A+xEb}w0egCV=`k$NhL(N*ET7`z+>-HZIS^0cyrZ>(z;X_{D zK+P4d-u(6Hg7b&@ZO17DW(uvdUJ#06EGri#6K{tLxu|Fo^j2Rre>B>QF}wT8fVZk* z?M921(^T$qkCSdWtv&Mr!9@PyYL2?sA{%{&z4=o)CFMhWQWTR)?UltJ;^C8&0+Sza znP${*&-Qz<>35FQhSS`7{@W{o>D-tbxb7mSWfj|I#URH?PNBa0=`ji421}9}`>=_kEGgg@o*>>`qp) zsK%^j->H4yUM;tPY39P{kT` zHPScNF)2>VFnb)lwdHC|*hkx`LQsz1GBq%=(=x9@QnF~DjCDy;2W zQ-*k7on9Oe^3^=HnPPiJp2c`7a1G2 z;uW1zM{Jj>T)2RJt(tlD1SUw!(-?N_M0#S|cna*3h7z}Jtk0{m%Uwo&axplEsr-r5 zw0=(}B{M;E4U(Fnp&;#46>XQrW*VD~PrvVDYC$pI?C)4W@3t{Z**UDOeQdec&CPj$ zruXHZefw_*v1+E%32&|~dU^Bo@D?u@&6M_TE#G3=ma_+V>5l+*2rPD!Q0x9w}p7eQ0 zQ56=#-c_ZnqqXlk>b6>?9qqI{Ej7o_{Qhw~X{eh-Hr~XbXa9qm4@$>=P%zi@J~Fk` za@$x*x*HpapA}I}+iPe(=LsQ+$x`D!f5T zl&iX+LbOjb2|g2G!IQBS9+4JWRA7yJWa9IW703rZ9Cd;j4RwS&Vpco2pL+Z?NA1hA zx1Mtv=Qe$~^+NBE{ji9Ebts>;{?aMqHOEHr%Pw)3>%YVX`)@78i_ON{~KZxBl#{L6Err-tR{jK30TWK%&!t8&*&rF53b zaQl%E`Srcp>Wz8~!u3^N_rpD%%)~=$OT#NS3*9ziI&QgB=^5#h8&7L-c%$zgSX~J9z;Cn=zGo+qmBNrQP%p@SbbB@ zKltu&iI2KPQP$^-gO~^b7WOXHFua8U!W~e@i=1&y29aZjC)XDzS`%>1iSqH2zRS{l zL;iEwP@Onx`9{L-xX@DF%Wc|Q6B#jGvb`nQ%$&@Jzx>HpaPDp)+yLdTk9D`+-Sz$) zn6-xnJ$U_NhBj5``9d~ZFd~kT%2=D#l>uJ>z_pUjK9&Pzipe~pB|o2cnB{cq)Cn!W zRyWVPs4m^=@8}Z=`^|exO<*chUNqOwv8YM#URBGN!ueVOOZcFIsX+hB^U;CLmp{!* z{CX9A)u%=^*VUmIs1%f)JrJ4u!v`R1V^y>PAQ<B!B%`NGvX&($!2-vV@yFKb*beb2_hLUa>0b#vg%W z`GNSS`p?ut#-}70-UK&4>*{Oe~zk*&L)Cu{klyy z26ZCa8|UhjSs>x0s6?{s-tjA}JwWgL@ssD3kO;^cc{9c9I`{$1}KLU(DheP|IOMJ1Jo9touIXXd4j^4=4(<@7j}neKvRgQpim zo}(*5nrCb&nf11~T^AvA{_sxO)LkD}E>W9(F_dU!@a^SM$*#S+TJX9J@SX)T#RaO4 zdo-P@qrgNjl;*Pp`8zYy2b0-K$IhsGBTW?xE~-U2BSDUCLF~ zcHuJ58w#9r4COZKci(p?;T6v)e85YRH7{1D69|ExQJfz(YGA2zBWO?F zixe)3eo=EsVDgB=*_|i;^5*O<#<*jWlzfPPxVbDHnL+0``hnegxY|=BmY(hwk@C~l_e!%B zYUVq8x3bTD64!!ILz{j8Fg}ZgI{eoFq)tOTdrb%zlMkRPQ`2%fCsS_Ct_|Jb*0wa= zfv04wFSA&cKK$SlOTkZJeu*Ww|A)l~Y z_J5mOqVUlK>O__Fc+J;v^F0wROYJA26gm_sCMh8d%_$M>G8}9WNFZuN<7yc8-1jah z{^`NSJZo~-9U)5hIJ>RYk#b44A`1I1OEEr;)ve_cdV@$8JJZaDJ84~3Rizbg&u7%B zj*!Q9WSIm>)wSeU^j)Yh6TLSF+c2w;2(N=9Y7Y8S-P97wS6Ho4~na*RA zQY*14A-Awy;%*g@fu*VrLjf6e%FA8Wbr{E#h8Qt}s{Yu@Xvntw+3+?nj)5_vv1^^3>xKjg%J z6k5~AX>*Ug$WkKS$pi6Nh8(KRK37+~QeF5e=J92gm^&P_=D9b|p|&ypci3^jWL%H-+y0(fT$y=evvoG;LQoKG~2 zI`T3vG{sBTX-kss~gEr5vj+IhX0pH zzg@Or9!Q!kk^h#93y`>&^J_)I-=METg4U)eFeCT>HkVkK_qW)zKdJA`#3jNDB~i)z z?^wuxPx%20#H_IGt;{gpotWfxGgN@b)3~;pPxcX}y>xQ|(-_w8s;%$2-Xcn|p{#GD zWLd&9Qp-A>)l9ju9n_k|y0l2C`nI=#TeCy-ZPrwWFwko`hh!g_@n@wh9U+No|0ms!}jDz~M)Enb;JB<(T zAqmm6+;0Ar5SB96|BdLh%lR87sQAZu{%scYKj)>W;!}_X9|jm3Z8OC<_}XzRk*(pi9Z7Wh?kev(|$otLnR-7pGBk> zNQfs;?SsaPIWOL~~pY^aogazGf{61oNp&b2(APScDPz$G0~ zx{WF!ji~tZZZ0%2WxK3*_WD3UIvK%vDl){X4hd9e~I=y z;lg;vzWW>|z!L-V;h!yKb7fDG6W5kkVYeBrZ-btOW^)m!q%ebeeAfNYOW_Pd zmcG(&U1bGYvTm}ozHF zffj_IfiLBUB_0syUA<40o2dXe-WDE>X=w4rAN%!OUQ)G&j=*KE(DOL2{LCE=G3k%` zlYtqg#dPU#T>i`+d= z22@aQG1``@Sx|NdBpIzpAtyUKVRUsvRUv!Nbdsu@@D~oVr6-tLpjVmSPg?@CyFzja zyb$ketc2UB@I$(%)CBO%KDQ1FaYj7pe|2&P7OBoeY85$_KBqB139BiETAK!75ihwh z0SMDd11&O%XLRNFeFs?QoCz1JKlY3LL38NX+OOfDnbng#D3+Qx9KyTQraPeCyQUNd zP`v>o8FY!JY64dRSEyu6;v8LBU3QzwYNgiGAYA0lRhh z0g-ei6g$=Tg5C!K+N2+e%i~En{q^k;k?mJoo9jGl?;B0(l7M9-=Xc%OjK3e=ab-ZF z(dDFw>&7+3a|Cd7-TKHhMLjh9eo`6PA1mJ@2FwonK~w5%;EXs>zjp`9WCTGEpy^$? zBM3~ttg>=zY~TGChiC&Cx`MZztNMXss+}fKYb1F7rgX}BqxkUb+wu{Z$3w8ay?Qr> zc_=$kjsSH*lQZl2b1`pPZsHBl{c8Vmd%a}4iqT;h_sGapGv6jrC6e1r?GK+wH`rZ7 z!G+~_qxOn0t4an-xVeCtJL3f$4s(M@t&ZvbKv8xtgfsEd*YFr0ent>wBKI$MA(5Ke zj7Z8DMrJFK9GQO8Zqp=%c!QyzdLTluLyV&wM?G~2VPBwU!{^q3iUn>I8Z^ch8jdFt zO>HdS6~M3n|Hzod=?yelJX1>10ctZbmxi2No65kOt0h6j4ODPyn~jb2IyXX;k{JH_ zOu!#W4*lkwHXWIzs)%6VNKw7@`ZS-_+Yy2g;vV=Z)70MuC}~Rj7)ZI^j`m1D_gsX2 zGWkaNVOoA0;wRm8mOB|F>cV$q)ymJc8v*Q0DVcIHJ>j4KI3PCBl2SDxEwX8IFPz`@ zm5q*xJI{I~aHi}aQ*!-iQv^mL3Rq)op)hnExl0`; zL8EOZsd<<5YeE#YdO^&HHmHm6<|jT-#lF5n>~@oFydz=OjMLu`Z<~;mWU^3TU4P?% zV)FJ}Pzgvf`pk3h7dCLNi%Fhlz;`^3@g?H)x$C*|XK;mc5*q{b+lKY*(5AaOPnl>} z?Tv7p>d5kvzt5+aR(|NzwF_kK<3YgSo2Qk>%IMce-JsM(%F+pkd(@I0xE|yKnzs1V~}Jeg$#NeCsInMd7!4M zKzB){wrMpKpv=0^c0%jPS)?18jh1+`PYz+P8 zl@K{Bg~W$O*iA}3cHb`seT&@Q7-<8U2Pt9IPqCLHo}7b)Rp@p-Z!^7f+mO|O4c#gg;dhgN{KfgRmO##ntL#p4>v$?8tY5n!ZzVF83D z<}=P1IXMa|&c;eyAnY5%F@^E2;E_N+vBg~1JI#+i~C(Fjqw!@@wWZ)>{r#9aM#T> z$o-~oy-%QTQ_IGetToZKtSTcl`sg%;LK5s(2&DFR2N*uJ15cMEM92vw2@Y|3gndZz zFBYKpfrNmKDhrUnm=awje*eKWfW9@sAyK-f>&kv1`vp8G3coG*Ki)w9mADdv#nFXgTD*`G%;Y0bh~YYi0(sAjAGGyEyVA# zw2v6Q3+EjW$LO7Z-dGrKWU1w7*>4Oe$C;|ea+Jf8s}geQ|}4ZKh0jKn{*ro zwHwv*Q=K`!;r2azwY&qb+aCNu9=K+!;<)2lsqe>J)dQ*67Yd-k2LJ`6H6YrqtAKJg zkXkTpoW8z`t2Et6z zd5hw=aKLGlo&0u_Cqqrs#kpBDL!wy~k4i-f3pufRh+Gc0k;6APm;QP=tp25PW&bbs z?jFXiSuPJ}?$0RZKsUg<^y&(hn>N(#e&;Z!K}*;bY+iC?MxBy|ADUZ~sdxJ?VqWfJJ;EO`S;p8I!g+)^r&$qbCKQJZ>4e zw78-AMm0lUk5ZVyQ2eYy5$tbvvIsekn*NRX$nDon(jrAV>iJs$UUPKT@@5Ch%M;@x zsDm`^R2|eEyLYiGrcjO=`)S*XP`2GgX+ZebuxSvb>@+W&EnDeXE>=D|c>y-HU6<2L zQ5p!OK{=T5_XOq1Vji@WKoD#pYdvZzWnpehJRAch2bYY!By~dBV!A7j{ZYl6i&zbQ zFi9|NU7vg*vhi>Ou>Qt9?GFkyHe;Bevcs-F|Cr0R(ZC*&82R9^1rm$sZ5KK(LnOCRb3xG{kJ0@#&+)3SR-T!dsi|gcOtc* zAGRO^E`F@#-{{K5k+89qP1O~K?uRH%1Hfa&#e2r?JH-3omDa?keTS>#o+T|qt5yaX zB*?gn^EI(wcc&VIRI+iex$0d9l%E)>Q0}2cg9wL9Wj?et&4FLAu(WVhBTN8Q{x(z^ z`35F~0dF3kp+I%>S6WFXt*L1ZcW4w9e#Oi;EbO>aGVlHm<=F?K0jJZ680KaaVR~mJ zi(sd*KxXT`uzaQ+@Rad1UFJV_5|UbMkhW`{4ycyO7WK1WB}`VK7j}N4&{Y57)&Od1 z+PJsZHiqe~duDD(&TgBOp>#@;AdA62x$OCYJN5kH^WYMZYR07G#whFRl5JEoYK$PI zwo{lIN#p|97EbC27O*#8T?ImRVzzt6)&A|s6Cg3IT@I@;?hssx9B#QEFE}nw(;{9u zt~DGw-cm=v!|91F)H+-2h~^Gyg#Pmgs=qqRNl8gw2uh+vI%#=p`qr%(6YJi6!WW5L zU6e7}Yy0U56%^Eqz%g49=$}0op*C`_oc=#mUFJ4bP+3BUgz+6hh73D?uBg&r5&S5; zg+J=p-9EB!^M!U+2XhRtxvffWfM(+fewY~5>02q?e+1qn+EK52<>Zc4QBS#}TGq`;`RX>ZqMSw7l|>)q>nbb8Ql*O6oiO{-E`1fwRhN$n5U?aH;JoaipF_caFC zM$inIaq%Ykpk@cH2qWcZc{%Fh5hAlx-_H!Y9oT(ePcw^>O}js85t*-;j7uw{mL*&2 zb=&q_M7Em`>$M`ot2hSSa@6QfcxR<9j8UkIsx)TdJwdfe(t%f}4WW8o%tvEz6{`nD zO43*yfy@8?((iJr@B4bAA`P`}`1Xe#LwP z=bxJhL9^1eeTM}%u^PwXrOYC&a7;@ zJ|^v!-!?$-9YC63WK=rD`@*67ijh`p~w%jLou?CPuNyUKN z6%G*i=GGwIkHtqgj-T@dAzMCub3CPJslyV0Ew^DIv(Qc^U}y1J@O}pq(`g`2bP)?SWCs7`90rV(H-8*wb315%LEcla-)rK$Lfx3>sRiszH6DK^ zMcb_uiktjwSDo9|yym!j!U%prf2(-w=D-z2V@CoB*O#0$GP}Z

=*HXy9_@hp?nnrxM^1~GZI`pD}(%;kR;N!)+WS& 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͇(F1le-J;ZD~r;jFX+$b$9$ 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 g|IgpLBfPxTG% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wolf-sheep-soil-creep/soil-creep-landlab/.gitignore b/wolf-sheep-soil-creep/soil-creep-landlab/.gitignore new file mode 100644 index 000000000..ea1472ec1 --- /dev/null +++ b/wolf-sheep-soil-creep/soil-creep-landlab/.gitignore @@ -0,0 +1 @@ +output/ diff --git a/wolf-sheep-soil-creep/soil-creep-landlab/clean.sh b/wolf-sheep-soil-creep/soil-creep-landlab/clean.sh new file mode 100755 index 000000000..ebce578f2 --- /dev/null +++ b/wolf-sheep-soil-creep/soil-creep-landlab/clean.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env sh +set -e -u + +rm -rfv ./output/ + +. ../../tools/cleaning-tools.sh + +clean_precice_logs . +clean_case_logs . diff --git a/wolf-sheep-soil-creep/soil-creep-landlab/requirements.txt b/wolf-sheep-soil-creep/soil-creep-landlab/requirements.txt new file mode 100644 index 000000000..16efd4ddd --- /dev/null +++ b/wolf-sheep-soil-creep/soil-creep-landlab/requirements.txt @@ -0,0 +1,4 @@ +numpy >1, <2 +matplotlib +landlab +pyprecice~=3.0 diff --git a/wolf-sheep-soil-creep/soil-creep-landlab/run.sh b/wolf-sheep-soil-creep/soil-creep-landlab/run.sh new file mode 100755 index 000000000..14753ac3d --- /dev/null +++ b/wolf-sheep-soil-creep/soil-creep-landlab/run.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -e -u + +. ../../tools/log.sh + +exec > >(tee --append "$LOGFILE") 2>&1 + +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + + if [ ! -d ".venv" ]; then + python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate + fi + +fi + +mkdir -p output +python3 soil_creep.py + +close_log diff --git a/wolf-sheep-soil-creep/soil-creep-landlab/soil_creep.py b/wolf-sheep-soil-creep/soil-creep-landlab/soil_creep.py new file mode 100644 index 000000000..2f17e3830 --- /dev/null +++ b/wolf-sheep-soil-creep/soil-creep-landlab/soil_creep.py @@ -0,0 +1,122 @@ +import copy +import matplotlib as mpl +import matplotlib.pyplot as plt +import numpy as np +from landlab import RasterModelGrid, imshow_grid +from landlab.components import LinearDiffuser +import precice + +initial_soil_depth = 0.3 + +# Set the soil-thickness scale for limiting creep where little soil is available +hstar = 0.2 + +# Set parameters for two soil creep coefficients: slow (full grass cover) and fast (partial or "eaten" grass cover) +fast_creep = 0.1 +slow_creep = 0.001 + +ground_cover_cmap = copy.copy(mpl.colormaps["YlGn"]) + +# Create a grid the same size as the W-S-G model's grid +width = 20 +height = 20 + +rmg = RasterModelGrid((width, height)) + +# Create elevation field and have it slope down to the south at 10% gradient +elev = rmg.add_zeros("topographic__elevation", at="node") +elev[:] = 0.1 * rmg.y_of_node + +# Have one open boundary on the south side +rmg.set_closed_boundaries_at_grid_edges(True, True, True, False) + +# Remember the starting elevation so we can calculate cumulative erosion/deposition +initial_elev = np.zeros(rmg.number_of_nodes) +initial_elev[:] = elev + +# Also remember the elevation of the prior time step, so we can difference +prior_elev = np.zeros(rmg.number_of_nodes) + +# Create a field for the creep coefficient +creep_coef = rmg.add_zeros("creep_coefficient", at="node") + +# Create a soil-thickness field +soil = rmg.add_zeros("soil__depth", at="node") +soil[:] = initial_soil_depth + +# Instantiate a LinearDiffuser (soil creep) Landlab component +diffuser = LinearDiffuser(rmg, linear_diffusivity=creep_coef) + +# preCICE setup +participant_name = "Soil-Creep" +config_file_name = "../precice-config.xml" +solver_process_index = 0 +solver_process_size = 1 +participant = precice.Participant(participant_name, config_file_name, solver_process_index, solver_process_size) + +positions = [[x, y] for x in range(width) for y in range(height)] +vertex_gm_ids = participant.set_mesh_vertices("Soil-Creep-Mesh", positions) +vertex_soil_ids = participant.set_mesh_vertices("Soil-Depth-Mesh", positions) + +participant.initialize() + +while participant.is_coupling_ongoing(): + solver_dt = 0.2 * rmg.dx * rmg.dx / fast_creep + precice_dt = participant.get_max_time_step_size() + dt = np.minimum(solver_dt, precice_dt) + + gm = participant.read_data("Soil-Creep-Mesh", "Grass", vertex_gm_ids, dt) + + # Assign the higher creep coefficient to cells where the grass has + # been eaten and not yet recovered; the slower value is assigned to + # "fully grown" grass patches. + creep_coef[gm.flatten() == 1] = fast_creep + creep_coef[gm.flatten() == 2] = slow_creep + + # Limit the creep coefficient according to the soil-thickness field, so absent soil cannot move. + creep_coef *= 1.0 - np.exp(-soil / hstar) + + # Remember the current elevation before LinearDiffuser updates the grid's + # topographic__elevation field in place. + prior_elev[:] = elev + + # Run the soil-creep model + diffuser.run_one_step(dt) + + # Update the soil cover + soil += elev - prior_elev + + participant.write_data("Soil-Depth-Mesh", "Soil", vertex_soil_ids, soil) + + participant.advance(dt) + +participant.finalize() + +# Calculate and plot the erosion/deposition patterns +plt.figure() +ero_dep = elev - initial_elev +maxchange = np.amax(np.abs(ero_dep)) +imshow_grid( + rmg, + ero_dep, + vmin=-maxchange, + vmax=maxchange, + cmap="coolwarm_r", + colorbar_label="Depth of soil accumulation (+) or loss (-), m", +) +plt.savefig("output/erosion_deposition_patterns.png") +plt.close() + +# Soil thickness +plt.figure() +imshow_grid(rmg, soil, colorbar_label="Soil thickness, m") +plt.savefig("output/soil_thickness.png") +plt.close() + +# Ground cover +plt.figure() +imshow_grid( + rmg, gm, cmap=ground_cover_cmap, colorbar_label="Ground cover (1 = bare, 2 = grass)" +) +plt.savefig("output/grass_map.png") +plt.close() diff --git a/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/clean.sh b/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/clean.sh new file mode 100755 index 000000000..494c80414 --- /dev/null +++ b/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/clean.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +set -e -u + +. ../../tools/cleaning-tools.sh + +clean_precice_logs . +clean_case_logs . diff --git a/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/requirements.txt b/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/requirements.txt new file mode 100644 index 000000000..f9ccca730 --- /dev/null +++ b/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/requirements.txt @@ -0,0 +1,5 @@ +numpy >1, <2 +matplotlib +mesa>=3 +pyprecice~=3.0 +networkx diff --git a/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/run.sh b/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/run.sh new file mode 100755 index 000000000..43c20ae0d --- /dev/null +++ b/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/run.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -e -u + +. ../../tools/log.sh + +exec > >(tee --append "$LOGFILE") 2>&1 + +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + + if [ ! -d ".venv" ]; then + python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate + fi + +fi + +python3 wolf_sheep_grass.py + +close_log diff --git a/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/wolf_sheep_grass.py b/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/wolf_sheep_grass.py new file mode 100644 index 000000000..54b267e4e --- /dev/null +++ b/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/wolf_sheep_grass.py @@ -0,0 +1,97 @@ +from mesa.examples.advanced.wolf_sheep.agents import GrassPatch +from mesa.examples.advanced.wolf_sheep.model import WolfSheep +from mesa.examples.advanced.wolf_sheep.model import WolfSheepScenario +import copy +import matplotlib as mpl +import matplotlib.pyplot as plt +import numpy as np +import precice + +min_depth_for_grass = 0.2 + +# Use the same time step size as the Soil-Creep participant +solver_dt = 2.0 + +# Initialize Wolf-Sheep-Grass model +wss = WolfSheepScenario( + initial_sheep=20, + initial_wolves=10, + grass=True, + grass_regrowth_time=15, # give grass a fighting chance... + rng=42, # fixed seed for reproducible results +) +ws = WolfSheep(wss) + +ground_cover_cmap = copy.copy(mpl.colormaps["YlGn"]) + + +def generate_grass_map(model): + grass_map = np.zeros((model.grid.width, model.grid.height)) + for cell in model.grid: + (x, y) = cell.coordinate + cell_content = cell.agents + for agent in cell_content: + if type(agent) is GrassPatch: + if agent.fully_grown: + grass_map[x][y] = 2 + else: + grass_map[x][y] = 1 + return grass_map + + +def plot_grass_map(grass_map): + plt.imshow(grass_map, interpolation="nearest", cmap=ground_cover_cmap) + plt.colorbar() + + +def limit_grass_by_soil(wsg_model, soil, min_soil_depth): + soilmatrix = soil.reshape((wsg_model.width, wsg_model.height)) + for cell in wsg_model.grid: + (x, y) = cell.coordinate + cell_content = cell.agents + if soilmatrix[x][y] < min_soil_depth: + for agent in cell_content: + if type(agent) is GrassPatch: + agent.fully_grown = False + + +width = ws.grid.width +height = ws.grid.height + +# preCICE setup +participant_name = "Wolf-Sheep-Grass" +config_file_name = "../precice-config.xml" +solver_process_index = 0 +solver_process_size = 1 +participant = precice.Participant(participant_name, config_file_name, solver_process_index, solver_process_size) + +positions = [[x, y] for x in range(width) for y in range(height)] +vertex_gm_ids = participant.set_mesh_vertices("Wolf-Sheep-Grass-Mesh", positions) +vertex_soil_ids = participant.set_mesh_vertices("Soil-Grass-Mesh", positions) + +soil = np.zeros([width * height]) + +if participant.requires_initial_data(): + gm = generate_grass_map(ws) + participant.write_data("Wolf-Sheep-Grass-Mesh", "Grass", vertex_gm_ids, gm.flatten()) + +participant.initialize() + +while participant.is_coupling_ongoing(): + precice_dt = participant.get_max_time_step_size() + dt = np.minimum(solver_dt, precice_dt) + + soil = participant.read_data("Soil-Grass-Mesh", "Soil", vertex_soil_ids, dt) + + # Update the grass cover + limit_grass_by_soil(ws, soil, min_depth_for_grass) + + # Run the W-S-G model + ws.step() + + gm = generate_grass_map(ws) + participant.write_data("Wolf-Sheep-Grass-Mesh", "Grass", vertex_gm_ids, gm.flatten()) + + participant.advance(dt) + +participant.finalize() From cebb16ba27c04ffc7289b328888e88e91535191d Mon Sep 17 00:00:00 2001 From: preCICE Tests VM Date: Mon, 1 Jun 2026 12:03:08 +0200 Subject: [PATCH 62/94] Add reference results for wolf-sheep-soil-creep --- .../reference_results.metadata | 71 +++++++++++++++++++ ...creep-landlab_wolf-sheep-grass-mesa.tar.gz | 3 + 2 files changed, 74 insertions(+) create mode 100644 wolf-sheep-soil-creep/reference-results/reference_results.metadata create mode 100644 wolf-sheep-soil-creep/reference-results/soil-creep-landlab_wolf-sheep-grass-mesa.tar.gz diff --git a/wolf-sheep-soil-creep/reference-results/reference_results.metadata b/wolf-sheep-soil-creep/reference-results/reference_results.metadata new file mode 100644 index 000000000..07b011e47 --- /dev/null +++ b/wolf-sheep-soil-creep/reference-results/reference_results.metadata @@ -0,0 +1,71 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| soil-creep-landlab_wolf-sheep-grass-mesa.tar.gz | 2026-06-01 12:01:54 | 6726ec7e3db23bd0cad8cecbd356eefae87b63b2f189e53af6e45f5ef7d828c3 | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | develop | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/wolf-sheep-soil-creep/reference-results/soil-creep-landlab_wolf-sheep-grass-mesa.tar.gz b/wolf-sheep-soil-creep/reference-results/soil-creep-landlab_wolf-sheep-grass-mesa.tar.gz new file mode 100644 index 000000000..3e467fab2 --- /dev/null +++ b/wolf-sheep-soil-creep/reference-results/soil-creep-landlab_wolf-sheep-grass-mesa.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6726ec7e3db23bd0cad8cecbd356eefae87b63b2f189e53af6e45f5ef7d828c3 +size 49721 From ebadb9bf9b6665e228edf5216a5785d09f641aa5 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Mon, 1 Jun 2026 12:11:06 +0200 Subject: [PATCH 63/94] Reduce test timeout to 5min --- tools/tests/README.md | 2 +- tools/tests/systemtests/Systemtest.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/tests/README.md b/tools/tests/README.md index 8a7df6e20..83a71910a 100644 --- a/tools/tests/README.md +++ b/tools/tests/README.md @@ -331,7 +331,7 @@ test_suites: timeout: 1200 ``` -The optional `timeout` field (in seconds) sets the maximum time for the solver run and fieldcompare phases of that specific case. If omitted, it defaults to `GLOBAL_TIMEOUT` (currently 900s, overridable via the `PRECICE_SYSTEMTESTS_TIMEOUT` environment variable). +The optional `timeout` field (in seconds) sets the maximum time for the solver run and fieldcompare phases of that specific case. If omitted, it defaults to `GLOBAL_TIMEOUT` (currently 300s (5min), overridable via the `PRECICE_SYSTEMTESTS_TIMEOUT` environment variable). This defines the test suite `openfoam-adapter`, with a case combination to run. diff --git a/tools/tests/systemtests/Systemtest.py b/tools/tests/systemtests/Systemtest.py index de9fab864..b666f6b1c 100644 --- a/tools/tests/systemtests/Systemtest.py +++ b/tools/tests/systemtests/Systemtest.py @@ -19,7 +19,7 @@ import os -GLOBAL_TIMEOUT = int(os.environ.get("PRECICE_SYSTEMTESTS_TIMEOUT", 900)) +GLOBAL_TIMEOUT = int(os.environ.get("PRECICE_SYSTEMTESTS_TIMEOUT", 300)) SHORT_TIMEOUT = 10 DIFF_RESULTS_DIR = "diff-results" From 656061472c7d270f1dfe5d2461889d73734d8288 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Mon, 1 Jun 2026 23:10:47 +0200 Subject: [PATCH 64/94] Improve the venv setup in run.sh --- channel-transport-particles/fluid-nutils/run.sh | 11 ++++++----- channel-transport-reaction/chemical-fenics/run.sh | 11 ++++++----- channel-transport-reaction/fluid-fenics/run.sh | 11 ++++++----- channel-transport/fluid-nutils/run.sh | 11 ++++++----- channel-transport/transport-nutils/run.sh | 11 ++++++----- elastic-tube-1d/fluid-python/run.sh | 11 ++++++----- elastic-tube-1d/solid-python/run.sh | 11 ++++++----- elastic-tube-3d/solid-fenics/run.sh | 11 ++++++----- .../controller-fmi/run.sh | 11 ++++++----- .../solid-python/run.sh | 11 ++++++----- flow-over-heated-plate/fluid-su2/run.sh | 11 ++++++----- flow-over-heated-plate/solid-dunefem/run.sh | 11 ++++++----- flow-over-heated-plate/solid-fenics/run.sh | 11 ++++++----- flow-over-heated-plate/solid-nutils/run.sh | 11 ++++++----- oscillator/mass-left-fmi/run.sh | 11 ++++++----- oscillator/mass-right-fmi/run.sh | 11 ++++++----- .../dirichlet-nutils/run.sh | 11 ++++++----- .../neumann-nutils/run.sh | 11 ++++++----- partitioned-heat-conduction/dirichlet-nutils/run.sh | 11 ++++++----- partitioned-heat-conduction/neumann-nutils/run.sh | 11 ++++++----- .../fluid1d-left-nutils/run.sh | 11 ++++++----- .../fluid1d-right-nutils/run.sh | 11 ++++++----- perpendicular-flap/fluid-fake/run.sh | 11 ++++++----- perpendicular-flap/fluid-nutils/run.sh | 11 ++++++----- perpendicular-flap/fluid-su2/run.sh | 11 ++++++----- perpendicular-flap/solid-fake/run.sh | 11 ++++++----- perpendicular-flap/solid-fenics/run.sh | 11 ++++++----- perpendicular-flap/solid-nutils/run.sh | 11 ++++++----- resonant-circuit/capacitor-python/run.sh | 11 ++++++----- resonant-circuit/coil-python/run.sh | 11 ++++++----- turek-hron-fsi3/fluid-nutils/run.sh | 11 ++++++----- turek-hron-fsi3/solid-nutils/run.sh | 11 ++++++----- two-scale-heat-conduction/macro-nutils/run.sh | 11 ++++++----- two-scale-heat-conduction/micro-nutils/run.sh | 11 ++++++----- volume-coupled-flow/source-nutils/run.sh | 11 ++++++----- water-hammer/fluid1d-left-nutils/run.sh | 11 ++++++----- water-hammer/fluid1d-right-nutils/run.sh | 11 ++++++----- wolf-sheep-soil-creep/soil-creep-landlab/run.sh | 2 -- wolf-sheep-soil-creep/wolf-sheep-grass-mesa/run.sh | 2 -- 39 files changed, 222 insertions(+), 189 deletions(-) diff --git a/channel-transport-particles/fluid-nutils/run.sh b/channel-transport-particles/fluid-nutils/run.sh index 7a46ac17e..8fa22ebdb 100755 --- a/channel-transport-particles/fluid-nutils/run.sh +++ b/channel-transport-particles/fluid-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 fluid.py diff --git a/channel-transport-reaction/chemical-fenics/run.sh b/channel-transport-reaction/chemical-fenics/run.sh index 9409a4a4e..51fe70d0d 100755 --- a/channel-transport-reaction/chemical-fenics/run.sh +++ b/channel-transport-reaction/chemical-fenics/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 chemical-reaction-advection-diffusion.py diff --git a/channel-transport-reaction/fluid-fenics/run.sh b/channel-transport-reaction/fluid-fenics/run.sh index b2b440be5..4df22d92b 100755 --- a/channel-transport-reaction/fluid-fenics/run.sh +++ b/channel-transport-reaction/fluid-fenics/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 fluid.py diff --git a/channel-transport/fluid-nutils/run.sh b/channel-transport/fluid-nutils/run.sh index 7a46ac17e..8fa22ebdb 100755 --- a/channel-transport/fluid-nutils/run.sh +++ b/channel-transport/fluid-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 fluid.py diff --git a/channel-transport/transport-nutils/run.sh b/channel-transport/transport-nutils/run.sh index 4f07bdeab..1a01ed51f 100755 --- a/channel-transport/transport-nutils/run.sh +++ b/channel-transport/transport-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 transport.py "$@" diff --git a/elastic-tube-1d/fluid-python/run.sh b/elastic-tube-1d/fluid-python/run.sh index 3c05487b6..4cebb2088 100755 --- a/elastic-tube-1d/fluid-python/run.sh +++ b/elastic-tube-1d/fluid-python/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 ./FluidSolver.py ../precice-config.xml diff --git a/elastic-tube-1d/solid-python/run.sh b/elastic-tube-1d/solid-python/run.sh index 06a0c2aaa..73674234d 100755 --- a/elastic-tube-1d/solid-python/run.sh +++ b/elastic-tube-1d/solid-python/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 ./SolidSolver.py ../precice-config.xml diff --git a/elastic-tube-3d/solid-fenics/run.sh b/elastic-tube-3d/solid-fenics/run.sh index 5e22a5d84..fb2c0a0e9 100755 --- a/elastic-tube-3d/solid-fenics/run.sh +++ b/elastic-tube-3d/solid-fenics/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 solid.py diff --git a/flow-around-controlled-moving-cylinder/controller-fmi/run.sh b/flow-around-controlled-moving-cylinder/controller-fmi/run.sh index c5da7b900..4ec90931e 100755 --- a/flow-around-controlled-moving-cylinder/controller-fmi/run.sh +++ b/flow-around-controlled-moving-cylinder/controller-fmi/run.sh @@ -15,13 +15,14 @@ if [ ! -f PIDcontroller.fmu ]; then cd ../../ fi -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi fmiprecice ./fmi-settings.json ./precice-settings.json diff --git a/flow-around-controlled-moving-cylinder/solid-python/run.sh b/flow-around-controlled-moving-cylinder/solid-python/run.sh index 89027ba47..6f77463d6 100755 --- a/flow-around-controlled-moving-cylinder/solid-python/run.sh +++ b/flow-around-controlled-moving-cylinder/solid-python/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 solid.py ../precice-config.xml diff --git a/flow-over-heated-plate/fluid-su2/run.sh b/flow-over-heated-plate/fluid-su2/run.sh index 20d439355..8b276b60b 100755 --- a/flow-over-heated-plate/fluid-su2/run.sh +++ b/flow-over-heated-plate/fluid-su2/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi SU2_preCICE_CHT.py -f laminar_config_unsteady.cfg -r --parallel diff --git a/flow-over-heated-plate/solid-dunefem/run.sh b/flow-over-heated-plate/solid-dunefem/run.sh index 01049fa9e..87f9d6477 100755 --- a/flow-over-heated-plate/solid-dunefem/run.sh +++ b/flow-over-heated-plate/solid-dunefem/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 solid.py diff --git a/flow-over-heated-plate/solid-fenics/run.sh b/flow-over-heated-plate/solid-fenics/run.sh index 649dca1aa..1d0d38150 100755 --- a/flow-over-heated-plate/solid-fenics/run.sh +++ b/flow-over-heated-plate/solid-fenics/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 solid.py diff --git a/flow-over-heated-plate/solid-nutils/run.sh b/flow-over-heated-plate/solid-nutils/run.sh index 01049fa9e..87f9d6477 100755 --- a/flow-over-heated-plate/solid-nutils/run.sh +++ b/flow-over-heated-plate/solid-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 solid.py diff --git a/oscillator/mass-left-fmi/run.sh b/oscillator/mass-left-fmi/run.sh index 6a7699269..9a7704943 100755 --- a/oscillator/mass-left-fmi/run.sh +++ b/oscillator/mass-left-fmi/run.sh @@ -18,13 +18,14 @@ if [ ! -f Oscillator.fmu ]; then rm -r _solver-fmi-copy fi -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi fmiprecice fmi-settings.json precice-settings.json diff --git a/oscillator/mass-right-fmi/run.sh b/oscillator/mass-right-fmi/run.sh index 94294b07b..15b283e9e 100755 --- a/oscillator/mass-right-fmi/run.sh +++ b/oscillator/mass-right-fmi/run.sh @@ -18,13 +18,14 @@ if [ ! -f Oscillator.fmu ]; then rm -r _solver-fmi-copy fi -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi fmiprecice fmi-settings.json precice-settings.json diff --git a/partitioned-heat-conduction-direct/dirichlet-nutils/run.sh b/partitioned-heat-conduction-direct/dirichlet-nutils/run.sh index 01f0bd50a..8dd9c5c2a 100755 --- a/partitioned-heat-conduction-direct/dirichlet-nutils/run.sh +++ b/partitioned-heat-conduction-direct/dirichlet-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi rm -rf Dirichlet-*.vtk diff --git a/partitioned-heat-conduction-direct/neumann-nutils/run.sh b/partitioned-heat-conduction-direct/neumann-nutils/run.sh index 8fab51f89..b072f8bb4 100755 --- a/partitioned-heat-conduction-direct/neumann-nutils/run.sh +++ b/partitioned-heat-conduction-direct/neumann-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi rm -rf Neumann-*.vtk diff --git a/partitioned-heat-conduction/dirichlet-nutils/run.sh b/partitioned-heat-conduction/dirichlet-nutils/run.sh index 01f0bd50a..8dd9c5c2a 100755 --- a/partitioned-heat-conduction/dirichlet-nutils/run.sh +++ b/partitioned-heat-conduction/dirichlet-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi rm -rf Dirichlet-*.vtk diff --git a/partitioned-heat-conduction/neumann-nutils/run.sh b/partitioned-heat-conduction/neumann-nutils/run.sh index 8fab51f89..b072f8bb4 100755 --- a/partitioned-heat-conduction/neumann-nutils/run.sh +++ b/partitioned-heat-conduction/neumann-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi rm -rf Neumann-*.vtk diff --git a/partitioned-pipe-multiscale/fluid1d-left-nutils/run.sh b/partitioned-pipe-multiscale/fluid1d-left-nutils/run.sh index 74f5dff15..4e3e431f5 100755 --- a/partitioned-pipe-multiscale/fluid1d-left-nutils/run.sh +++ b/partitioned-pipe-multiscale/fluid1d-left-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi NUTILS_RICHOUTPUT=no python3 ../solver-fluid1d-nutils/Fluid1D.py side=Left diff --git a/partitioned-pipe-multiscale/fluid1d-right-nutils/run.sh b/partitioned-pipe-multiscale/fluid1d-right-nutils/run.sh index da8cfab83..72b0163b1 100755 --- a/partitioned-pipe-multiscale/fluid1d-right-nutils/run.sh +++ b/partitioned-pipe-multiscale/fluid1d-right-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi NUTILS_RICHOUTPUT=no python3 ../solver-fluid1d-nutils/Fluid1D.py side=Right diff --git a/perpendicular-flap/fluid-fake/run.sh b/perpendicular-flap/fluid-fake/run.sh index e33d1259b..3d09d256a 100755 --- a/perpendicular-flap/fluid-fake/run.sh +++ b/perpendicular-flap/fluid-fake/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 fake.py diff --git a/perpendicular-flap/fluid-nutils/run.sh b/perpendicular-flap/fluid-nutils/run.sh index aedb2e255..9d67e978a 100755 --- a/perpendicular-flap/fluid-nutils/run.sh +++ b/perpendicular-flap/fluid-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 fluid.py diff --git a/perpendicular-flap/fluid-su2/run.sh b/perpendicular-flap/fluid-su2/run.sh index 590a46e0f..93f800d97 100755 --- a/perpendicular-flap/fluid-su2/run.sh +++ b/perpendicular-flap/fluid-su2/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi SU2_preCICE_FSI.py -f euler_config_unsteady.cfg --parallel diff --git a/perpendicular-flap/solid-fake/run.sh b/perpendicular-flap/solid-fake/run.sh index e93deb140..2739404f2 100755 --- a/perpendicular-flap/solid-fake/run.sh +++ b/perpendicular-flap/solid-fake/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 fake.py diff --git a/perpendicular-flap/solid-fenics/run.sh b/perpendicular-flap/solid-fenics/run.sh index 649dca1aa..1d0d38150 100755 --- a/perpendicular-flap/solid-fenics/run.sh +++ b/perpendicular-flap/solid-fenics/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 solid.py diff --git a/perpendicular-flap/solid-nutils/run.sh b/perpendicular-flap/solid-nutils/run.sh index 4a74159a6..83e7257b8 100755 --- a/perpendicular-flap/solid-nutils/run.sh +++ b/perpendicular-flap/solid-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 solid.py richoutput=no diff --git a/resonant-circuit/capacitor-python/run.sh b/resonant-circuit/capacitor-python/run.sh index ee4ecc84a..4b9fa3ba5 100755 --- a/resonant-circuit/capacitor-python/run.sh +++ b/resonant-circuit/capacitor-python/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 capacitor.py diff --git a/resonant-circuit/coil-python/run.sh b/resonant-circuit/coil-python/run.sh index e816881c2..6c2a260f4 100755 --- a/resonant-circuit/coil-python/run.sh +++ b/resonant-circuit/coil-python/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 coil.py diff --git a/turek-hron-fsi3/fluid-nutils/run.sh b/turek-hron-fsi3/fluid-nutils/run.sh index 1c473f01f..6cc554f0f 100755 --- a/turek-hron-fsi3/fluid-nutils/run.sh +++ b/turek-hron-fsi3/fluid-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python fluid.py diff --git a/turek-hron-fsi3/solid-nutils/run.sh b/turek-hron-fsi3/solid-nutils/run.sh index c83b503d5..08ecc9a7f 100755 --- a/turek-hron-fsi3/solid-nutils/run.sh +++ b/turek-hron-fsi3/solid-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python solid.py diff --git a/two-scale-heat-conduction/macro-nutils/run.sh b/two-scale-heat-conduction/macro-nutils/run.sh index f6bd9f6b8..42703c0b0 100755 --- a/two-scale-heat-conduction/macro-nutils/run.sh +++ b/two-scale-heat-conduction/macro-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 macro.py richoutput=no diff --git a/two-scale-heat-conduction/micro-nutils/run.sh b/two-scale-heat-conduction/micro-nutils/run.sh index b0e7f8250..a7de2a099 100755 --- a/two-scale-heat-conduction/micro-nutils/run.sh +++ b/two-scale-heat-conduction/micro-nutils/run.sh @@ -6,13 +6,14 @@ exec > >(tee --append "$LOGFILE") 2>&1 usage() { echo "Usage: cmd [-s] [-p n]" 1>&2; exit 1; } -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi # Check if no input argument was provided diff --git a/volume-coupled-flow/source-nutils/run.sh b/volume-coupled-flow/source-nutils/run.sh index 084a325c7..3c2284a2e 100755 --- a/volume-coupled-flow/source-nutils/run.sh +++ b/volume-coupled-flow/source-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi python3 source.py diff --git a/water-hammer/fluid1d-left-nutils/run.sh b/water-hammer/fluid1d-left-nutils/run.sh index 74f5dff15..4e3e431f5 100755 --- a/water-hammer/fluid1d-left-nutils/run.sh +++ b/water-hammer/fluid1d-left-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi NUTILS_RICHOUTPUT=no python3 ../solver-fluid1d-nutils/Fluid1D.py side=Left diff --git a/water-hammer/fluid1d-right-nutils/run.sh b/water-hammer/fluid1d-right-nutils/run.sh index da8cfab83..72b0163b1 100755 --- a/water-hammer/fluid1d-right-nutils/run.sh +++ b/water-hammer/fluid1d-right-nutils/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r requirements.txt && pip freeze > pip-installed-packages.log fi NUTILS_RICHOUTPUT=no python3 ../solver-fluid1d-nutils/Fluid1D.py side=Right diff --git a/wolf-sheep-soil-creep/soil-creep-landlab/run.sh b/wolf-sheep-soil-creep/soil-creep-landlab/run.sh index 14753ac3d..6cfe51b1c 100755 --- a/wolf-sheep-soil-creep/soil-creep-landlab/run.sh +++ b/wolf-sheep-soil-creep/soil-creep-landlab/run.sh @@ -6,7 +6,6 @@ set -e -u exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then - if [ ! -d ".venv" ]; then python3 -m venv .venv source .venv/bin/activate @@ -14,7 +13,6 @@ if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then else source .venv/bin/activate fi - fi mkdir -p output diff --git a/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/run.sh b/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/run.sh index 43c20ae0d..3ff1a9459 100755 --- a/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/run.sh +++ b/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/run.sh @@ -6,7 +6,6 @@ set -e -u exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then - if [ ! -d ".venv" ]; then python3 -m venv .venv source .venv/bin/activate @@ -14,7 +13,6 @@ if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then else source .venv/bin/activate fi - fi python3 wolf_sheep_grass.py From effdac7c2623c919ecf896a9564a8da74cfaef3d Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Mon, 1 Jun 2026 23:31:03 +0200 Subject: [PATCH 65/94] Update the PR template --- .github/pull_request_template.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 4750058f0..5de16264c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,16 +1,15 @@ -## Description - -TODO +TODO: Summarize and motivate the changes, link to issues, remove the checklist entries that are not relevant. ## Checklist - [ ] I added a summary of any user-facing changes (compared to the last release) in the `changelog-entries/.md`. -- [ ] New tutorial case (e.g., new `fluid-openfoam` folder for existing tutorial)? Add it to the respective `README.md`. -- [ ] New tutorial? Update the website. - - Add a [sidebar entry](https://github.com/precice/precice.github.io/blob/master/_data/sidebars/tutorial_sidebar.yml) - - Add it to the [overview](https://github.com/precice/precice.github.io/blob/master/content/tutorials/tutorials.md) -## Resources +For new tutorials or tutorial cases: -- [Contributing tutorials](https://precice.org/community-contribute-to-precice.html#contributing-tutorials) -- [System tests documentation](https://precice.org/dev-docs-system-tests.html) \ No newline at end of file +- [ ] I followed the [tutorial folder structure](https://precice.org/community-contribute-to-precice.html#contributing-tutorials) +- [ ] I added/updated the tutorial `README.md` +- [ ] I added/updated the tutorial `metadata.yaml` +- [ ] I added tests in `tools/tests/tests.yaml` +- [ ] I submitted a pull request to the website with: + - A [sidebar entry](https://github.com/precice/precice.github.io/blob/master/_data/sidebars/tutorial_sidebar.yml) + - An entry in the [tutorials overview](https://github.com/precice/precice.github.io/blob/master/content/tutorials/tutorials.md) From 11cb2c94b064b1017c0e0cc2bf56eda50322851f Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Tue, 2 Jun 2026 21:14:46 +0200 Subject: [PATCH 66/94] Add default values for the scheduled system-tests-latest-components --- .github/workflows/system-tests-latest-components.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/system-tests-latest-components.yml b/.github/workflows/system-tests-latest-components.yml index 13abbc137..07e808213 100644 --- a/.github/workflows/system-tests-latest-components.yml +++ b/.github/workflows/system-tests-latest-components.yml @@ -172,7 +172,7 @@ jobs: needs: gather-refs uses: precice/tutorials/.github/workflows/run_testsuite_workflow.yml@develop with: - suites: ${{ inputs.suites }} + suites: ${{ inputs.suites || 'release_test' }} build_args: "PLATFORM:ubuntu_2404,\ PRECICE_REF:${{ needs.gather-refs.outputs.ref-precice }},\ PYTHON_BINDINGS_REF:${{ needs.gather-refs.outputs.ref-python-bindings }},\ @@ -183,5 +183,5 @@ jobs: OPENFOAM_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-openfoam-adapter }},\ SU2_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-su2-adapter }},\ TUTORIALS_REF:${{ needs.gather-refs.outputs.ref-tutorials }}" - system_tests_branch: ${{ inputs.system_tests_branch }} + system_tests_branch: ${{ inputs.system_tests_branch || 'develop' }} log_level: "INFO" From 150c75242fd386248c981b79185646e885212689 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Wed, 3 Jun 2026 12:00:35 +0200 Subject: [PATCH 67/94] Increase system tests GLOBAL_TIMEOUT to 10min --- tools/tests/systemtests/Systemtest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tests/systemtests/Systemtest.py b/tools/tests/systemtests/Systemtest.py index b666f6b1c..d50dc8c82 100644 --- a/tools/tests/systemtests/Systemtest.py +++ b/tools/tests/systemtests/Systemtest.py @@ -19,7 +19,7 @@ import os -GLOBAL_TIMEOUT = int(os.environ.get("PRECICE_SYSTEMTESTS_TIMEOUT", 300)) +GLOBAL_TIMEOUT = int(os.environ.get("PRECICE_SYSTEMTESTS_TIMEOUT", 600)) SHORT_TIMEOUT = 10 DIFF_RESULTS_DIR = "diff-results" From 5fe1c1fc0f2f3b6eab25ffe87817cc1d613dcaf5 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Fri, 5 Jun 2026 14:18:54 +0200 Subject: [PATCH 68/94] Update pull_request_template.md Add a reminder about `_config.yaml`. --- .github/pull_request_template.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 5de16264c..b5a665647 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -11,5 +11,6 @@ For new tutorials or tutorial cases: - [ ] I added/updated the tutorial `metadata.yaml` - [ ] I added tests in `tools/tests/tests.yaml` - [ ] I submitted a pull request to the website with: + - An entry in [`_config.yaml`](https://github.com/precice/precice.github.io/blob/master/_config.yml) - A [sidebar entry](https://github.com/precice/precice.github.io/blob/master/_data/sidebars/tutorial_sidebar.yml) - An entry in the [tutorials overview](https://github.com/precice/precice.github.io/blob/master/content/tutorials/tutorials.md) From 4456cf1a8f178313589645f78932660485a75fca Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Sat, 6 Jun 2026 11:28:19 +0200 Subject: [PATCH 69/94] Fix volume-coupled-flow test --- tools/tests/dockerfiles/ubuntu_2404/Dockerfile | 5 +++-- volume-coupled-flow/fluid-openfoam/run.sh | 2 +- volume-coupled-flow/metadata.yaml | 2 +- volume-coupled-flow/source-nutils/run.sh | 8 ++++++-- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/tools/tests/dockerfiles/ubuntu_2404/Dockerfile b/tools/tests/dockerfiles/ubuntu_2404/Dockerfile index c386c9ab9..df926fd33 100644 --- a/tools/tests/dockerfiles/ubuntu_2404/Dockerfile +++ b/tools/tests/dockerfiles/ubuntu_2404/Dockerfile @@ -38,7 +38,7 @@ USER precice FROM base_image AS precice_dependecies USER root -# Installing necessary dependecies for preCICE +# Dependencies for preCICE and common dependencies for the tutorials/tests RUN apt-get -qq update && \ apt-get -qq -y install \ build-essential \ @@ -61,7 +61,8 @@ RUN apt-get -qq update && \ python3-pip \ python3-venv \ pkg-config \ - wget + wget \ + inotify-tools USER precice FROM precice_dependecies AS precice diff --git a/volume-coupled-flow/fluid-openfoam/run.sh b/volume-coupled-flow/fluid-openfoam/run.sh index 93416a154..5e97cbce6 100755 --- a/volume-coupled-flow/fluid-openfoam/run.sh +++ b/volume-coupled-flow/fluid-openfoam/run.sh @@ -14,4 +14,4 @@ close_log # Necessary signal file for the system tests, # to keep the Source container running till the end -touch ../fluid-participant-finished.log +date > ../fluid-participant-finished.log diff --git a/volume-coupled-flow/metadata.yaml b/volume-coupled-flow/metadata.yaml index 959340385..d6234cf05 100644 --- a/volume-coupled-flow/metadata.yaml +++ b/volume-coupled-flow/metadata.yaml @@ -16,5 +16,5 @@ cases: source-nutils: participant: Source directory: ./source-nutils - run: ./run.sh + run: PRECICE_TUTORIALS_TESTING="TRUE" ./run.sh component: nutils-adapter diff --git a/volume-coupled-flow/source-nutils/run.sh b/volume-coupled-flow/source-nutils/run.sh index 3c2284a2e..5d768edd7 100755 --- a/volume-coupled-flow/source-nutils/run.sh +++ b/volume-coupled-flow/source-nutils/run.sh @@ -17,7 +17,11 @@ fi python3 source.py # System tests: Keep the container and the respective network alive till the end. -echo "Waiting for the Fluid participant to finish..." -inotifywait -e create,attrib -qq ../fluid-participant-finished.log +if [[ -v PRECICE_TUTORIALS_TESTING ]]; then + echo "Waiting for the Fluid participant to finish..." + if [ ! -f "../fluid-participant-finished.log" ]; then + inotifywait -e create,modify,attrib --include '/fluid-participant-finished\.log$' -qq .. + fi +fi close_log From 015c03f1a09793e4f69f363cac0c18f50fe82dbb Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Sun, 7 Jun 2026 20:15:54 +0200 Subject: [PATCH 70/94] Add a basic precice test suite (and rename release_test) (#828) --- .../system-tests-latest-components.yml | 2 +- .github/workflows/system-tests-pr.yml | 2 +- tools/tests/README.md | 2 +- tools/tests/tests.yaml | 18 +++++++++++++++--- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/system-tests-latest-components.yml b/.github/workflows/system-tests-latest-components.yml index 07e808213..2e2da3edd 100644 --- a/.github/workflows/system-tests-latest-components.yml +++ b/.github/workflows/system-tests-latest-components.yml @@ -8,7 +8,7 @@ on: inputs: suites: description: 'Test suites to execute (comma-separated)' - default: 'release_test' + default: 'release' required: true type: string system_tests_branch: diff --git a/.github/workflows/system-tests-pr.yml b/.github/workflows/system-tests-pr.yml index f59b6fc70..27361db36 100644 --- a/.github/workflows/system-tests-pr.yml +++ b/.github/workflows/system-tests-pr.yml @@ -150,7 +150,7 @@ jobs: needs: gather-refs uses: precice/tutorials/.github/workflows/run_testsuite_workflow.yml@develop with: - suites: release_test + suites: release build_args: "PLATFORM:ubuntu_2404,\ PRECICE_REF:${{ needs.gather-refs.outputs.ref-precice }},\ PYTHON_BINDINGS_REF:${{ needs.gather-refs.outputs.ref-python-bindings }},\ diff --git a/tools/tests/README.md b/tools/tests/README.md index 83a71910a..607f7b3dc 100644 --- a/tools/tests/README.md +++ b/tools/tests/README.md @@ -25,7 +25,7 @@ Workflow for the preCICE v3 release testing: 3. Trigger the GitHub Actions Workflow. Until we merge the workflow to develop, this can only happen via the [GitHub CLI](https://cli.github.com/): ```bash - gh workflow run run_testsuite_manual.yml -f suites=release_test -f build_args="PRECICE_REF:v3.1.1,PRECICE_PRESET:production-audit,OPENFOAM_ADAPTER_REF:v1.3.0,PYTHON_BINDINGS_REF:v3.1.0,FENICS_ADAPTER_REF:v2.1.0,SU2_VERSION:7.5.1,SU2_ADAPTER_REF:64d4aff,DEALII_ADAPTER_REF:02c5d18,TUTORIALS_REF:340b447" --ref=develop + gh workflow run run_testsuite_manual.yml -f suites=release -f build_args="PRECICE_REF:v3.1.1,PRECICE_PRESET:production-audit,OPENFOAM_ADAPTER_REF:v1.3.0,PYTHON_BINDINGS_REF:v3.1.0,FENICS_ADAPTER_REF:v2.1.0,SU2_VERSION:7.5.1,SU2_ADAPTER_REF:64d4aff,DEALII_ADAPTER_REF:02c5d18,TUTORIALS_REF:340b447" --ref=develop ``` 4. Go to the tutorials [Actions](https://github.com/precice/tutorials/actions) page and find the running workflow diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index 1a497d904..ffcec4ffa 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -1,6 +1,6 @@ # Test suites that can be triggered by the system tests. # Define test suites per tutorial in the beginning of the file, -# then refer to these in the release_test and in the individual test +# then refer to these in the release test suite and in the individual test # suites for repositories, at the lower part of the file. # # Conventions: @@ -500,7 +500,7 @@ test_suites: ##################################################################### ## Test suites referring to the test suites defined above - release_test: + release: tutorials: - *breaking-dam-2d_fluid-openfoam_solid-calculix - *channel-transport_fluid-openfoam_transport-nutils @@ -551,7 +551,7 @@ test_suites: - *water-hammer_fluid1d-left-nutils_fluid3d-right-openfoam - *wolf-sheep-soil-creep_soil-creep-landlab_wolf-sheep-grass-mesa - # These test suites take longer to run. They are available, but not regularly executed. + # These tests take longer to run. They are available, but not regularly executed. extra: tutorials: - *channel-transport_fluid-nutils_transport-nutils @@ -564,6 +564,18 @@ test_suites: - *elastic-tube-3d_fluid-openfoam_solid-fenics # too small values to compare - *two-scale-heat-conduction_macro-dumux_micro-dumux # too small values to compare + # A selection of tests that cover a wide range of main features, meant for quicker CI executions + precice: + tutorials: + - *quickstart_openfoam_cpp # serial-explicit, RBF + - *channel-transport_fluid-openfoam_transport-nutils # parallel-explicit, RBF, python bindings + - *flow-over-heated-plate_fluid-openfoam_solid-openfoam # serial-implicit (Aitken), nearest-neighbor + - *flow-over-heated-plate-nearest-projection_fluid-openfoam_solid-openfoam # serial-implicit (IQN-ILS), nearest-projection + - *elastic-tube-1d_fluid-cpp_solid-cpp # serial-implicit (IQN-ILS), nearest-neighbor + - *partitioned-pipe-multiscale_fluid1d-left-nutils_fluid3d-right-openfoam # parallel-implicit (IQN-ILS), axial-geometric-multiscale + - *elastic-tube-1d_fluid-fortran-module_solid-fortran-module # integration with Fortran (module) + - *perpendicular-flap_fluid-openfoam_solid-calculix # integration with C + calculix-adapter: tutorials: - *breaking-dam-2d_fluid-openfoam_solid-calculix From afda11111dac87e889000c2bd4c2b2b283486d40 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Mon, 8 Jun 2026 08:50:12 +0200 Subject: [PATCH 71/94] Rename remaining release_test to release --- .github/workflows/generate_reference_results_manual.yml | 2 +- .github/workflows/run_testsuite_manual.yml | 2 +- .github/workflows/run_testsuite_workflow.yml | 2 +- .github/workflows/system-tests-latest-components.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/generate_reference_results_manual.yml b/.github/workflows/generate_reference_results_manual.yml index a2fcfd706..1d0d52d08 100644 --- a/.github/workflows/generate_reference_results_manual.yml +++ b/.github/workflows/generate_reference_results_manual.yml @@ -4,7 +4,7 @@ on: inputs: suites: description: 'Test suites to execute (comma-separated, see tests.yaml)' - default: 'release_test' + default: 'release' required: true type: string from_ref: diff --git a/.github/workflows/run_testsuite_manual.yml b/.github/workflows/run_testsuite_manual.yml index 2eb4521ca..25c4e820e 100644 --- a/.github/workflows/run_testsuite_manual.yml +++ b/.github/workflows/run_testsuite_manual.yml @@ -5,7 +5,7 @@ on: suites: description: 'Test suites to execute (comma-separated)' required: true - default: 'release_test' + default: 'release' type: string build_args: description: 'Build arguments (override component defaults)' diff --git a/.github/workflows/run_testsuite_workflow.yml b/.github/workflows/run_testsuite_workflow.yml index f93b32e8b..aa1d6f9f7 100644 --- a/.github/workflows/run_testsuite_workflow.yml +++ b/.github/workflows/run_testsuite_workflow.yml @@ -4,7 +4,7 @@ on: inputs: suites: description: 'Test suites to execute (comma-separated)' - default: 'release_test' + default: 'release' type: string build_args: description: 'Build arguments' diff --git a/.github/workflows/system-tests-latest-components.yml b/.github/workflows/system-tests-latest-components.yml index 2e2da3edd..a6c7b1fe1 100644 --- a/.github/workflows/system-tests-latest-components.yml +++ b/.github/workflows/system-tests-latest-components.yml @@ -172,7 +172,7 @@ jobs: needs: gather-refs uses: precice/tutorials/.github/workflows/run_testsuite_workflow.yml@develop with: - suites: ${{ inputs.suites || 'release_test' }} + suites: ${{ inputs.suites || 'release' }} build_args: "PLATFORM:ubuntu_2404,\ PRECICE_REF:${{ needs.gather-refs.outputs.ref-precice }},\ PYTHON_BINDINGS_REF:${{ needs.gather-refs.outputs.ref-python-bindings }},\ From daa95e0ab612ef542fef6f1e7db17f710bf93672 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Tue, 9 Jun 2026 09:52:11 +0200 Subject: [PATCH 72/94] Fix run.sh of paritioned-pipe solicliquidfoam cases --- partitioned-pipe/fluid1-openfoam-sonicliquidfoam/run.sh | 2 +- partitioned-pipe/fluid2-openfoam-sonicliquidfoam/run.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/partitioned-pipe/fluid1-openfoam-sonicliquidfoam/run.sh b/partitioned-pipe/fluid1-openfoam-sonicliquidfoam/run.sh index 883849233..86fbb5e22 100755 --- a/partitioned-pipe/fluid1-openfoam-sonicliquidfoam/run.sh +++ b/partitioned-pipe/fluid1-openfoam-sonicliquidfoam/run.sh @@ -8,7 +8,7 @@ exec > >(tee --append "$LOGFILE") 2>&1 # That improves the results for the compressible case, so we un-comment it. if [ ! -f ../precice-config.xml.orig ]; then echo "Modifying the ../precice-config.xml to enable PressureGradient (see precice-config.xml.orig for the original)" - cp -r ../precice-config.xml ../precice-config.xml.orig + cp -f ../precice-config.xml ../precice-config.xml.orig sed -i "s,,,g" ../precice-config.xml fi diff --git a/partitioned-pipe/fluid2-openfoam-sonicliquidfoam/run.sh b/partitioned-pipe/fluid2-openfoam-sonicliquidfoam/run.sh index 883849233..86fbb5e22 100755 --- a/partitioned-pipe/fluid2-openfoam-sonicliquidfoam/run.sh +++ b/partitioned-pipe/fluid2-openfoam-sonicliquidfoam/run.sh @@ -8,7 +8,7 @@ exec > >(tee --append "$LOGFILE") 2>&1 # That improves the results for the compressible case, so we un-comment it. if [ ! -f ../precice-config.xml.orig ]; then echo "Modifying the ../precice-config.xml to enable PressureGradient (see precice-config.xml.orig for the original)" - cp -r ../precice-config.xml ../precice-config.xml.orig + cp -f ../precice-config.xml ../precice-config.xml.orig sed -i "s,,,g" ../precice-config.xml fi From faca2eed7c78700cf9c70a37bbbb5864154ebb0e Mon Sep 17 00:00:00 2001 From: Pranjal Date: Wed, 10 Jun 2026 17:20:26 +0530 Subject: [PATCH 73/94] Split the system tests log file into stages (#801) --- .../generate_reference_results_workflow.yml | 7 +- .github/workflows/run_testsuite_workflow.yml | 7 +- changelog-entries/801.md | 1 + tools/tests/README.md | 4 +- tools/tests/systemtests.py | 85 ++++- tools/tests/systemtests/Systemtest.py | 300 +++++++++++------- 6 files changed, 256 insertions(+), 148 deletions(-) create mode 100644 changelog-entries/801.md diff --git a/.github/workflows/generate_reference_results_workflow.yml b/.github/workflows/generate_reference_results_workflow.yml index 4665d8e2a..2a61bb7cc 100644 --- a/.github/workflows/generate_reference_results_workflow.yml +++ b/.github/workflows/generate_reference_results_workflow.yml @@ -92,14 +92,15 @@ jobs: fi git commit -m "${{inputs.commit_msg}}" git push - - name: Archive main log files + - name: Archive system test logs if: ${{ always() }} uses: actions/upload-artifact@v7 with: name: system_tests_run_${{ github.run_id }}_${{ github.run_attempt }}_reference_logs path: | - runs/*/system-tests-stdout.log - runs/*/system-tests-stderr.log + runs/*/system-tests-build.log + runs/*/system-tests-run.log + runs/*/system-tests-compare.log runs/*/*/system-tests_*.log - name: Archive run files if: failure() diff --git a/.github/workflows/run_testsuite_workflow.yml b/.github/workflows/run_testsuite_workflow.yml index aa1d6f9f7..0ce67cd63 100644 --- a/.github/workflows/run_testsuite_workflow.yml +++ b/.github/workflows/run_testsuite_workflow.yml @@ -76,14 +76,15 @@ jobs: cd tools/tests python systemtests.py --build_args=${{ inputs.build_args}} --suites=${{ inputs.suites}} --log-level=${{ inputs.log_level}} cd ../../ - - name: Archive main log files + - name: Archive system test logs if: ${{ always() }} uses: actions/upload-artifact@v7 with: name: system_tests_run_${{ github.run_id }}_${{ github.run_attempt }}_logs path: | - runs/*/system-tests-stdout.log - runs/*/system-tests-stderr.log + runs/*/system-tests-build.log + runs/*/system-tests-run.log + runs/*/system-tests-compare.log runs/*/*/system-tests_*.log - name: Archive run files if: ${{ failure() || inputs.upload_artifacts == 'TRUE' }} diff --git a/changelog-entries/801.md b/changelog-entries/801.md new file mode 100644 index 000000000..745806f4a --- /dev/null +++ b/changelog-entries/801.md @@ -0,0 +1 @@ +- System tests now write logs during each run (build, run, compare), including `system-tests-build.log`, `system-tests-run.log`, and `system-tests-compare.log` [#801](https://github.com/precice/tutorials/pull/801). diff --git a/tools/tests/README.md b/tools/tests/README.md index 607f7b3dc..374731d92 100644 --- a/tools/tests/README.md +++ b/tools/tests/README.md @@ -36,7 +36,7 @@ Workflow for the preCICE v3 release testing: 6. Download the build artifacts from Summary > runs. - - In there, you may want to check the `system-tests-stdout.log` and `system-tests-stderr.log` files. + - In there, inspect the per-stage logs (`system-tests-build.log`, `system-tests-run.log`, `system-tests-compare.log`). - The produced results are in `precice-exports/`, the reference results in `reference-results-unpacked`. - Compare using, e.g., ParaView or [fieldcompare](https://gitlab.com/dglaeser/fieldcompare): `fieldcompare dir precice-exports/ reference/`. The `--diff` option will give you `precice-exports/diff_*.vtu` files, while you can also try different tolerances with `-rtol` and `-atol`. @@ -105,7 +105,7 @@ In this case, building and running seems to work out, but the tests fail because The easiest way to debug a systemtest run is first to have a look at the output written into the action on GitHub. If this does not provide enough hints, the next step is to download the generated `system_tests_run__` artifact. Note that by default this will only be generated if the systemtests fail. -Inside the archive, a test-specific subfolder like `flow-over-heated-plate_fluid-openfoam-solid-fenics_2023-11-19-211723` contains two log files: `system-tests-stderr.log` and `system-tests-stdout.log`. This can be a starting point for a further investigation. When fieldcompare runs with `--diff`, it writes VTK (.vtu) diff files under `precice-exports/`; if the comparison fails, those files are copied into a `diff-results/` subfolder in the same run directory (mirroring any subpaths under `precice-exports/`) so you can open them (e.g. in ParaView) to see where results differ from the reference. On successful comparisons, `diff-results/` is therefore absent. +Inside the archive, a test-specific subfolder like `flow-over-heated-plate_fluid-openfoam-solid-fenics_2023-11-19-211723` contains per-stage logs (`system-tests-build.log`, `system-tests-run.log`, `system-tests-compare.log`). These are a good starting point for further investigation. When fieldcompare runs with `--diff`, it writes VTK (.vtu) diff files under `precice-exports/`; if the comparison fails, those files are copied into a `diff-results/` subfolder in the same run directory (mirroring any subpaths under `precice-exports/`) so you can open them (e.g. in ParaView) to see where results differ from the reference. On successful comparisons, `diff-results/` is therefore absent. ## Adding new tests diff --git a/tools/tests/systemtests.py b/tools/tests/systemtests.py index 6debe18d0..33771cbc6 100644 --- a/tools/tests/systemtests.py +++ b/tools/tests/systemtests.py @@ -7,9 +7,20 @@ from metadata_parser.metdata import Tutorials, Case import logging import time +import os +import sys from paths import PRECICE_TUTORIAL_DIR, PRECICE_TESTS_RUN_DIR, PRECICE_TESTS_DIR +class _ConsoleLogFormatter(logging.Formatter): + """Omit level prefix for INFO/DEBUG; keep it for warnings and errors.""" + + def format(self, record: logging.LogRecord) -> str: + if record.levelno >= logging.WARNING: + return f"{record.levelname}: {record.getMessage()}" + return record.getMessage() + + def main(): parser = argparse.ArgumentParser(description='systemtest') @@ -29,12 +40,30 @@ def main(): # Parse the command-line arguments args = parser.parse_args() - # Configure logging based on the provided log level - logging.basicConfig(level=args.log_level, format='%(levelname)s: %(message)s') + # Configure logging + handler = logging.StreamHandler() + handler.setFormatter(_ConsoleLogFormatter()) + logging.basicConfig(level=args.log_level, handlers=[handler]) + + gh_actions = os.environ.get("GITHUB_ACTIONS", "").lower() == "true" + # Skip ANSI colors when TERM is unset or "dumb" (minimal terminal, common in CI). + ansi_colors = sys.stdout.isatty() and os.environ.get("TERM", "") not in {"", "dumb"} + + def _style(text: str, color_code: int | None) -> str: + if not ansi_colors or color_code is None: + return text + return f"\x1b[{color_code}m{text}\x1b[0m" + + def _group_start(title: str) -> None: + if gh_actions: + print(f"::group::{title}", flush=True) - print(f"Using log-level: {args.log_level}") + def _group_end() -> None: + if gh_actions: + print("::endgroup::", flush=True) systemtests_to_run = [] + test_suites_to_execute = [] available_tutorials = Tutorials.from_path(PRECICE_TUTORIAL_DIR) build_args = SystemtestArguments.from_args(args.build_args) @@ -43,7 +72,6 @@ def main(): test_suites_requested = args.suites.split(',') available_testsuites = TestSuites.from_yaml( PRECICE_TESTS_DIR / "tests.yaml", available_tutorials) - test_suites_to_execute = [] for test_suite_requested in test_suites_requested: test_suite_found = available_testsuites.get_by_name( test_suite_requested) @@ -72,24 +100,47 @@ def main(): if not systemtests_to_run: raise RuntimeError("Did not find any Systemtests to execute.") - logging.info(f"About to run the following systemtest in the directory {run_directory}:\n {systemtests_to_run}") + total = len(systemtests_to_run) + + if test_suites_to_execute: + print("Selected test suite(s):", flush=True) + print(flush=True) + for test_suite in test_suites_to_execute: + print(f"- {test_suite.name}", flush=True) + print(flush=True) + + print(f"About to run {total} test(s) in the directory {run_directory}:", flush=True) + print(flush=True) + for number, systemtest in enumerate(systemtests_to_run, start=1): + print(f"{number}. {systemtest}", flush=True) + print(flush=True) + print(f"Using log-level: {args.log_level}", flush=True) results = [] for number, systemtest in enumerate(systemtests_to_run, start=1): - logging.info(f"Started running {systemtest}, {number}/{len(systemtests_to_run)}") - t = time.perf_counter() - result = systemtest.run(run_directory) - elapsed_time = time.perf_counter() - t - logging.info(f"Running {systemtest} took {elapsed_time:^.1f} seconds") + print(flush=True) + started_header = f"[{number}/{total}] Started {systemtest}" + _group_start(started_header) + try: + if not gh_actions: + logging.info(started_header) + t = time.perf_counter() + result = systemtest.run(run_directory) + elapsed_time = time.perf_counter() - t + + if result.success: + status_label = _style("✅ PASS", 32) + else: + status_label = _style("❌ FAIL", 31) + finally: + _group_end() + + print(f"{status_label} Finished in {elapsed_time:.1f}s", flush=True) + print(flush=True) results.append(result) - system_test_success = True - for result in results: - if not result.success: - logging.error(f"Failed to run {result.systemtest}") - system_test_success = False - else: - logging.info(f"Success running {result.systemtest}") + print(flush=True) + system_test_success = all(result.success for result in results) display_systemtestresults_as_table(results) if system_test_success: diff --git a/tools/tests/systemtests/Systemtest.py b/tools/tests/systemtests/Systemtest.py index d50dc8c82..14ec8a97d 100644 --- a/tools/tests/systemtests/Systemtest.py +++ b/tools/tests/systemtests/Systemtest.py @@ -1,4 +1,5 @@ import subprocess +import threading from typing import List, Dict, Optional, Tuple from jinja2 import Environment, FileSystemLoader from dataclasses import dataclass, field @@ -24,6 +25,36 @@ DIFF_RESULTS_DIR = "diff-results" +STAGE_LOG_FILES = { + "build": "system-tests-build.log", + "run": "system-tests-run.log", + "compare": "system-tests-compare.log", +} + + +class _SystemtestLogSink: + """Writes subprocess output incrementally to per-stage log files.""" + + def __init__(self, system_test_dir: Path): + self._system_test_dir = system_test_dir + self._lock = threading.Lock() + + def begin_stage(self, stage: str) -> None: + stage_path = self._system_test_dir / STAGE_LOG_FILES[stage] + stage_path.write_text(f"=== {stage} ===\n", encoding="utf-8") + + def append_stdout(self, line: str, stage: str) -> None: + with self._lock: + stage_path = self._system_test_dir / STAGE_LOG_FILES[stage] + with stage_path.open("a", encoding="utf-8") as log_file: + log_file.write(line + "\n") + + def append_stderr(self, line: str, stage: str) -> None: + with self._lock: + stage_path = self._system_test_dir / STAGE_LOG_FILES[stage] + with stage_path.open("a", encoding="utf-8") as log_file: + log_file.write(f"[stderr] {line}\n") + def slugify(value, allow_unicode=False): """ @@ -76,17 +107,23 @@ class SystemtestResult: fieldcompare_time: float # in seconds +def _success_status_symbol(success: bool) -> str: + return "✅" if success else "❌" + + def display_systemtestresults_as_table(results: List[SystemtestResult]): """ Prints the result in a nice tabluated way to get an easy overview """ + print() + def _get_length_of_name(results: List[SystemtestResult]) -> int: return max(len(str(result.systemtest)) for result in results) max_name_length = _get_length_of_name(results) header = f"| {'systemtest':<{max_name_length + 2}} "\ - f"| {'success':^7} "\ + f"| {'status':^7} "\ f"| {'building time [s]':^17} "\ f"| {'solver time [s]':^15} "\ f"| {'fieldcompare time [s]':^21} |" @@ -105,7 +142,7 @@ def _get_length_of_name(results: List[SystemtestResult]) -> int: for result in results: row = f"| {str(result.systemtest):<{max_name_length + 2}} "\ - f"| {result.success:^7} "\ + f"| {_success_status_symbol(result.success):^7} "\ f"| {result.build_time:^17.1f} "\ f"| {result.solver_time:^15.1f} "\ f"| {result.fieldcompare_time:^21.1f} |" @@ -119,7 +156,7 @@ def _get_length_of_name(results: List[SystemtestResult]) -> int: with open(os.environ["GITHUB_STEP_SUMMARY"], "a") as f: print("\n\n", file=f) print( - "In case a test fails, download the archive from the bottom of this page and look into each `system-tests-stdout.log` and `system-tests-stderr.log`. The time spent in each step might already give useful hints.", + "In case a test fails, download the archive from the bottom of this page and inspect the per-stage logs (`system-tests-build.log`, `system-tests-run.log`, `system-tests-compare.log`). The stage runtimes might already give useful hints.", file=f) print( "See the [documentation](https://precice.org/dev-docs-system-tests.html#understanding-what-went-wrong).", @@ -399,6 +436,94 @@ def __unpack_reference_results(self) -> Tuple[bool, str]: logging.error(error_message) return False, error_message + def __init_run_logs(self) -> None: + self._log_sink = _SystemtestLogSink(self.system_test_dir) + + def _run_docker_compose_subprocess( + self, + command: List[str], + stage: str, + timeout: int, + ) -> Tuple[int, List[str], List[str]]: + """ + Run a docker compose command, streaming stdout/stderr to log files as they arrive. + """ + stdout_data: List[str] = [] + stderr_data: List[str] = [] + log_sink = getattr(self, "_log_sink", None) + if log_sink is not None: + log_sink.begin_stage(stage) + logging.info(f"Docker compose {stage} for {self}") + + try: + process = subprocess.Popen( + command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + bufsize=1, + start_new_session=True, + cwd=self.system_test_dir, + ) + except Exception as e: + logging.critical(f"Error starting docker compose {stage} command: {e}") + return 1, stdout_data, stderr_data + + def read_stream(stream, is_stderr: bool) -> None: + if stream is None: + return + for line in stream: + line = line.rstrip("\n\r") + if is_stderr: + stderr_data.append(line) + if log_sink is not None: + log_sink.append_stderr(line, stage) + else: + stdout_data.append(line) + if log_sink is not None: + log_sink.append_stdout(line, stage) + stream.close() + + stdout_thread = threading.Thread( + target=read_stream, args=(process.stdout, False), daemon=True) + stderr_thread = threading.Thread( + target=read_stream, args=(process.stderr, True), daemon=True) + stdout_thread.start() + stderr_thread.start() + + try: + exit_code = process.wait(timeout=timeout) + except KeyboardInterrupt as k: + process.kill() + stdout_thread.join(timeout=SHORT_TIMEOUT) + stderr_thread.join(timeout=SHORT_TIMEOUT) + raise KeyboardInterrupt from k + except subprocess.TimeoutExpired: + logging.critical( + f"Systemtest {self} timed out during docker compose {stage} " + f"after {timeout}s. Killing the process.") + process.kill() + try: + process.wait(timeout=SHORT_TIMEOUT) + except subprocess.TimeoutExpired: + pass + exit_code = process.returncode if process.returncode is not None else 1 + except Exception as e: + logging.critical( + f"Systemtest {self} had serious issues during docker compose {stage}: {e}") + process.kill() + try: + process.wait(timeout=SHORT_TIMEOUT) + except subprocess.TimeoutExpired: + pass + exit_code = process.returncode if process.returncode is not None else 1 + + stdout_thread.join(timeout=SHORT_TIMEOUT) + stderr_thread.join(timeout=SHORT_TIMEOUT) + if exit_code is None: + exit_code = process.poll() or 1 + return exit_code, stdout_data, stderr_data + def _cleanup_docker_networks(self): """ Prunes the unused Docker networks, since there is an upper limit on the number of custom networks defined. @@ -439,47 +564,31 @@ def _run_field_compare(self): time_start = time.perf_counter() unpack_success, unpack_error_message = self.__unpack_reference_results() if not unpack_success: + log_sink = getattr(self, "_log_sink", None) + if log_sink is not None: + log_sink.begin_stage("compare") + log_sink.append_stderr(unpack_error_message, "compare") elapsed_time = time.perf_counter() - time_start return FieldCompareResult(1, [], [unpack_error_message], self, elapsed_time) docker_compose_content = self.__get_field_compare_compose_file() - stdout_data = [] - stderr_data = [] with open(self.system_test_dir / "docker-compose.field_compare.yaml", 'w') as file: file.write(docker_compose_content) - try: - # Execute docker-compose command - process = subprocess.Popen(['docker', - 'compose', - '--file', - 'docker-compose.field_compare.yaml', - 'up', - '--exit-code-from', - 'field-compare'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - start_new_session=True, - cwd=self.system_test_dir) - - try: - stdout, stderr = process.communicate(timeout=self.timeout) - except KeyboardInterrupt as k: - process.kill() - raise KeyboardInterrupt from k - except Exception as e: - logging.critical( - f"Systemtest {self} had serious issues executing the docker compose command about to kill the docker compose command. Please check the logs! {e}") - process.kill() - process.communicate(timeout=SHORT_TIMEOUT) - stdout_data.extend(stdout.decode().splitlines()) - stderr_data.extend(stderr.decode().splitlines()) - process.poll() - elapsed_time = time.perf_counter() - time_start - return FieldCompareResult(process.returncode, stdout_data, stderr_data, self, elapsed_time) - except Exception as e: - logging.CRITICAL("Error executing docker compose command:", e) - elapsed_time = time.perf_counter() - time_start - return FieldCompareResult(1, stdout_data, stderr_data, self, elapsed_time) + exit_code, stdout_data, stderr_data = self._run_docker_compose_subprocess( + [ + 'docker', + 'compose', + '--file', + 'docker-compose.field_compare.yaml', + 'up', + '--exit-code-from', + 'field-compare', + ], + "compare", + self.timeout, + ) + elapsed_time = time.perf_counter() - time_start + return FieldCompareResult(exit_code, stdout_data, stderr_data, self, elapsed_time) def __archive_fieldcompare_diffs(self) -> None: """ @@ -531,41 +640,20 @@ def _build_docker(self): with open(self.system_test_dir / "docker-compose.tutorial.yaml", 'w') as file: file.write(docker_compose_content) - stdout_data = [] - stderr_data = [] - - try: - # Execute docker-compose command - process = subprocess.Popen(['docker', - 'compose', - '--file', - 'docker-compose.tutorial.yaml', - 'build'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - start_new_session=True, - cwd=self.system_test_dir) - - try: - stdout, stderr = process.communicate(timeout=GLOBAL_TIMEOUT) - except KeyboardInterrupt as k: - process.kill() - # process.send_signal(9) - raise KeyboardInterrupt from k - except Exception as e: - logging.critical( - f"systemtest {self} had serious issues building the docker images via the `docker compose build` command. About to kill the docker compose command. Please check the logs! {e}") - process.communicate(timeout=SHORT_TIMEOUT) - process.kill() - - stdout_data.extend(stdout.decode().splitlines()) - stderr_data.extend(stderr.decode().splitlines()) - elapsed_time = time.perf_counter() - time_start - return DockerComposeResult(process.returncode, stdout_data, stderr_data, self, elapsed_time) - except Exception as e: - logging.critical(f"Error executing docker compose build command: {e}") - elapsed_time = time.perf_counter() - time_start - return DockerComposeResult(1, stdout_data, stderr_data, self, elapsed_time) + exit_code, stdout_data, stderr_data = self._run_docker_compose_subprocess( + [ + 'docker', + 'compose', + '--progress=plain', + '--file', + 'docker-compose.tutorial.yaml', + 'build', + ], + "build", + GLOBAL_TIMEOUT, + ) + elapsed_time = time.perf_counter() - time_start + return DockerComposeResult(exit_code, stdout_data, stderr_data, self, elapsed_time) def _run_tutorial(self): """ @@ -576,51 +664,23 @@ def _run_tutorial(self): """ logging.debug(f"Running tutorial {self}") time_start = time.perf_counter() - stdout_data = [] - stderr_data = [] - try: - # Execute docker-compose command - process = subprocess.Popen(['docker', - 'compose', - '--file', - 'docker-compose.tutorial.yaml', - 'up'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - start_new_session=True, - cwd=self.system_test_dir) - - try: - stdout, stderr = process.communicate(timeout=self.timeout) - except KeyboardInterrupt as k: - process.kill() - # process.send_signal(9) - raise KeyboardInterrupt from k - except Exception as e: - logging.critical( - f"Systemtest {self} had serious issues executing the docker compose command about to kill the docker compose command. Please check the logs! {e}") - process.kill() - stdout, stderr = process.communicate(timeout=SHORT_TIMEOUT) - process.kill() - - stdout_data.extend(stdout.decode().splitlines()) - stderr_data.extend(stderr.decode().splitlines()) - elapsed_time = time.perf_counter() - time_start - return DockerComposeResult(process.returncode, stdout_data, stderr_data, self, elapsed_time) - except Exception as e: - logging.critical(f"Error executing docker compose up command: {e}") - elapsed_time = time.perf_counter() - time_start - return DockerComposeResult(1, stdout_data, stderr_data, self, elapsed_time) + exit_code, stdout_data, stderr_data = self._run_docker_compose_subprocess( + [ + 'docker', + 'compose', + '--file', + 'docker-compose.tutorial.yaml', + 'up', + ], + "run", + self.timeout, + ) + elapsed_time = time.perf_counter() - time_start + return DockerComposeResult(exit_code, stdout_data, stderr_data, self, elapsed_time) def __repr__(self): return f"{self.tutorial.name} {self.case_combination}" - def __write_logs(self, stdout_data: List[str], stderr_data: List[str]): - with open(self.system_test_dir / "system-tests-stdout.log", 'w') as stdout_file: - stdout_file.write("\n".join(stdout_data)) - with open(self.system_test_dir / "system-tests-stderr.log", 'w') as stderr_file: - stderr_file.write("\n".join(stderr_data)) - def __apply_max_time_override(self): """Overwrite or value in precice-config.xml.""" if self.max_time is None and self.max_time_windows is None: @@ -661,6 +721,7 @@ def run(self, run_directory: Path): Runs the system test by generating the Docker Compose file, copying everything into a run folder, and executing docker-compose up. """ self.__prepare_for_run(run_directory) + self.__init_run_logs() std_out: List[str] = [] std_err: List[str] = [] @@ -669,7 +730,6 @@ def run(self, run_directory: Path): std_out.extend(docker_build_result.stdout_data) std_err.extend(docker_build_result.stderr_data) if docker_build_result.exit_code != 0: - self.__write_logs(std_out, std_err) logging.critical(f"Could not build the docker images, {self} failed") return SystemtestResult( False, @@ -684,7 +744,6 @@ def run(self, run_directory: Path): std_out.extend(docker_run_result.stdout_data) std_err.extend(docker_run_result.stderr_data) if docker_run_result.exit_code != 0: - self.__write_logs(std_out, std_err) logging.critical(f"Could not run the tutorial, {self} failed") return SystemtestResult( False, @@ -700,7 +759,6 @@ def run(self, run_directory: Path): std_err.extend(fieldcompare_result.stderr_data) if fieldcompare_result.exit_code != 0: self.__archive_fieldcompare_diffs() - self.__write_logs(std_out, std_err) logging.critical(f"Fieldcompare returned non zero exit code, therefore {self} failed") return SystemtestResult( False, @@ -713,7 +771,6 @@ def run(self, run_directory: Path): # self.__cleanup() self._cleanup_docker_networks() - self.__write_logs(std_out, std_err) return SystemtestResult( True, std_out, @@ -728,6 +785,7 @@ def run_for_reference_results(self, run_directory: Path): Runs the system test by generating the Docker Compose files to generate the reference results """ self.__prepare_for_run(run_directory) + self.__init_run_logs() std_out: List[str] = [] std_err: List[str] = [] self._cleanup_docker_networks() @@ -735,7 +793,6 @@ def run_for_reference_results(self, run_directory: Path): std_out.extend(docker_build_result.stdout_data) std_err.extend(docker_build_result.stderr_data) if docker_build_result.exit_code != 0: - self.__write_logs(std_out, std_err) logging.critical(f"Could not build the docker images, {self} failed") return SystemtestResult( False, @@ -750,7 +807,6 @@ def run_for_reference_results(self, run_directory: Path): std_out.extend(docker_run_result.stdout_data) std_err.extend(docker_run_result.stderr_data) if docker_run_result.exit_code != 0: - self.__write_logs(std_out, std_err) logging.critical(f"Could not run the tutorial, {self} failed") return SystemtestResult( False, @@ -762,7 +818,6 @@ def run_for_reference_results(self, run_directory: Path): fieldcompare_time=0) self._cleanup_docker_networks() - self.__write_logs(std_out, std_err) return SystemtestResult( True, std_out, @@ -777,13 +832,13 @@ def run_only_build(self, run_directory: Path): Runs only the build commmand, for example to preheat the caches of the docker builder. """ self.__prepare_for_run(run_directory) + self.__init_run_logs() std_out: List[str] = [] std_err: List[str] = [] docker_build_result = self._build_docker() std_out.extend(docker_build_result.stdout_data) std_err.extend(docker_build_result.stderr_data) if docker_build_result.exit_code != 0: - self.__write_logs(std_out, std_err) logging.critical(f"Could not build the docker images, {self} failed") return SystemtestResult( False, @@ -794,7 +849,6 @@ def run_only_build(self, run_directory: Path): solver_time=0, fieldcompare_time=0) - self.__write_logs(std_out, std_err) return SystemtestResult( True, std_out, From 68c423b2332ff6475852283ed8030078d33c91f6 Mon Sep 17 00:00:00 2001 From: Pranjal Date: Thu, 11 Jun 2026 03:34:26 +0530 Subject: [PATCH 74/94] Append failed system test log tails to GHA job summary (#830) --- changelog-entries/830.md | 1 + tools/tests/systemtests/Systemtest.py | 43 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 changelog-entries/830.md diff --git a/changelog-entries/830.md b/changelog-entries/830.md new file mode 100644 index 000000000..d481a1e90 --- /dev/null +++ b/changelog-entries/830.md @@ -0,0 +1 @@ +- Failed system tests now append the last lines of the relevant stage log to the GitHub Actions job summary [#830](https://github.com/precice/tutorials/pull/830). diff --git a/tools/tests/systemtests/Systemtest.py b/tools/tests/systemtests/Systemtest.py index 14ec8a97d..93e972f2e 100644 --- a/tools/tests/systemtests/Systemtest.py +++ b/tools/tests/systemtests/Systemtest.py @@ -31,6 +31,8 @@ "compare": "system-tests-compare.log", } +FAILURE_LOG_TAIL_LINES = 100 + class _SystemtestLogSink: """Writes subprocess output incrementally to per-stage log files.""" @@ -111,6 +113,45 @@ def _success_status_symbol(success: bool) -> str: return "✅" if success else "❌" +def _read_log_tail(log_path: Path, max_lines: int = FAILURE_LOG_TAIL_LINES) -> str: + lines = log_path.read_text(encoding="utf-8", errors="replace").splitlines() + if not lines: + return "(log file is empty)" + return "\n".join(lines[-max_lines:]) + + +def _append_failure_log_tails_to_summary(results: List[SystemtestResult]) -> None: + summary_path = os.environ.get("GITHUB_STEP_SUMMARY") + if not summary_path: + return + + failed_results = [result for result in results if not result.success] + if not failed_results: + return + + with open(summary_path, "a", encoding="utf-8") as summary_file: + print("\n## Failed test logs\n", file=summary_file) + for result in failed_results: + print( + f"### {_success_status_symbol(False)} {result.systemtest}\n", + file=summary_file, + ) + run_dir = result.systemtest.get_system_test_dir() + for log_name in STAGE_LOG_FILES.values(): + log_path = run_dir / log_name + if not log_path.is_file(): + continue + tail = _read_log_tail(log_path) + print("

", file=summary_file) + print(f"{log_name} tail", file=summary_file) + print("", file=summary_file) + print("```text", file=summary_file) + print(tail, file=summary_file) + print("```", file=summary_file) + print("
", file=summary_file) + print("", file=summary_file) + + def display_systemtestresults_as_table(results: List[SystemtestResult]): """ Prints the result in a nice tabluated way to get an easy overview @@ -152,6 +193,8 @@ def _get_length_of_name(results: List[SystemtestResult]) -> int: with open(os.environ["GITHUB_STEP_SUMMARY"], "a") as f: print(row, file=f) + _append_failure_log_tails_to_summary(results) + if "GITHUB_STEP_SUMMARY" in os.environ: with open(os.environ["GITHUB_STEP_SUMMARY"], "a") as f: print("\n\n", file=f) From 781395a3dd3fb19531eae93f404f013b0a95d64b Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 11 Jun 2026 16:27:47 +0200 Subject: [PATCH 75/94] Add common output/ directory to .gitignore --- .gitignore | 1 + elastic-tube-1d/.gitignore | 2 -- flow-around-controlled-moving-cylinder/.gitignore | 1 - oscillator/mass-left-fmi/.gitignore | 3 +-- oscillator/mass-right-fmi/.gitignore | 3 +-- wolf-sheep-soil-creep/soil-creep-landlab/.gitignore | 1 - 6 files changed, 3 insertions(+), 8 deletions(-) delete mode 100644 wolf-sheep-soil-creep/soil-creep-landlab/.gitignore diff --git a/.gitignore b/.gitignore index 1bec1510a..ba29ff2fa 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ out # Common result files +output/ *.vtk *.vtu *.pvd diff --git a/elastic-tube-1d/.gitignore b/elastic-tube-1d/.gitignore index 584556938..bd6ed9162 100644 --- a/elastic-tube-1d/.gitignore +++ b/elastic-tube-1d/.gitignore @@ -1,8 +1,6 @@ fluid-cpp/build/ -fluid-cpp/output/*.vtk solid-cpp/build/ fluid-python/__pycache__/ -fluid-python/output/*.vtk solid-python/__pycache__/ *.o diff --git a/flow-around-controlled-moving-cylinder/.gitignore b/flow-around-controlled-moving-cylinder/.gitignore index 016def67a..2e8d5b709 100644 --- a/flow-around-controlled-moving-cylinder/.gitignore +++ b/flow-around-controlled-moving-cylinder/.gitignore @@ -1,4 +1,3 @@ controller-fmi/PIDcontroller.fmu -controller-fmi/output/ # The fluid-openfoam case uses a 0.orig fluid-openfoam/0/ diff --git a/oscillator/mass-left-fmi/.gitignore b/oscillator/mass-left-fmi/.gitignore index 63adc8640..653e4b980 100644 --- a/oscillator/mass-left-fmi/.gitignore +++ b/oscillator/mass-left-fmi/.gitignore @@ -1,2 +1 @@ -Oscillator.fmu -output \ No newline at end of file +Oscillator.fmu \ No newline at end of file diff --git a/oscillator/mass-right-fmi/.gitignore b/oscillator/mass-right-fmi/.gitignore index 63adc8640..653e4b980 100644 --- a/oscillator/mass-right-fmi/.gitignore +++ b/oscillator/mass-right-fmi/.gitignore @@ -1,2 +1 @@ -Oscillator.fmu -output \ No newline at end of file +Oscillator.fmu \ No newline at end of file diff --git a/wolf-sheep-soil-creep/soil-creep-landlab/.gitignore b/wolf-sheep-soil-creep/soil-creep-landlab/.gitignore deleted file mode 100644 index ea1472ec1..000000000 --- a/wolf-sheep-soil-creep/soil-creep-landlab/.gitignore +++ /dev/null @@ -1 +0,0 @@ -output/ From fd6e3aedc14027a4d16e6b20f1ebf6e281721c9e Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 11 Jun 2026 20:55:21 +0200 Subject: [PATCH 76/94] Fix some inconsistencies (#831) --- .gitignore | 14 ++++++++++++++ elastic-tube-1d/.gitignore | 9 --------- flow-around-controlled-moving-cylinder/.gitignore | 3 --- .../fluid-openfoam/.gitignore | 2 ++ flow-over-heated-plate/solid-fenicsx/clean.sh | 2 +- flow-over-heated-plate/solid-fenicsx/run.sh | 12 ++++++++---- heat-exchanger/.gitignore | 12 ------------ heat-exchanger/fluid-inner-openfoam/.gitignore | 1 + heat-exchanger/fluid-outer-openfoam/.gitignore | 1 + heat-exchanger/solid-calculix/.gitignore | 10 ++++++++++ oscillator/.gitignore | 1 - oscillator/mass-left-fmi/.gitignore | 1 - oscillator/mass-right-fmi/.gitignore | 1 - .../dirichlet-fenics/run.sh | 11 ++++++----- .../dirichlet-fenicsx/run.sh | 12 ++++++++---- partitioned-heat-conduction/neumann-fenics/run.sh | 11 ++++++----- partitioned-heat-conduction/neumann-fenicsx/run.sh | 13 +++++++++---- .../solver-fenicsx/clean.sh | 2 +- .../solver-openfoam/.gitignore | 4 ---- .../solidDisplacementFoamForce/.gitignore | 4 ---- quickstart/solid-cpp/.gitignore | 4 ---- resonant-circuit/capacitor-julia/clean.sh | 2 +- resonant-circuit/capacitor-matlab/clean.sh | 2 +- resonant-circuit/capacitor-matlab/run.sh | 2 +- resonant-circuit/capacitor-python/clean.sh | 2 +- resonant-circuit/coil-julia/clean.sh | 2 +- resonant-circuit/coil-matlab/clean.sh | 2 +- resonant-circuit/coil-matlab/run.sh | 2 +- resonant-circuit/coil-python/clean.sh | 2 +- resonant-circuit/plot-solution.sh | 2 +- tools/tests/.gitignore | 2 -- volume-coupled-flow/.gitignore | 1 + 32 files changed, 77 insertions(+), 74 deletions(-) delete mode 100644 elastic-tube-1d/.gitignore delete mode 100644 flow-around-controlled-moving-cylinder/.gitignore create mode 100644 flow-around-controlled-moving-cylinder/fluid-openfoam/.gitignore delete mode 100644 heat-exchanger/.gitignore create mode 100644 heat-exchanger/fluid-inner-openfoam/.gitignore create mode 100644 heat-exchanger/fluid-outer-openfoam/.gitignore create mode 100644 heat-exchanger/solid-calculix/.gitignore delete mode 100644 oscillator/.gitignore delete mode 100644 oscillator/mass-left-fmi/.gitignore delete mode 100644 oscillator/mass-right-fmi/.gitignore delete mode 100644 partitioned-heat-conduction/solver-openfoam/.gitignore delete mode 100644 perpendicular-flap/solid-openfoam/solidDisplacementFoamForce/.gitignore delete mode 100644 tools/tests/.gitignore diff --git a/.gitignore b/.gitignore index ba29ff2fa..1a45bde2a 100644 --- a/.gitignore +++ b/.gitignore @@ -31,12 +31,19 @@ precice-config.xml.orig *.o *.so .out + +# CMake build/ +CMakeFiles/ +cmake_install.cmake +CMakeCache.txt +Makefile # Python .venv/ __pycache__/ *.pyc +*.pyo # Rust Cargo.lock @@ -60,6 +67,10 @@ postProcessing/ *.foam *.OpenFOAM dynamicCode +Make/* +!Make/files +!Make/options +lnInclude/ # CalculiX spooles.out @@ -108,6 +119,9 @@ install* # G+smo pointData.csv +# FMI +*.fmu + # FEniCS covered by .vtk # Nutils covered by .vtk diff --git a/elastic-tube-1d/.gitignore b/elastic-tube-1d/.gitignore deleted file mode 100644 index bd6ed9162..000000000 --- a/elastic-tube-1d/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -fluid-cpp/build/ -solid-cpp/build/ -fluid-python/__pycache__/ -solid-python/__pycache__/ - -*.o -*.log -*.json -*.pyc diff --git a/flow-around-controlled-moving-cylinder/.gitignore b/flow-around-controlled-moving-cylinder/.gitignore deleted file mode 100644 index 2e8d5b709..000000000 --- a/flow-around-controlled-moving-cylinder/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -controller-fmi/PIDcontroller.fmu -# The fluid-openfoam case uses a 0.orig -fluid-openfoam/0/ diff --git a/flow-around-controlled-moving-cylinder/fluid-openfoam/.gitignore b/flow-around-controlled-moving-cylinder/fluid-openfoam/.gitignore new file mode 100644 index 000000000..9a342fb3d --- /dev/null +++ b/flow-around-controlled-moving-cylinder/fluid-openfoam/.gitignore @@ -0,0 +1,2 @@ +# Pre-processing 0.orig +0/ diff --git a/flow-over-heated-plate/solid-fenicsx/clean.sh b/flow-over-heated-plate/solid-fenicsx/clean.sh index 9413511cc..57d3ea1b0 100755 --- a/flow-over-heated-plate/solid-fenicsx/clean.sh +++ b/flow-over-heated-plate/solid-fenicsx/clean.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -e -u . ../../tools/cleaning-tools.sh diff --git a/flow-over-heated-plate/solid-fenicsx/run.sh b/flow-over-heated-plate/solid-fenicsx/run.sh index 01793a3e7..d339c8e23 100755 --- a/flow-over-heated-plate/solid-fenicsx/run.sh +++ b/flow-over-heated-plate/solid-fenicsx/run.sh @@ -1,10 +1,14 @@ -#!/bin/sh +#!/usr/bin/env bash set -e -u -if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi -. .venv/bin/activate -pip install -r requirements.txt +fi python3 solid.py diff --git a/heat-exchanger/.gitignore b/heat-exchanger/.gitignore deleted file mode 100644 index 41190654c..000000000 --- a/heat-exchanger/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -# This case stores pre-generated meshes outside the repository -solid-calculix/adiabatic.dfl -solid-calculix/all.msh -solid-calculix/inner-interface.flm -solid-calculix/inner-interface.nam -solid-calculix/inner-interface.sur -solid-calculix/outer-interface.flm -solid-calculix/outer-interface.nam -solid-calculix/outer-interface.sur -solid-calculix/solid.inp -fluid-inner-openfoam/constant/polyMesh -fluid-outer-openfoam/constant/polyMesh diff --git a/heat-exchanger/fluid-inner-openfoam/.gitignore b/heat-exchanger/fluid-inner-openfoam/.gitignore new file mode 100644 index 000000000..8cd8f4a4c --- /dev/null +++ b/heat-exchanger/fluid-inner-openfoam/.gitignore @@ -0,0 +1 @@ +constant/polyMesh \ No newline at end of file diff --git a/heat-exchanger/fluid-outer-openfoam/.gitignore b/heat-exchanger/fluid-outer-openfoam/.gitignore new file mode 100644 index 000000000..8cd8f4a4c --- /dev/null +++ b/heat-exchanger/fluid-outer-openfoam/.gitignore @@ -0,0 +1 @@ +constant/polyMesh \ No newline at end of file diff --git a/heat-exchanger/solid-calculix/.gitignore b/heat-exchanger/solid-calculix/.gitignore new file mode 100644 index 000000000..b15ea7aaf --- /dev/null +++ b/heat-exchanger/solid-calculix/.gitignore @@ -0,0 +1,10 @@ +# Files downloaded via run.sh +adiabatic.dfl +all.msh +inner-interface.flm +inner-interface.nam +inner-interface.sur +outer-interface.flm +outer-interface.nam +outer-interface.sur +solid.inp diff --git a/oscillator/.gitignore b/oscillator/.gitignore deleted file mode 100644 index 69310321e..000000000 --- a/oscillator/.gitignore +++ /dev/null @@ -1 +0,0 @@ -fmi/Oscillator.fmu diff --git a/oscillator/mass-left-fmi/.gitignore b/oscillator/mass-left-fmi/.gitignore deleted file mode 100644 index 653e4b980..000000000 --- a/oscillator/mass-left-fmi/.gitignore +++ /dev/null @@ -1 +0,0 @@ -Oscillator.fmu \ No newline at end of file diff --git a/oscillator/mass-right-fmi/.gitignore b/oscillator/mass-right-fmi/.gitignore deleted file mode 100644 index 653e4b980..000000000 --- a/oscillator/mass-right-fmi/.gitignore +++ /dev/null @@ -1 +0,0 @@ -Oscillator.fmu \ No newline at end of file diff --git a/partitioned-heat-conduction/dirichlet-fenics/run.sh b/partitioned-heat-conduction/dirichlet-fenics/run.sh index 288101292..f0ae51182 100755 --- a/partitioned-heat-conduction/dirichlet-fenics/run.sh +++ b/partitioned-heat-conduction/dirichlet-fenics/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r ../solver-fenics/requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r ../solver-fenics/requirements.txt fi if [ $# -eq 0 ] diff --git a/partitioned-heat-conduction/dirichlet-fenicsx/run.sh b/partitioned-heat-conduction/dirichlet-fenicsx/run.sh index 81dc8e08c..f18697619 100755 --- a/partitioned-heat-conduction/dirichlet-fenicsx/run.sh +++ b/partitioned-heat-conduction/dirichlet-fenicsx/run.sh @@ -1,10 +1,14 @@ -#!/bin/sh +#!/usr/bin/env bash set -e -u -if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r ../solver-fenicsx/requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi -. .venv/bin/activate -pip install -r ../solver-fenicsx/requirements.txt +fi python3 ../solver-fenicsx/heat.py Dirichlet diff --git a/partitioned-heat-conduction/neumann-fenics/run.sh b/partitioned-heat-conduction/neumann-fenics/run.sh index d71a3122f..cb8940be6 100755 --- a/partitioned-heat-conduction/neumann-fenics/run.sh +++ b/partitioned-heat-conduction/neumann-fenics/run.sh @@ -4,13 +4,14 @@ set -e -u . ../../tools/log.sh exec > >(tee --append "$LOGFILE") 2>&1 -if [ ! -v PRECICE_TUTORIALS_NO_VENV ] -then - if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r ../solver-fenics/requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r ../solver-fenics/requirements.txt fi python3 ../solver-fenics/heat.py Neumann diff --git a/partitioned-heat-conduction/neumann-fenicsx/run.sh b/partitioned-heat-conduction/neumann-fenicsx/run.sh index c352157be..0ba6e8b47 100755 --- a/partitioned-heat-conduction/neumann-fenicsx/run.sh +++ b/partitioned-heat-conduction/neumann-fenicsx/run.sh @@ -1,9 +1,14 @@ -#!/bin/sh +#!/usr/bin/env bash set -e -u -if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r ../solver-fenicsx/requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi -. .venv/bin/activate -pip install -r ../solver-fenicsx/requirements.txt +fi + python3 ../solver-fenicsx/heat.py Neumann diff --git a/partitioned-heat-conduction/solver-fenicsx/clean.sh b/partitioned-heat-conduction/solver-fenicsx/clean.sh index a5aeebd18..bdd9995e1 100755 --- a/partitioned-heat-conduction/solver-fenicsx/clean.sh +++ b/partitioned-heat-conduction/solver-fenicsx/clean.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -e -u . ../../tools/cleaning-tools.sh diff --git a/partitioned-heat-conduction/solver-openfoam/.gitignore b/partitioned-heat-conduction/solver-openfoam/.gitignore deleted file mode 100644 index 1729c7850..000000000 --- a/partitioned-heat-conduction/solver-openfoam/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -Make/* -!Make/files -!Make/options -lnInclude/ diff --git a/perpendicular-flap/solid-openfoam/solidDisplacementFoamForce/.gitignore b/perpendicular-flap/solid-openfoam/solidDisplacementFoamForce/.gitignore deleted file mode 100644 index 1729c7850..000000000 --- a/perpendicular-flap/solid-openfoam/solidDisplacementFoamForce/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -Make/* -!Make/files -!Make/options -lnInclude/ diff --git a/quickstart/solid-cpp/.gitignore b/quickstart/solid-cpp/.gitignore index 01e69efeb..21e96cc46 100644 --- a/quickstart/solid-cpp/.gitignore +++ b/quickstart/solid-cpp/.gitignore @@ -1,5 +1 @@ -CMakeFiles/ -cmake_install.cmake -CMakeCache.txt -Makefile rigid_body_solver diff --git a/resonant-circuit/capacitor-julia/clean.sh b/resonant-circuit/capacitor-julia/clean.sh index 8c3dba08a..f9c06b0f3 100755 --- a/resonant-circuit/capacitor-julia/clean.sh +++ b/resonant-circuit/capacitor-julia/clean.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -e -u . ../../tools/cleaning-tools.sh diff --git a/resonant-circuit/capacitor-matlab/clean.sh b/resonant-circuit/capacitor-matlab/clean.sh index 8b1311d03..e0b5926f1 100755 --- a/resonant-circuit/capacitor-matlab/clean.sh +++ b/resonant-circuit/capacitor-matlab/clean.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -e -u . ../../tools/cleaning-tools.sh diff --git a/resonant-circuit/capacitor-matlab/run.sh b/resonant-circuit/capacitor-matlab/run.sh index 37a830a91..906b25220 100755 --- a/resonant-circuit/capacitor-matlab/run.sh +++ b/resonant-circuit/capacitor-matlab/run.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # Run MATLAB code without GUI matlab -nodisplay -r "capacitor;exit;" diff --git a/resonant-circuit/capacitor-python/clean.sh b/resonant-circuit/capacitor-python/clean.sh index 8c3dba08a..f9c06b0f3 100755 --- a/resonant-circuit/capacitor-python/clean.sh +++ b/resonant-circuit/capacitor-python/clean.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -e -u . ../../tools/cleaning-tools.sh diff --git a/resonant-circuit/coil-julia/clean.sh b/resonant-circuit/coil-julia/clean.sh index 8c3dba08a..f9c06b0f3 100755 --- a/resonant-circuit/coil-julia/clean.sh +++ b/resonant-circuit/coil-julia/clean.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -e -u . ../../tools/cleaning-tools.sh diff --git a/resonant-circuit/coil-matlab/clean.sh b/resonant-circuit/coil-matlab/clean.sh index 5c6294dfa..8dc0ec32b 100755 --- a/resonant-circuit/coil-matlab/clean.sh +++ b/resonant-circuit/coil-matlab/clean.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -e -u . ../../tools/cleaning-tools.sh diff --git a/resonant-circuit/coil-matlab/run.sh b/resonant-circuit/coil-matlab/run.sh index 2c5e471a7..085ae73f2 100755 --- a/resonant-circuit/coil-matlab/run.sh +++ b/resonant-circuit/coil-matlab/run.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # Run MATLAB code without GUI matlab -nodisplay -r "coil;exit;" diff --git a/resonant-circuit/coil-python/clean.sh b/resonant-circuit/coil-python/clean.sh index 8c3dba08a..f9c06b0f3 100755 --- a/resonant-circuit/coil-python/clean.sh +++ b/resonant-circuit/coil-python/clean.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -e -u . ../../tools/cleaning-tools.sh diff --git a/resonant-circuit/plot-solution.sh b/resonant-circuit/plot-solution.sh index 85f5b901c..bd2158b27 100755 --- a/resonant-circuit/plot-solution.sh +++ b/resonant-circuit/plot-solution.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash if [ "${1:-}" = "" ]; then echo "No target directory specified. Please specify the directory of the participant containing the watchpoint, e.g. ./plot-displacement.sh coil-python." diff --git a/tools/tests/.gitignore b/tools/tests/.gitignore deleted file mode 100644 index 3072e6ffe..000000000 --- a/tools/tests/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.pyc -*.pyo \ No newline at end of file diff --git a/volume-coupled-flow/.gitignore b/volume-coupled-flow/.gitignore index 0e6291ec5..1706a8443 100644 --- a/volume-coupled-flow/.gitignore +++ b/volume-coupled-flow/.gitignore @@ -1 +1,2 @@ +# Signal file expected by the system tests specifically in uni-directional coupling fluid-participant-finished.log \ No newline at end of file From a93acd399f732c178092795d1f22b4dc3766fafb Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Thu, 11 Jun 2026 21:29:11 +0200 Subject: [PATCH 77/94] Fix more inconsistencies regarding venv handling --- oscillator-overlap/mass-left-python/run.sh | 8 +++++--- oscillator-overlap/mass-right-python/run.sh | 8 +++++--- oscillator/mass-left-python/run.sh | 8 +++++--- oscillator/mass-right-python/run.sh | 8 +++++--- .../dirichlet-fenics/run.sh | 4 ++-- .../neumann-fenics/run.sh | 4 ++-- .../left-fenics/run.sh | 8 +++++--- .../right-fenics/run.sh | 8 +++++--- tools/visualize-configs.sh | 11 ++++++++--- volume-coupled-diffusion/drain-fenics/run.sh | 8 +++++--- volume-coupled-diffusion/source-fenics/run.sh | 8 +++++--- 11 files changed, 52 insertions(+), 31 deletions(-) diff --git a/oscillator-overlap/mass-left-python/run.sh b/oscillator-overlap/mass-left-python/run.sh index 361b2d2a2..2582e4270 100755 --- a/oscillator-overlap/mass-left-python/run.sh +++ b/oscillator-overlap/mass-left-python/run.sh @@ -6,11 +6,13 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - if [ ! -d .venv ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r ../solver-python/requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r ../solver-python/requirements.txt && pip freeze > pip-installed-packages.log fi python3 ../solver-python/oscillator.py Mass-Left diff --git a/oscillator-overlap/mass-right-python/run.sh b/oscillator-overlap/mass-right-python/run.sh index 82ab74754..be8fad2c5 100755 --- a/oscillator-overlap/mass-right-python/run.sh +++ b/oscillator-overlap/mass-right-python/run.sh @@ -6,11 +6,13 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - if [ ! -d .venv ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r ../solver-python/requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r ../solver-python/requirements.txt && pip freeze > pip-installed-packages.log fi python3 ../solver-python/oscillator.py Mass-Right diff --git a/oscillator/mass-left-python/run.sh b/oscillator/mass-left-python/run.sh index 361b2d2a2..2582e4270 100755 --- a/oscillator/mass-left-python/run.sh +++ b/oscillator/mass-left-python/run.sh @@ -6,11 +6,13 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - if [ ! -d .venv ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r ../solver-python/requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r ../solver-python/requirements.txt && pip freeze > pip-installed-packages.log fi python3 ../solver-python/oscillator.py Mass-Left diff --git a/oscillator/mass-right-python/run.sh b/oscillator/mass-right-python/run.sh index 82ab74754..be8fad2c5 100755 --- a/oscillator/mass-right-python/run.sh +++ b/oscillator/mass-right-python/run.sh @@ -6,11 +6,13 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - if [ ! -d .venv ]; then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install -r ../solver-python/requirements.txt && pip freeze > pip-installed-packages.log + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r ../solver-python/requirements.txt && pip freeze > pip-installed-packages.log fi python3 ../solver-python/oscillator.py Mass-Right diff --git a/partitioned-heat-conduction-complex/dirichlet-fenics/run.sh b/partitioned-heat-conduction-complex/dirichlet-fenics/run.sh index 7f2251819..02bae5076 100755 --- a/partitioned-heat-conduction-complex/dirichlet-fenics/run.sh +++ b/partitioned-heat-conduction-complex/dirichlet-fenics/run.sh @@ -6,10 +6,10 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - if [ ! -d .venv ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv fi - . .venv/bin/activate + source .venv/bin/activate pip install -r ../solver-fenics/requirements.txt fi diff --git a/partitioned-heat-conduction-complex/neumann-fenics/run.sh b/partitioned-heat-conduction-complex/neumann-fenics/run.sh index 4c82b9962..df26e7195 100755 --- a/partitioned-heat-conduction-complex/neumann-fenics/run.sh +++ b/partitioned-heat-conduction-complex/neumann-fenics/run.sh @@ -6,10 +6,10 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - if [ ! -d .venv ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv fi - . .venv/bin/activate + source .venv/bin/activate pip install -r ../solver-fenics/requirements.txt fi diff --git a/partitioned-heat-conduction-overlap/left-fenics/run.sh b/partitioned-heat-conduction-overlap/left-fenics/run.sh index af99ead39..49156c5d6 100755 --- a/partitioned-heat-conduction-overlap/left-fenics/run.sh +++ b/partitioned-heat-conduction-overlap/left-fenics/run.sh @@ -6,11 +6,13 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - if [ ! -d .venv ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r ../solver-fenics/requirements.txt + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r ../solver-fenics/requirements.txt fi python3 ../solver-fenics/heat.py Left diff --git a/partitioned-heat-conduction-overlap/right-fenics/run.sh b/partitioned-heat-conduction-overlap/right-fenics/run.sh index dfbabbdfe..3c985c42a 100755 --- a/partitioned-heat-conduction-overlap/right-fenics/run.sh +++ b/partitioned-heat-conduction-overlap/right-fenics/run.sh @@ -6,11 +6,13 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - if [ ! -d .venv ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r ../solver-fenics/requirements.txt + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r ../solver-fenics/requirements.txt fi python3 ../solver-fenics/heat.py Right diff --git a/tools/visualize-configs.sh b/tools/visualize-configs.sh index a02b72895..e56a110ae 100755 --- a/tools/visualize-configs.sh +++ b/tools/visualize-configs.sh @@ -30,11 +30,16 @@ visualize_config(){ export -f visualize_config -if [ ! -d .venv ]; then +if [ ! -v PRECICE_TUTORIALS_NO_VENV ] +then + if [ ! -d ".venv" ]; then python3 -m venv .venv + source .venv/bin/activate + pip install precice-config-visualizer + else + source .venv/bin/activate fi -. .venv/bin/activate -pip install precice-config-visualizer +fi tutorials=$(find . -maxdepth 1 -type d -not -name ".*" | sed "s/^.\///") diff --git a/volume-coupled-diffusion/drain-fenics/run.sh b/volume-coupled-diffusion/drain-fenics/run.sh index 705626ca6..ef8d54901 100755 --- a/volume-coupled-diffusion/drain-fenics/run.sh +++ b/volume-coupled-diffusion/drain-fenics/run.sh @@ -6,11 +6,13 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - if [ ! -d .venv ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r ../solver-fenics/requirements.txt + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r ../solver-fenics/requirements.txt fi python3 ../solver-fenics/volume-coupled-diffusion.py --drain diff --git a/volume-coupled-diffusion/source-fenics/run.sh b/volume-coupled-diffusion/source-fenics/run.sh index 61fc2fd90..2812b4fd9 100755 --- a/volume-coupled-diffusion/source-fenics/run.sh +++ b/volume-coupled-diffusion/source-fenics/run.sh @@ -6,11 +6,13 @@ exec > >(tee --append "$LOGFILE") 2>&1 if [ ! -v PRECICE_TUTORIALS_NO_VENV ] then - if [ ! -d .venv ]; then + if [ ! -d ".venv" ]; then python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r ../solver-fenics/requirements.txt + else + source .venv/bin/activate fi - . .venv/bin/activate - pip install -r ../solver-fenics/requirements.txt fi python3 ../solver-fenics/volume-coupled-diffusion.py --source From 6cb49261197a500cd4cc6eb9cff4112a855b98f3 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Fri, 12 Jun 2026 18:02:55 +0200 Subject: [PATCH 78/94] Add FEniCSx adapter to the system tests (#832) --- flow-over-heated-plate/metadata.yaml | 10 ++++---- .../fluid-openfoam_solid-fenicsx.tar.gz | 3 +++ .../reference_results.metadata | 5 ++-- partitioned-heat-conduction/metadata.yaml | 20 ++++++++-------- .../dirichlet-fenicsx_neumann-fenicsx.tar.gz | 3 +++ .../reference_results.metadata | 7 +++--- .../component-templates/fenicsx-adapter.yaml | 16 +++++++++++++ tools/tests/components.yaml | 23 +++++++++++++++++++ .../tests/dockerfiles/ubuntu_2404/Dockerfile | 12 ++++++++++ tools/tests/reference_versions.yaml | 1 + tools/tests/tests.yaml | 21 +++++++++++++++++ 11 files changed, 100 insertions(+), 21 deletions(-) create mode 100644 flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenicsx.tar.gz create mode 100644 partitioned-heat-conduction/reference-results/dirichlet-fenicsx_neumann-fenicsx.tar.gz create mode 100644 tools/tests/component-templates/fenicsx-adapter.yaml diff --git a/flow-over-heated-plate/metadata.yaml b/flow-over-heated-plate/metadata.yaml index bc88eb463..ec86ec951 100644 --- a/flow-over-heated-plate/metadata.yaml +++ b/flow-over-heated-plate/metadata.yaml @@ -31,11 +31,11 @@ cases: run: ./run.sh component: fenics-adapter - # solid-fenicsx: - # participant: Solid - # directory: ./solid-fenicsx - # run: ./run.sh - # component: fenicsx-adapter + solid-fenicsx: + participant: Solid + directory: ./solid-fenicsx + run: ./run.sh + component: fenicsx-adapter solid-nutils: participant: Solid diff --git a/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenicsx.tar.gz b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenicsx.tar.gz new file mode 100644 index 000000000..98780963b --- /dev/null +++ b/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenicsx.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b31adcb4938e92dd05418425329281f48a86575792fc5063a65f4ef03be058ae +size 1128130 diff --git a/flow-over-heated-plate/reference-results/reference_results.metadata b/flow-over-heated-plate/reference-results/reference_results.metadata index 3fcdb8497..2d90de3ff 100644 --- a/flow-over-heated-plate/reference-results/reference_results.metadata +++ b/flow-over-heated-plate/reference-results/reference_results.metadata @@ -11,7 +11,7 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| fluid-su2_solid-openfoam.tar.gz | 2026-05-30 15:48:12 | 9f09992b4c0a472dc1a978d5187c3e9c2335d298c93a79081fd30abbb941bfa8 | +| fluid-openfoam_solid-fenicsx.tar.gz | 2026-06-12 17:50:05 | b31adcb4938e92dd05418425329281f48a86575792fc5063a65f4ef03be058ae | ## List of arguments used to generate the files @@ -24,6 +24,7 @@ We also include some information on the machine used to generate them | OPENFOAM_EXECUTABLE | openfoam2512 | | SU2_VERSION | 7.5.1 | | FENICS_ADAPTER_REF | v2.3.0 | +| FENICSX_ADAPTER_REF | v1.0.1 | | CALCULIX_ADAPTER_REF | v2.20.1 | | DEALII_ADAPTER_REF | a421d92 | | DUMUX_ADAPTER_REF | 3f3f54f | @@ -32,7 +33,7 @@ We also include some information on the machine used to generate them | PRECICE_REF | v3.4.1 | | PYTHON_BINDINGS_REF | v3.4.0 | | SU2_ADAPTER_REF | 5abe79b | -| TUTORIALS_REF | more-tests | +| TUTORIALS_REF | c1c5b4a3b044b5401d06f933204ff41e3252cedc | | PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | diff --git a/partitioned-heat-conduction/metadata.yaml b/partitioned-heat-conduction/metadata.yaml index 9a655e4e8..b7c058970 100644 --- a/partitioned-heat-conduction/metadata.yaml +++ b/partitioned-heat-conduction/metadata.yaml @@ -13,11 +13,11 @@ cases: run: ./run.sh component: fenics-adapter - # dirichlet-fenicsx: - # participant: Dirichlet - # directory: ./dirichlet-fenicsx - # run: ./run.sh - # component: fenicsx-adapter + dirichlet-fenicsx: + participant: Dirichlet + directory: ./dirichlet-fenicsx + run: ./run.sh + component: fenicsx-adapter # dirichlet-gismo: # participant: Dirichlet @@ -43,11 +43,11 @@ cases: run: ./run.sh component: fenics-adapter - # neumann-fenicsx: - # participant: Neumann - # directory: ./neumann-fenicsx - # run: ./run.sh - # component: fenicsx-adapter + neumann-fenicsx: + participant: Neumann + directory: ./neumann-fenicsx + run: ./run.sh + component: fenicsx-adapter # neumann-gismo: # participant: Neumann diff --git a/partitioned-heat-conduction/reference-results/dirichlet-fenicsx_neumann-fenicsx.tar.gz b/partitioned-heat-conduction/reference-results/dirichlet-fenicsx_neumann-fenicsx.tar.gz new file mode 100644 index 000000000..5b6fd6906 --- /dev/null +++ b/partitioned-heat-conduction/reference-results/dirichlet-fenicsx_neumann-fenicsx.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:034a8b34ab2f4d003d53d5f07aedc8b76aa52dc63788aac80a78e72cdd2e59a2 +size 4593 diff --git a/partitioned-heat-conduction/reference-results/reference_results.metadata b/partitioned-heat-conduction/reference-results/reference_results.metadata index fc2d101d8..d9636303a 100644 --- a/partitioned-heat-conduction/reference-results/reference_results.metadata +++ b/partitioned-heat-conduction/reference-results/reference_results.metadata @@ -11,9 +11,7 @@ We also include some information on the machine used to generate them | name | time | sha256 | |------|------|-------| -| dirichlet-openfoam_neumann-openfoam.tar.gz | 2026-05-28 17:44:13 | 12a281982b1dc08e7fd9092dd5b30110ccc5f5db5b76594d60e7186b38d5fcbb | -| dirichlet-nutils_neumann-nutils.tar.gz | 2026-05-28 17:44:13 | 031a3bcc9a9d6d55ea4715b29e0c1a939bf522f1bf9cce048625590ab9fa3792 | -| dirichlet-fenics_neumann-fenics.tar.gz | 2026-05-28 17:44:13 | 6d2123f020ee4cddf89fb05fdd6e7abeb8f16cb33cca4a74ca7cfafceabcd91b | +| dirichlet-fenicsx_neumann-fenicsx.tar.gz | 2026-06-12 17:50:05 | 034a8b34ab2f4d003d53d5f07aedc8b76aa52dc63788aac80a78e72cdd2e59a2 | ## List of arguments used to generate the files @@ -26,6 +24,7 @@ We also include some information on the machine used to generate them | OPENFOAM_EXECUTABLE | openfoam2512 | | SU2_VERSION | 7.5.1 | | FENICS_ADAPTER_REF | v2.3.0 | +| FENICSX_ADAPTER_REF | v1.0.1 | | CALCULIX_ADAPTER_REF | v2.20.1 | | DEALII_ADAPTER_REF | a421d92 | | DUMUX_ADAPTER_REF | 3f3f54f | @@ -34,7 +33,7 @@ We also include some information on the machine used to generate them | PRECICE_REF | v3.4.1 | | PYTHON_BINDINGS_REF | v3.4.0 | | SU2_ADAPTER_REF | 5abe79b | -| TUTORIALS_REF | 7a5a9c9 | +| TUTORIALS_REF | c1c5b4a3b044b5401d06f933204ff41e3252cedc | | PRECICE_PRESET | production-audit | | PRECICE_UID | 1003 | | PRECICE_GID | 1003 | diff --git a/tools/tests/component-templates/fenicsx-adapter.yaml b/tools/tests/component-templates/fenicsx-adapter.yaml new file mode 100644 index 000000000..c92ee0db1 --- /dev/null +++ b/tools/tests/component-templates/fenicsx-adapter.yaml @@ -0,0 +1,16 @@ +build: + context: {{ dockerfile_context }} + args: + {% for key, value in build_arguments.items() %} + - {{key}}={{value}} + {% endfor %} + target: fenicsx_adapter +depends_on: + prepare: + condition: service_completed_successfully +volumes: + - {{ run_directory }}:/runs +command: > + /bin/bash -c "id && + cd '/runs/{{ tutorial_folder }}/{{ case_folder }}' && + {{ run }} | tee system-tests_{{ case_folder }}.log 2>&1" diff --git a/tools/tests/components.yaml b/tools/tests/components.yaml index d8498fd88..b9ba46e5b 100644 --- a/tools/tests/components.yaml +++ b/tools/tests/components.yaml @@ -81,6 +81,29 @@ fenics-adapter: description: Git ref of the fenics adapter to use default: "master" +fenicsx-adapter: + repository: https://github.com/precice/fenicsx-adapter + template: component-templates/fenicsx-adapter.yaml + build_arguments: + PLATFORM: + description: Dockerfile platform used + default: "ubuntu_2404" + PRECICE_REF: + description: Version of preCICE to use + default: "main" + PRECICE_PRESET: + description: CMake preset of preCICE + default: "production-audit" + TUTORIALS_REF: + description: Tutorial git reference to use + default: "master" + PYTHON_BINDINGS_REF: + description: Git ref of the Python bindings to use + default: "master" + FENICSX_ADAPTER_REF: + description: Git ref of the fenicsx adapter to use + default: "master" + nutils-adapter: repository: https://github.com/precice/nutils-adapter template: component-templates/nutils-adapter.yaml diff --git a/tools/tests/dockerfiles/ubuntu_2404/Dockerfile b/tools/tests/dockerfiles/ubuntu_2404/Dockerfile index df926fd33..dd1397002 100644 --- a/tools/tests/dockerfiles/ubuntu_2404/Dockerfile +++ b/tools/tests/dockerfiles/ubuntu_2404/Dockerfile @@ -126,6 +126,18 @@ RUN python3 -m venv --system-site-packages /home/precice/venv && \ . /home/precice/venv/bin/activate && \ pip3 install git+https://github.com/precice/fenics-adapter.git@${FENICS_ADAPTER_REF} +FROM precice_dependecies AS fenicsx_adapter +COPY --from=python_bindings /home/precice/.local /home/precice/.local +USER root +RUN add-apt-repository -y ppa:fenics-packages/fenics && \ + apt-get -qq update && \ + apt-get -qq install --no-install-recommends fenicsx +USER precice +ARG FENICSX_ADAPTER_REF +# Building fenicsx-adapter +RUN python3 -m venv --system-site-packages /home/precice/venv && \ + . /home/precice/venv/bin/activate && \ + pip3 install git+https://github.com/precice/fenics-adapter.git@${FENICSX_ADAPTER_REF} FROM precice_dependecies AS nutils_adapter COPY --from=python_bindings /home/precice/.local /home/precice/.local diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index 124975bc6..6c7e69563 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -13,6 +13,7 @@ SU2_VERSION: "7.5.1" # Tested components FENICS_ADAPTER_REF: "v2.3.0" +FENICSX_ADAPTER_REF: "v1.0.1" CALCULIX_ADAPTER_REF: "v2.20.1" DEALII_ADAPTER_REF: "a421d92" # develop, May 27, 2026 DUMUX_ADAPTER_REF: "3f3f54f" # develop, May 27, 2026 diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index ffcec4ffa..ce3a8a1f8 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -118,6 +118,12 @@ test_suites: - fluid-openfoam - solid-fenics reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz + - &flow-over-heated-plate_fluid-openfoam_solid-fenicsx + path: flow-over-heated-plate + case_combination: + - fluid-openfoam + - solid-fenicsx + reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenicsx.tar.gz - &flow-over-heated-plate_fluid-openfoam_solid-nutils path: flow-over-heated-plate case_combination: @@ -265,6 +271,13 @@ test_suites: - neumann-fenics max_time: 0.3 reference_result: ./partitioned-heat-conduction/reference-results/dirichlet-fenics_neumann-fenics.tar.gz + - &partitioned-heat-conduction_dirichlet-fenicsx_neumann-fenicsx + path: partitioned-heat-conduction + case_combination: + - dirichlet-fenicsx + - neumann-fenicsx + max_time: 0.3 + reference_result: ./partitioned-heat-conduction/reference-results/dirichlet-fenicsx_neumann-fenicsx.tar.gz - &partitioned-heat-conduction_dirichlet-nutils_neumann-nutils path: partitioned-heat-conduction case_combination: @@ -513,6 +526,7 @@ test_suites: - *elastic-tube-3d_fluid-openfoam_solid-calculix - *flow-around-controlled-moving-cylinder_controller-fmi_fluid-openfoam_solid-python - *flow-over-heated-plate_fluid-openfoam_solid-fenics + - *flow-over-heated-plate_fluid-openfoam_solid-fenicsx - *flow-over-heated-plate_fluid-openfoam_solid-nutils - *flow-over-heated-plate_fluid-openfoam_solid-openfoam - *flow-over-heated-plate_fluid-su2_solid-openfoam @@ -529,6 +543,7 @@ test_suites: - *partitioned-backwards-facing-step_fluid1-openfoam_fluid2-openfoam - *partitioned-elastic-beam_dirichlet-calculix_neumann-calculix - *partitioned-heat-conduction_dirichlet-fenics_neumann-fenics + - *partitioned-heat-conduction_dirichlet-fenicsx_neumann-fenicsx - *partitioned-heat-conduction_dirichlet-openfoam_neumann-openfoam - *partitioned-heat-conduction-complex_dirichlet-fenics_neumann-fenics - *partitioned-heat-conduction-direct_dirichlet-nutils_neumann-nutils @@ -557,6 +572,7 @@ test_suites: - *channel-transport_fluid-nutils_transport-nutils - *partitioned-heat-conduction_dirichlet-nutils_neumann-nutils - *perpendicular-flap_fluid-nutils_solid-calculix + - *perpendicular-flap_fluid-openfoam_solid-nutils - *turek-hron-fsi3_fluid-nutils_solid-nutils expected-to-fail: @@ -611,6 +627,11 @@ test_suites: # Excluded: # *elastic-tube-3d_fluid-openfoam_solid-fenics # too small values to compare + fenicsx-adapter: + tutorials: + - *flow-over-heated-plate_fluid-openfoam_solid-fenicsx + - *partitioned-heat-conduction_dirichlet-fenicsx_neumann-fenicsx + fmi-runner: tutorials: - *oscillator_mass-left-fmi_mass-right-fmi From 87579cff9c2d14a1ea99c4350999938695057593 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Fri, 12 Jun 2026 18:31:21 +0200 Subject: [PATCH 79/94] Add getting FEniCSx adapter latest component to the CI workflows Follow-up of https://github.com/precice/tutorials/pull/832 --- .../workflows/system-tests-latest-components.yml | 16 ++++++++++++++++ .github/workflows/system-tests-pr.yml | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/.github/workflows/system-tests-latest-components.yml b/.github/workflows/system-tests-latest-components.yml index a6c7b1fe1..94e407abb 100644 --- a/.github/workflows/system-tests-latest-components.yml +++ b/.github/workflows/system-tests-latest-components.yml @@ -26,6 +26,7 @@ jobs: ref-calculix-adapter: ${{ steps.ref-calculix-adapter.outputs.shorthash }} ref-dumux-adapter: ${{ steps.ref-dumux-adapter.outputs.shorthash }} ref-fenics-adapter: ${{ steps.ref-fenics-adapter.outputs.shorthash }} + ref-fenicsx-adapter: ${{ steps.ref-fenicsx-adapter.outputs.shorthash }} ref-micro-manager: ${{ steps.ref-micro-manager.outputs.shorthash }} ref-openfoam-adapter: ${{ steps.ref-openfoam-adapter.outputs.shorthash }} ref-su2-adapter: ${{ steps.ref-su2-adapter.outputs.shorthash }} @@ -66,6 +67,13 @@ jobs: owner: precice repo: fenics-adapter branch: develop + - id: ref-fenicsx-adapter + name: Get FEniCSx adapter ref + uses: nmbgeek/github-action-get-latest-commit@main + with: + owner: precice + repo: fenicsx-adapter + branch: develop - id: ref-micro-manager name: Get Micro-Manager ref uses: nmbgeek/github-action-get-latest-commit@main @@ -102,6 +110,7 @@ jobs: printf 'CalculiX adapter: ${{ steps.ref-calculix-adapter.outputs.shorthash }}\n ${{ steps.ref-calculix-adapter.outputs.description }}\n----------\n' printf 'DuMux adapter: ${{ steps.ref-dumux-adapter.outputs.shorthash }}\n ${{ steps.ref-dumux-adapter.outputs.description }}\n----------\n' printf 'FEniCS adapter: ${{ steps.ref-fenics-adapter.outputs.shorthash }}\n ${{ steps.ref-fenics-adapter.outputs.description }}\n----------\n' + printf 'FEniCSx adapter: ${{ steps.ref-fenicsx-adapter.outputs.shorthash }}\n ${{ steps.ref-fenicsx-adapter.outputs.description }}\n----------\n' printf 'Micro-Manager: ${{ steps.ref-micro-manager.outputs.shorthash }}\n ${{ steps.ref-micro-manager.outputs.description }}\n----------\n' printf 'OpenFOAM adapter: ${{ steps.ref-openfoam-adapter.outputs.shorthash }} ${{ steps.ref-openfoam-adapter.outputs.description }}\n----------\n' printf 'SU2 adapter: ${{ steps.ref-su2-adapter.outputs.shorthash }}\n ${{ steps.ref-su2-adapter.outputs.description }}\n----------\n' @@ -141,6 +150,12 @@ jobs: echo "\`\`\`" echo "${{ steps.ref-fenics-adapter.outputs.description }}" echo "\`\`\`" + echo "### FEniCSx adapter" + echo "Reference: [\`${{ steps.ref-fenicsx-adapter.outputs.shorthash }}\`](https://github.com/precice/fenics-adapter/commit/${{ steps.ref-fenicsx-adapter.outputs.shorthash }})" + echo "Description:" + echo "\`\`\`" + echo "${{ steps.ref-fenicsx-adapter.outputs.description }}" + echo "\`\`\`" echo "### Micro-Manager" echo "Reference: [\`${{ steps.ref-micro-manager.outputs.shorthash }}\`](https://github.com/precice/micro-manager/commit/${{ steps.ref-micro-manager.outputs.shorthash }})" echo "Description:" @@ -179,6 +194,7 @@ jobs: CALCULIX_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-calculix-adapter }},\ DUMUX_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-dumux-adapter }},\ FENICS_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-fenics-adapter }},\ + FENICSX_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-fenicsx-adapter }},\ MICRO_MANAGER_REF:${{ needs.gather-refs.outputs.ref-micro-manager }},\ OPENFOAM_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-openfoam-adapter }},\ SU2_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-su2-adapter }},\ diff --git a/.github/workflows/system-tests-pr.yml b/.github/workflows/system-tests-pr.yml index 27361db36..3af5fc451 100644 --- a/.github/workflows/system-tests-pr.yml +++ b/.github/workflows/system-tests-pr.yml @@ -15,6 +15,7 @@ jobs: ref-calculix-adapter: ${{ steps.ref-calculix-adapter.outputs.shorthash }} ref-dumux-adapter: ${{ steps.ref-dumux-adapter.outputs.shorthash }} ref-fenics-adapter: ${{ steps.ref-fenics-adapter.outputs.shorthash }} + ref-fenicsx-adapter: ${{ steps.ref-fenicsx-adapter.outputs.shorthash }} ref-micro-manager: ${{ steps.ref-micro-manager.outputs.shorthash }} ref-openfoam-adapter: ${{ steps.ref-openfoam-adapter.outputs.shorthash }} ref-su2-adapter: ${{ steps.ref-su2-adapter.outputs.shorthash }} @@ -54,6 +55,13 @@ jobs: owner: precice repo: fenics-adapter branch: develop + - id: ref-fenicsx-adapter + name: Get FEniCSx adapter ref + uses: nmbgeek/github-action-get-latest-commit@main + with: + owner: precice + repo: fenicsx-adapter + branch: develop - id: ref-micro-manager name: Get Micro-Manager ref uses: nmbgeek/github-action-get-latest-commit@main @@ -83,6 +91,7 @@ jobs: printf 'CalculiX adapter: ${{ steps.ref-calculix-adapter.outputs.shorthash }}\n ${{ steps.ref-calculix-adapter.outputs.description }}\n----------\n' printf 'DuMux adapter: ${{ steps.ref-dumux-adapter.outputs.shorthash }}\n ${{ steps.ref-dumux-adapter.outputs.description }}\n----------\n' printf 'FEniCS adapter: ${{ steps.ref-fenics-adapter.outputs.shorthash }}\n ${{ steps.ref-fenics-adapter.outputs.description }}\n----------\n' + printf 'FEniCSx adapter: ${{ steps.ref-fenicsx-adapter.outputs.shorthash }}\n ${{ steps.ref-fenicsx-adapter.outputs.description }}\n----------\n' printf 'Micro-Manager: ${{ steps.ref-micro-manager.outputs.shorthash }}\n ${{ steps.ref-micro-manager.outputs.description }}\n----------\n' printf 'OpenFOAM adapter: ${{ steps.ref-openfoam-adapter.outputs.shorthash }} ${{ steps.ref-openfoam-adapter.outputs.description }}\n----------\n' printf 'SU2 adapter: ${{ steps.ref-su2-adapter.outputs.shorthash }}\n ${{ steps.ref-su2-adapter.outputs.description }}\n----------\n' @@ -121,6 +130,12 @@ jobs: echo "\`\`\`" echo "${{ steps.ref-fenics-adapter.outputs.description }}" echo "\`\`\`" + echo "### FEniCSx adapter" + echo "Reference: [\`${{ steps.ref-fenicsx-adapter.outputs.shorthash }}\`](https://github.com/precice/fenicsx-adapter/commit/${{ steps.ref-fenicsx-adapter.outputs.shorthash }})" + echo "Description:" + echo "\`\`\`" + echo "${{ steps.ref-fenicsx-adapter.outputs.description }}" + echo "\`\`\`" echo "### Micro-Manager" echo "Reference: [\`${{ steps.ref-micro-manager.outputs.shorthash }}\`](https://github.com/precice/micro-manager/commit/${{ steps.ref-micro-manager.outputs.shorthash }})" echo "Description:" @@ -157,6 +172,7 @@ jobs: CALCULIX_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-calculix-adapter }},\ DUMUX_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-dumux-adapter }},\ FENICS_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-fenics-adapter }},\ + FENICSX_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-fenicsx-adapter }},\ MICRO_MANAGER_REF:${{ needs.gather-refs.outputs.ref-micro-manager }},\ OPENFOAM_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-openfoam-adapter }},\ SU2_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-su2-adapter }},\ From b8d5b3537a797432552aceec1441c9aace095ba4 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Fri, 12 Jun 2026 19:39:58 +0200 Subject: [PATCH 80/94] Always upload the full archives when generating reference results --- .github/workflows/generate_reference_results_workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate_reference_results_workflow.yml b/.github/workflows/generate_reference_results_workflow.yml index 2a61bb7cc..48dfd62ee 100644 --- a/.github/workflows/generate_reference_results_workflow.yml +++ b/.github/workflows/generate_reference_results_workflow.yml @@ -103,7 +103,7 @@ jobs: runs/*/system-tests-compare.log runs/*/*/system-tests_*.log - name: Archive run files - if: failure() + if: ${{ always() }} uses: actions/upload-artifact@v7 with: name: system_tests_run_${{ github.run_id }}_${{ github.run_attempt }}_reference_full From 30371bafa85890cfb2d9086dc3b16cf18bf70d47 Mon Sep 17 00:00:00 2001 From: NiklasV <139122850+NiklasVin@users.noreply.github.com> Date: Fri, 12 Jun 2026 20:44:31 +0200 Subject: [PATCH 81/94] Add 3D case for partitioned heat conduction (#714) --- changelog-entries/714.md | 1 + partitioned-heat-conduction-3d/README.md | 57 ++++ .../clean-tutorial.sh | 10 + .../dirichlet-fenicsx/clean.sh | 6 + .../precice-adapter-config-D.json | 19 ++ .../dirichlet-fenicsx/run.sh | 15 + ...oned-heat-conduction-3d-precice-config.png | Bin 0 -> 81894 bytes ...s-partitioned-heat-conduction-3d-setup.png | Bin 0 -> 26816 bytes partitioned-heat-conduction-3d/metadata.yaml | 20 ++ .../neumann-fenicsx/clean.sh | 6 + .../precice-adapter-config-N.json | 19 ++ .../neumann-fenicsx/run.sh | 15 + .../precice-config.xml | 80 +++++ .../solver-fenicsx/clean.sh | 6 + .../solver-fenicsx/errorcomputation.py | 21 ++ .../solver-fenicsx/heat.py | 311 ++++++++++++++++++ .../solver-fenicsx/my_enums.py | 17 + .../solver-fenicsx/problem_setup.py | 131 ++++++++ .../solver-fenicsx/requirements.txt | 5 + .../solver-fenicsx/run.sh | 28 ++ .../tests/dockerfiles/ubuntu_2404/Dockerfile | 12 +- tools/tests/tests.yaml | 12 + 22 files changed, 787 insertions(+), 4 deletions(-) create mode 100644 changelog-entries/714.md create mode 100644 partitioned-heat-conduction-3d/README.md create mode 100755 partitioned-heat-conduction-3d/clean-tutorial.sh create mode 100755 partitioned-heat-conduction-3d/dirichlet-fenicsx/clean.sh create mode 100644 partitioned-heat-conduction-3d/dirichlet-fenicsx/precice-adapter-config-D.json create mode 100755 partitioned-heat-conduction-3d/dirichlet-fenicsx/run.sh create mode 100644 partitioned-heat-conduction-3d/images/tutorials-partitioned-heat-conduction-3d-precice-config.png create mode 100644 partitioned-heat-conduction-3d/images/tutorials-partitioned-heat-conduction-3d-setup.png create mode 100644 partitioned-heat-conduction-3d/metadata.yaml create mode 100755 partitioned-heat-conduction-3d/neumann-fenicsx/clean.sh create mode 100644 partitioned-heat-conduction-3d/neumann-fenicsx/precice-adapter-config-N.json create mode 100755 partitioned-heat-conduction-3d/neumann-fenicsx/run.sh create mode 100644 partitioned-heat-conduction-3d/precice-config.xml create mode 100755 partitioned-heat-conduction-3d/solver-fenicsx/clean.sh create mode 100644 partitioned-heat-conduction-3d/solver-fenicsx/errorcomputation.py create mode 100644 partitioned-heat-conduction-3d/solver-fenicsx/heat.py create mode 100644 partitioned-heat-conduction-3d/solver-fenicsx/my_enums.py create mode 100644 partitioned-heat-conduction-3d/solver-fenicsx/problem_setup.py create mode 100644 partitioned-heat-conduction-3d/solver-fenicsx/requirements.txt create mode 100755 partitioned-heat-conduction-3d/solver-fenicsx/run.sh diff --git a/changelog-entries/714.md b/changelog-entries/714.md new file mode 100644 index 000000000..f1f0fc71c --- /dev/null +++ b/changelog-entries/714.md @@ -0,0 +1 @@ +- Added a 3D partitioned heat conduction tutorial with FEniCSx [#714](https://github.com/precice/tutorials/pull/714) \ No newline at end of file diff --git a/partitioned-heat-conduction-3d/README.md b/partitioned-heat-conduction-3d/README.md new file mode 100644 index 000000000..eac1d6d56 --- /dev/null +++ b/partitioned-heat-conduction-3d/README.md @@ -0,0 +1,57 @@ +--- +title: 3D partitioned heat conduction +permalink: tutorials-partitioned-heat-conduction-3d.html +keywords: FEniCSx, Heat conduction +summary: We solve a simple heat equation on a 3D domain. The domain is partitioned and the coupling is established in a Dirichlet-Neumann fashion. +--- + +{% note %} +Get the [case files of this tutorial](https://github.com/precice/tutorials/tree/develop/partitioned-heat-conduction-3d), as continuously rendered here, or see the [latest released version](https://github.com/precice/tutorials/tree/master/partitioned-heat-conduction-3d) (if there is already one). Read how in the [tutorials introduction](https://precice.org/tutorials.html). +{% endnote %} + +## Setup + +We solve a partitioned heat equation. For information on the two dimensional non-partitioned case, please refer to [1, p.37ff]. In this tutorial the computational domain is partitioned and coupled via preCICE. The coupling roughly follows the approach described in [2]. + +![Case setup of partitioned-heat-conduction case](images/tutorials-partitioned-heat-conduction-3d-setup.png) + +The computational domain can be seen in the picture above. To get a better overview of the domain, the domains of the participants are translated. +The domain of the Neumann participant is the small box, and the Dirichlet participant's domain is the rest. In the simulation, the small box is fully contained in the Dirichlet domain. This means, the coupling interface consists of five sides of the box. + +## Configuration + +preCICE configuration (image generated using the [precice-config-visualizer](https://precice.org/tooling-config-visualization.html)): + +![preCICE configuration visualization](images/tutorials-partitioned-heat-conduction-3d-precice-config.png) + +## Available solvers and dependencies + +You can either couple a solver with itself or different solvers with each other. In any case you will need to have preCICE and the python bindings installed on your system. + +* FEniCSx. Install [FEniCS](https://fenicsproject.org/download/) and the [FEniCSx-adapter](https://github.com/precice/fenicsx-adapter). The code is adapted from the existing [fenics-tutorial](https://github.com/hplgit/fenics-tutorial/blob/master/pub/python/vol1/ft03_heat.py) from [1]. + +## Running the simulation + +You can find the corresponding `run.sh` script for running the case in the folders corresponding to the participant you want to use: + +```bash +cd dirichlet-fenicsx +./run.sh +``` + +and + +```bash +cd neumann-fenicsx +./run.sh +``` + +## Visualization + +Output is written into the folders of the FEniCSx solvers (`neumann-fenicsx/output-neumann.bp` and `dirichlet-fenicsx/output-dirichlet.bp`). + +It is sufficient to import the folders to ParaView to get the visualization of the simulation. + +## References + +[1] Hans Petter Langtangen and Anders Logg. "Solving PDEs in Minutes-The FEniCS Tutorial Volume I." (2016). [pdf](https://fenicsproject.org/pub/tutorial/pdf/fenics-tutorial-vol1.pdf) diff --git a/partitioned-heat-conduction-3d/clean-tutorial.sh b/partitioned-heat-conduction-3d/clean-tutorial.sh new file mode 100755 index 000000000..56e40d689 --- /dev/null +++ b/partitioned-heat-conduction-3d/clean-tutorial.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env sh +set -e -u + +# shellcheck disable=SC1091 +. ../tools/cleaning-tools.sh + +clean_tutorial . +clean_precice_logs . +rm -fv ./*.log + diff --git a/partitioned-heat-conduction-3d/dirichlet-fenicsx/clean.sh b/partitioned-heat-conduction-3d/dirichlet-fenicsx/clean.sh new file mode 100755 index 000000000..46529f8d7 --- /dev/null +++ b/partitioned-heat-conduction-3d/dirichlet-fenicsx/clean.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env sh +set -e -u + +. ../../tools/cleaning-tools.sh + +clean_fenicsx . diff --git a/partitioned-heat-conduction-3d/dirichlet-fenicsx/precice-adapter-config-D.json b/partitioned-heat-conduction-3d/dirichlet-fenicsx/precice-adapter-config-D.json new file mode 100644 index 000000000..5976848e0 --- /dev/null +++ b/partitioned-heat-conduction-3d/dirichlet-fenicsx/precice-adapter-config-D.json @@ -0,0 +1,19 @@ +{ + "participant_name": "Dirichlet", + "precice_config_file_path": "../precice-config.xml", + "interfaces": [ + { + "mesh_name": "Dirichlet-Mesh", + "write_data": [ + { + "name": "Heat-Flux" + } + ], + "read_data": [ + { + "name": "Temperature" + } + ] + } + ] +} \ No newline at end of file diff --git a/partitioned-heat-conduction-3d/dirichlet-fenicsx/run.sh b/partitioned-heat-conduction-3d/dirichlet-fenicsx/run.sh new file mode 100755 index 000000000..0193545c4 --- /dev/null +++ b/partitioned-heat-conduction-3d/dirichlet-fenicsx/run.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -e -u + +if [ ! -v PRECICE_TUTORIALS_NO_VENV ] +then + if [ ! -d ".venv" ]; then + python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r ../solver-fenicsx/requirements.txt + else + source .venv/bin/activate + fi +fi + +python3 ../solver-fenicsx/heat.py Dirichlet --error-tol 10e-3 diff --git a/partitioned-heat-conduction-3d/images/tutorials-partitioned-heat-conduction-3d-precice-config.png b/partitioned-heat-conduction-3d/images/tutorials-partitioned-heat-conduction-3d-precice-config.png new file mode 100644 index 0000000000000000000000000000000000000000..bd9a35c7177015cfc801273b71154fef863e132c GIT binary patch literal 81894 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 literal 0 HcmV?d00001 diff --git a/partitioned-heat-conduction-3d/images/tutorials-partitioned-heat-conduction-3d-setup.png b/partitioned-heat-conduction-3d/images/tutorials-partitioned-heat-conduction-3d-setup.png new file mode 100644 index 0000000000000000000000000000000000000000..70a1d12df3a7805f5e824c15263a235627cc64a7 GIT binary patch literal 26816 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 F{|Dl-6}|uf literal 0 HcmV?d00001 diff --git a/partitioned-heat-conduction-3d/metadata.yaml b/partitioned-heat-conduction-3d/metadata.yaml new file mode 100644 index 000000000..df957913f --- /dev/null +++ b/partitioned-heat-conduction-3d/metadata.yaml @@ -0,0 +1,20 @@ +name: Partitioned heat conduction 3D +path: partitioned-heat-conduction-3d # relative to git repo +url: https://precice.org/tutorials-partitioned-heat-conduction-3d.html + +participants: + - Dirichlet + - Neumann + +cases: + dirichlet-fenicsx: + participant: Dirichlet + directory: ./dirichlet-fenicsx + run: ./run.sh + component: fenicsx-adapter + + neumann-fenicsx: + participant: Neumann + directory: ./neumann-fenicsx + run: ./run.sh + component: fenicsx-adapter diff --git a/partitioned-heat-conduction-3d/neumann-fenicsx/clean.sh b/partitioned-heat-conduction-3d/neumann-fenicsx/clean.sh new file mode 100755 index 000000000..46529f8d7 --- /dev/null +++ b/partitioned-heat-conduction-3d/neumann-fenicsx/clean.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env sh +set -e -u + +. ../../tools/cleaning-tools.sh + +clean_fenicsx . diff --git a/partitioned-heat-conduction-3d/neumann-fenicsx/precice-adapter-config-N.json b/partitioned-heat-conduction-3d/neumann-fenicsx/precice-adapter-config-N.json new file mode 100644 index 000000000..dffe0e5e9 --- /dev/null +++ b/partitioned-heat-conduction-3d/neumann-fenicsx/precice-adapter-config-N.json @@ -0,0 +1,19 @@ +{ + "participant_name": "Neumann", + "precice_config_file_path": "../precice-config.xml", + "interfaces": [ + { + "mesh_name": "Neumann-Mesh", + "write_data": [ + { + "name": "Temperature" + } + ], + "read_data": [ + { + "name": "Heat-Flux" + } + ] + } + ] +} \ No newline at end of file diff --git a/partitioned-heat-conduction-3d/neumann-fenicsx/run.sh b/partitioned-heat-conduction-3d/neumann-fenicsx/run.sh new file mode 100755 index 000000000..21e98e115 --- /dev/null +++ b/partitioned-heat-conduction-3d/neumann-fenicsx/run.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -e -u + +if [ ! -v PRECICE_TUTORIALS_NO_VENV ] +then + if [ ! -d ".venv" ]; then + python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install -r ../solver-fenicsx/requirements.txt + else + source .venv/bin/activate + fi +fi + +python3 ../solver-fenicsx/heat.py Neumann --error-tol 10e-3 diff --git a/partitioned-heat-conduction-3d/precice-config.xml b/partitioned-heat-conduction-3d/precice-config.xml new file mode 100644 index 000000000..d5c8ffdd6 --- /dev/null +++ b/partitioned-heat-conduction-3d/precice-config.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/partitioned-heat-conduction-3d/solver-fenicsx/clean.sh b/partitioned-heat-conduction-3d/solver-fenicsx/clean.sh new file mode 100755 index 000000000..bdd9995e1 --- /dev/null +++ b/partitioned-heat-conduction-3d/solver-fenicsx/clean.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -e -u + +. ../../tools/cleaning-tools.sh + +clean_fenicsx . diff --git a/partitioned-heat-conduction-3d/solver-fenicsx/errorcomputation.py b/partitioned-heat-conduction-3d/solver-fenicsx/errorcomputation.py new file mode 100644 index 000000000..c3d239216 --- /dev/null +++ b/partitioned-heat-conduction-3d/solver-fenicsx/errorcomputation.py @@ -0,0 +1,21 @@ +from dolfinx import fem +import numpy as np +from mpi4py import MPI +import ufl + + +def compute_errors(u_approx, u_ref, total_error_tol=10 ** -7): + mesh = u_ref.function_space.mesh + # Compute L2 error and error at nodes + error_L2 = np.sqrt(mesh.comm.allreduce(fem.assemble_scalar(fem.form((u_approx - u_ref)**2 * ufl.dx)), op=MPI.SUM)) + if mesh.comm.rank == 0: + print(f"L2-error: {error_L2:.2e}") + + # Compute values at mesh vertices + error_max = mesh.comm.allreduce(np.max(np.abs(u_approx.x.array - u_ref.x.array)), op=MPI.MAX) + if mesh.comm.rank == 0: + print(f"Error_max: {error_max:.2e}") + + assert (error_L2 < total_error_tol) + + return (error_L2, error_max) diff --git a/partitioned-heat-conduction-3d/solver-fenicsx/heat.py b/partitioned-heat-conduction-3d/solver-fenicsx/heat.py new file mode 100644 index 000000000..310e164ae --- /dev/null +++ b/partitioned-heat-conduction-3d/solver-fenicsx/heat.py @@ -0,0 +1,311 @@ +""" +The basic example is taken from "Langtangen, Hans Petter, and Anders Logg. Solving PDEs in Python: The FEniCS +Tutorial I. Springer International Publishing, 2016." + +The example code has been extended with preCICE API calls, mixed boundary conditions to allow for a Dirichlet-Neumann +coupling of two separate heat equations and the domain has been changed to three dimensions. +It also has been adapted to be compatible with FEniCSx. + +The original source code can be found on https://jsdokken.com/dolfinx-tutorial/chapter2/heat_equation.html. + +Heat equation with Dirichlet conditions. (Dirichlet problem) + u'= Laplace(u) + f in the outer domain + u = u_C on the coupling boundary + u = u_D on the remaining boundary + u = u_0 at t = 0 + u = 1 + x^2 + alpha*y^2 + \beta*t + f = beta - 2 - 2*alpha + +Heat equation with mixed boundary conditions. (Neumann problem) + u'= Laplace(u) + f in the inner domain + du/dn = f_N on the coupling boundary + u = u_D on the remaining boundary + u = u_0 at t = 0 + u = 1 + x^2 + alpha*y^2 + \beta*t + f = beta - 2 - 2*alpha +""" +import argparse +import numpy as np +from mpi4py import MPI +import basix.ufl +from petsc4py import PETSc +import ufl +from dolfinx import fem, io +from dolfinx.fem.petsc import assemble_matrix, assemble_vector, apply_lifting, create_vector, set_bc +import basix +from fenicsxprecice import Adapter, CouplingMesh +from errorcomputation import compute_errors +from my_enums import ProblemType, DomainPart +from problem_setup import get_geometry + + +class GradientSolver: + """ + compute flux following http://hplgit.github.io/INF5620/doc/pub/fenics_tutorial1.1/tu2.html#tut-poisson-gradu + The solver has been changed since the original version from the link above introduces larger errors + + :param V_g: Vector function space + :param u: solution where gradient is to be determined + """ + + def __init__(self, domain, V_g): + self.domain = domain, + self.V_g = V_g + + w = ufl.TrialFunction(V_g) + self.v = ufl.TestFunction(V_g) + a = fem.form(ufl.inner(w, self.v) * ufl.dx) + self.A = assemble_matrix(a) + self.A.assemble() + + self.solver = PETSc.KSP().create(domain.comm) + self.solver.setOperators(self.A) + self.solver.setType(PETSc.KSP.Type.PREONLY) + self.solver.getPC().setType(PETSc.PC.Type.LU) + + self.returnValue = fem.Function(V_g) + + def compute(self, u): + L = fem.form(ufl.inner(ufl.grad(u), self.v) * ufl.dx) + b = create_vector(fem.extract_function_spaces(L)) + assemble_vector(b, L) + # In the assembly above, ranks did not communicate and therefore, the cells have incorrect values + # To resolve this, the values in the ghost region must be added to the values from the owner + b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE) + self.solver.solve(b, self.returnValue.x.petsc_vec) + return self.returnValue + + +# Parse arguments +parser = argparse.ArgumentParser(description="Solving heat equation for simple or complex interface case") +parser.add_argument("participantName", help="Name of the solver.", type=str, choices=[p.value for p in ProblemType]) +parser.add_argument("-e", "--error-tol", help="set error tolerance", type=float, default=10**-8,) +args = parser.parse_args() +# Init variables with arguments +participant_name = args.participantName +error_tol = args.error_tol + +comm = MPI.COMM_WORLD +rank = comm.Get_rank() + +t = 0 +fenics_dt = 0.1 +alpha = 3 +beta = .25 +gamma = 1.2 + +# define the domain +if participant_name == ProblemType.DIRICHLET.value: + problem = ProblemType.DIRICHLET + domain_part = DomainPart.OUTER +elif participant_name == ProblemType.NEUMANN.value: + problem = ProblemType.NEUMANN + domain_part = DomainPart.INNER + +# create domain and function space +domain, coupling_boundary, remaining_boundary = get_geometry(domain_part, comm) +V = fem.functionspace(domain, ("Lagrange", 2)) +element = basix.ufl.element("Lagrange", domain.topology.cell_name(), 1, shape=(domain.geometry.dim,)) +V_g = fem.functionspace(domain, element) +V_coup = None +if problem is ProblemType.DIRICHLET: + V_coup = V +else: + V_coup = V_g + + +class exact_solution(): + # Define the exact solution + def __init__(self, alpha, beta, gamma, t): + self.alpha = alpha + self.beta = beta + self.gamma = gamma + self.t = t + + def __call__(self, x): + return 1 + x[0]**2 + self.alpha * x[1]**2 + self.beta * x[2]**2 + self.gamma * self.t + + +u_exact = exact_solution(alpha, beta, gamma, t) + +gradient_solver = GradientSolver(domain, V_g) + +# Define the boundary condition +bcs = [] +u_D = fem.Function(V) +u_D.interpolate(u_exact) +tdim = domain.topology.dim +fdim = tdim - 1 +domain.topology.create_connectivity(fdim, tdim) +# dofs for the remaining boundary. Can be directly set to u_D +dofs_remaining = fem.locate_dofs_geometrical(V, remaining_boundary) +bc_D = fem.dirichletbc(u_D, dofs_remaining) +bcs.append(bc_D) + +# dofs (and the corresponding coordinates) for the coupling boundary (coupling function space) +# this is later used to read data from preCICE and update boundary condition +dofs_coupling = fem.locate_dofs_geometrical(V_coup, coupling_boundary) +dofs_coupling_coordinates = V_coup.tabulate_dof_coordinates()[dofs_coupling] + +if problem is ProblemType.DIRICHLET: + f_N = fem.Function(V_g) + f_N.interpolate(gradient_solver.compute(u_D)) + +u_n = fem.Function(V) # IV and solution u for the n-th time step +u_n.interpolate(u_exact) + +# initialise precice +precice, precice_dt, initial_data = None, 0.0, None +if problem is ProblemType.DIRICHLET: + precice = Adapter(adapter_config_filename="precice-adapter-config-D.json", mpi_comm=comm) +else: + precice = Adapter(adapter_config_filename="precice-adapter-config-N.json", mpi_comm=comm, digit_cutoff=12) + +coupling_mesh = None +if problem is ProblemType.DIRICHLET: + coupling_mesh = CouplingMesh("Dirichlet-Mesh", coupling_boundary, {"Temperature": V_coup}, {"Heat-Flux": f_N}) + precice.initialize([coupling_mesh]) +elif problem is ProblemType.NEUMANN: + coupling_mesh = CouplingMesh("Neumann-Mesh", coupling_boundary, {"Heat-Flux": V_coup}, {"Temperature": u_D}) + precice.initialize([coupling_mesh]) + +# get precice's dt +precice_dt = precice.get_max_time_step_size() +dt = np.min([fenics_dt, precice_dt]) + + +# Define the variational formualation +# As $f$ is a constant independent of $t$, we can define it as a constant. +f = fem.Constant(domain, gamma - 2 - 2 * alpha - 2 * beta) +# We can now create our variational formulation, with the bilinear form `a` and linear form `L`. +u, v = ufl.TrialFunction(V), ufl.TestFunction(V) +F = u * v * ufl.dx + dt * ufl.dot(ufl.grad(u), ufl.grad(v)) * ufl.dx - (u_n + dt * f) * v * ufl.dx + +name_read = None +if problem is ProblemType.DIRICHLET: + name_read = "Temperature" + read_mesh = "Dirichlet-Mesh" +else: + name_read = "Heat-Flux" + read_mesh = "Neumann-Mesh" + +coupling_function = fem.Function(V_coup) + +if problem is ProblemType.DIRICHLET: + # modify Dirichlet boundary condition on coupling interface + bc_coup = fem.dirichletbc(coupling_function, dofs_coupling) + bcs.append(bc_coup) +if problem is ProblemType.NEUMANN: + # modify Neumann boundary condition on coupling interface, modify weak + # form correspondingly + n = ufl.FacetNormal(domain) + F -= dt * ufl.inner(coupling_function, n) * v * ufl.ds + coupling_function.interpolate(gradient_solver.compute(u_D)) +a = fem.form(ufl.lhs(F)) +L = fem.form(ufl.rhs(F)) + +# ## Create the matrix and vector for the linear problem +# To ensure that we are solving the variational problem efficiently, we +# will create several structures which can reuse data, such as matrix +# sparisty patterns. Especially note as the bilinear form `a` is +# independent of time, we only need to assemble the matrix once. +A = assemble_matrix(a, bcs=bcs) +A.assemble() +b = create_vector(fem.extract_function_spaces(L)) +uh = fem.Function(V) + +# ## Define a linear variational solver +# We will use [PETSc](https://www.mcs.anl.gov/petsc/) to solve the +# resulting linear algebra problem. We use the Python-API `petsc4py` to +# define the solver. We will use a linear solver. +solver = PETSc.KSP().create(domain.comm) +solver.setOperators(A) +solver.setType(PETSc.KSP.Type.PREONLY) +solver.getPC().setType(PETSc.PC.Type.LU) + +if problem is ProblemType.DIRICHLET: + flux = fem.Function(V_g) + +# boundaries point as always to the end of the timestep +u_exact.t += dt +u_D.interpolate(u_exact) + +f_err = fem.Function(V) +# create writer for output files +vtxwriter = io.VTXWriter(MPI.COMM_WORLD, f"output-{problem.name}.bp".lower(), [f_err]) +vtxwriter.write(t) + +if problem is ProblemType.NEUMANN: + mappingToOriginalSpace = None + dofs_coupling_coordinates = None + Tmp, mappingToOriginalSpace = V_coup.sub(0).collapse() + dofs_subspace = fem.locate_dofs_geometrical(Tmp, coupling_boundary) + # to get the vector dof's (true) coordinates, it is sufficient to use the dof's coordinates of an arbitrary subspace + dofs_coupling_coordinates = V_coup.tabulate_dof_coordinates()[dofs_subspace] + +while precice.is_coupling_ongoing(): + + if precice.requires_writing_checkpoint(): + precice.store_checkpoint(u_n, t, 0) + + precice_dt = precice.get_max_time_step_size() + dt = np.min([fenics_dt, precice_dt]) + + # Update the coupling expression with the new read data + precice.read_data(read_mesh, name_read, dt, coupling_function) + + # Update the right hand side reusing the initial vector + with b.localForm() as loc_b: + loc_b.set(0) + assemble_vector(b, L) + + # Apply Dirichlet boundary condition to the vector (according to the tutorial, the lifting operation is used to preserve the symmetry of the matrix) + # Boundary condition bc should be updated by u_D.interpolate above, since + # this function is wrapped into the bc object + apply_lifting(b, [a], [bcs]) + b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE) + set_bc(b, bcs) + + # Solve linear problem + solver.solve(b, uh.x.petsc_vec) + uh.x.scatter_forward() + + # Write data to preCICE according to which problem is being solved + if problem is ProblemType.DIRICHLET: + # Dirichlet problem reads temperature and writes flux on boundary to Neumann problem + flux = gradient_solver.compute(uh) + flux.x.scatter_forward() + precice.write_data(coupling_mesh.get_name(), "Heat-Flux", flux) + elif problem is ProblemType.NEUMANN: + # Neumann problem reads flux and writes temperature on boundary to Dirichlet problem + precice.write_data(coupling_mesh.get_name(), "Temperature", uh) + + precice.advance(dt) + precice_dt = precice.get_max_time_step_size() + + # roll back to checkpoint + if precice.requires_reading_checkpoint(): + u_cp, t_cp, _ = precice.retrieve_checkpoint() + u_n.x.array[:] = u_cp.x.array + t = t_cp + else: # update solution + # Update solution at previous time step (u_n) + u_n.x.array[:] = uh.x.array + f_err.x.array[:] = np.abs(u_n.x.array - u_D.x.array) + t += float(dt) + vtxwriter.write(t) + + if precice.is_time_window_complete(): + u_ref = fem.Function(V) + u_ref.interpolate(u_D) + error, error_pointwise = compute_errors(u_n, u_ref, 1e-4) + print("t = %.2f: L2 error on domain = %.3g" % (t, error)) + + # Update Dirichlet BC + u_exact.t += dt + u_D.interpolate(u_exact) + # TODO: update time dependent f (as soon as it is time dependent)! + +precice.finalize() + +vtxwriter.close() diff --git a/partitioned-heat-conduction-3d/solver-fenicsx/my_enums.py b/partitioned-heat-conduction-3d/solver-fenicsx/my_enums.py new file mode 100644 index 000000000..666ae348e --- /dev/null +++ b/partitioned-heat-conduction-3d/solver-fenicsx/my_enums.py @@ -0,0 +1,17 @@ +from enum import Enum + + +class ProblemType(Enum): + """ + Enum defines problem type. Details see above. + """ + DIRICHLET = "Dirichlet" # Dirichlet problem + NEUMANN = "Neumann" # Neumann problem + + +class DomainPart(Enum): + """ + Enum defines which part of the domain [x_left, x_right] x [y_bottom, y_top] we compute. + """ + OUTER = 1 # left part of domain in simple interface case + INNER = 2 # right part of domain in simple interface case diff --git a/partitioned-heat-conduction-3d/solver-fenicsx/problem_setup.py b/partitioned-heat-conduction-3d/solver-fenicsx/problem_setup.py new file mode 100644 index 000000000..35719f452 --- /dev/null +++ b/partitioned-heat-conduction-3d/solver-fenicsx/problem_setup.py @@ -0,0 +1,131 @@ +""" +Problem setup for partitioned-heat-conduction/fenicsx tutorial +""" +from dolfinx.io import gmsh as gmshio +from dolfinx.mesh import DiagonalType, create_box +import dolfinx.mesh +from my_enums import DomainPart +import numpy as np +from mpi4py import MPI +import gmsh + + +def get_geometry(domain_part, communicator): + gmsh.initialize() + gmsh.model.add("HeatMesh") + + if domain_part is DomainPart.OUTER: + # In this section the outer box of the domain is defined. + # It is a cuboid from (0,0,0) to (2,1,2) and has a cuboid cutout from (1, .25, .5) to (2, .75, 1.5) + # The coupling interface is the surface from the cutout. + + # Definition of the coupling boundary + def coupling_bc(x): + tol = 1E-14 + top = np.logical_and.reduce(( + np.isclose(x[1], 0.75, atol=tol), + np.logical_or(x[0] >= 1, np.isclose(x[0], 1, tol)), + np.logical_or(x[2] >= 0.5, np.isclose(x[2], 0.5, tol)), + np.logical_or(x[2] <= 1.5, np.isclose(x[2], 1.5, tol)) + )) + + bot = np.logical_and.reduce(( + np.isclose(x[1], 0.25, tol), + np.logical_or(x[0] >= 1, np.isclose(x[0], 1, tol)), + np.logical_or(x[2] >= 0.5, np.isclose(x[2], 0.5, tol)), + np.logical_or(x[2] <= 1.5, np.isclose(x[2], 1.5, tol)) + )) + + sideCenter = np.logical_and.reduce(( + np.isclose(x[0], 1, tol), + x[1] >= 0.25, + x[1] <= 0.75, + x[2] >= 0.5, + x[2] <= 1.5 + )) + + sideLeft = np.logical_and.reduce(( + x[0] >= 1, + x[1] >= 0.25, + x[1] <= 0.75, + np.isclose(x[2], 0.5, tol) + )) + + sideRight = np.logical_and.reduce(( + x[0] >= 1, + x[1] >= 0.25, + x[1] <= 0.75, + np.isclose(x[2], 1.5, tol) + )) + + return np.logical_or.reduce((top, bot, sideCenter, sideLeft, sideRight)) + + # definition of the boundary excluding the coupling boundary + + def boundary_bc(x): + tol = 1E-14 + or_part = np.logical_or.reduce(( + np.isclose(x[0], 0, tol), + np.isclose(x[0], 2, tol), + np.isclose(x[1], 0, tol), + np.isclose(x[1], 1, tol), + np.isclose(x[2], 0, tol), + np.isclose(x[2], 2, tol) + )) + and_part = np.logical_and.reduce(( + np.isclose(x[0], 2, tol), + x[1] >= 0.25, + x[1] <= 0.75, + x[2] >= 0.5, + x[2] <= 1.5, + + )) + return np.logical_and(or_part, ~and_part) + + # for creating the mesh, gmsh is used + # create full cuboid + gmsh.model.occ.addBox(0, 0, 0, 2, 1, 2, 1) + gmsh.model.occ.synchronize() + # create cuboid for cutout + gmsh.model.occ.addBox(1, 0.25, .5, 1, .5, 1, 2) + gmsh.model.occ.synchronize() + + # remove smaller cuboid from large cuboid + _, _ = gmsh.model.occ.cut([(3, 1)], [(3, 2)], 3, True, True) + gmsh.model.occ.synchronize() + gmsh.model.addPhysicalGroup(3, [3], 4, "foo") + # generate mesh with max. edge length of 0.15 + gmsh.model.mesh.setOrder(2) + gmsh.option.setNumber("Mesh.CharacteristicLengthMax", 0.15) + gmsh.model.mesh.generate(3) + # convert from gmsh to a dolfinx representation + outer_mesh = gmshio.model_to_mesh(gmsh.model, communicator, 0, 3).mesh + gmsh.finalize() + return outer_mesh, coupling_bc, boundary_bc + else: + # inner part of the domain + # it is defined by a cuboid from (1, .25, .5) to (2, .75, 1.5) + + # 5 sides of the cuboid define the coupling interface + def coupling_bc(x): + tol = 1E-14 + return np.logical_or( + np.logical_or(np.isclose(x[1], 0.75, tol), np.isclose(x[1], 0.25, tol)), + np.logical_or(np.isclose(x[0], 1, tol), + np.logical_or(np.isclose(x[2], 0.5, tol), np.isclose(x[2], 1.5, tol)))) + + def boundary_bc(x): + tol = 1E-14 + return np.isclose(x[0], 2, tol) + + # create cuboid + gmsh.model.occ.addBox(1, 0.25, .5, 1, .5, 1, 1) + gmsh.model.occ.synchronize() + gmsh.model.addPhysicalGroup(3, [1], 2, "foo") + # generate mesh + gmsh.model.mesh.setOrder(2) + gmsh.model.mesh.generate(3) + # convert from gmsh to dolfinx representation + inner_mesh = gmshio.model_to_mesh(gmsh.model, communicator, 0, 3).mesh + gmsh.finalize() + return inner_mesh, coupling_bc, boundary_bc diff --git a/partitioned-heat-conduction-3d/solver-fenicsx/requirements.txt b/partitioned-heat-conduction-3d/solver-fenicsx/requirements.txt new file mode 100644 index 000000000..1759af720 --- /dev/null +++ b/partitioned-heat-conduction-3d/solver-fenicsx/requirements.txt @@ -0,0 +1,5 @@ +numpy >1, <2 +fenicsxprecice +scipy +sympy +gmsh diff --git a/partitioned-heat-conduction-3d/solver-fenicsx/run.sh b/partitioned-heat-conduction-3d/solver-fenicsx/run.sh new file mode 100755 index 000000000..731545f5f --- /dev/null +++ b/partitioned-heat-conduction-3d/solver-fenicsx/run.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -e -u + +if [ ! -v PRECICE_TUTORIALS_NO_VENV ] +then + if [ ! -d ".venv" ]; then + python3 -m venv --system-site-packages .venv + source .venv/bin/activate + pip install ../../.. + pip install -r .requirements.txt + else + source .venv/bin/activate + fi +fi + +while getopts ":dn" opt; do + case ${opt} in + d) + python3 heat.py Dirichlet --error-tol 10e-3 + ;; + n) + python3 heat.py Neumann --error-tol 10e-3 + ;; + \?) + echo "Usage: cmd [-d] [-n]" + ;; + esac +done diff --git a/tools/tests/dockerfiles/ubuntu_2404/Dockerfile b/tools/tests/dockerfiles/ubuntu_2404/Dockerfile index dd1397002..69f908045 100644 --- a/tools/tests/dockerfiles/ubuntu_2404/Dockerfile +++ b/tools/tests/dockerfiles/ubuntu_2404/Dockerfile @@ -63,6 +63,14 @@ RUN apt-get -qq update && \ pkg-config \ wget \ inotify-tools +# The following are dependencies of gmsh, needed by some tutorials +RUN apt-get -qq update && \ + apt-get -qq install \ + libglu1-mesa \ + libxrender1 \ + libxcursor1 \ + libxft2 \ + libxinerama1 USER precice FROM precice_dependecies AS precice @@ -141,10 +149,6 @@ RUN python3 -m venv --system-site-packages /home/precice/venv && \ FROM precice_dependecies AS nutils_adapter COPY --from=python_bindings /home/precice/.local /home/precice/.local -USER root -# The following are dependencies of gmsh, needed by the turek-hron-fsi3 nutils participant -RUN apt-get -qq update && \ -apt-get -qq install libglu1-mesa libxrender1 libxcursor1 libxft2 libxinerama1 USER precice # Installing nutils - There is no adapter RUN python3 -m venv /home/precice/venv && \ diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index ce3a8a1f8..0094aceb7 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -293,6 +293,16 @@ test_suites: max_time: 0.3 reference_result: ./partitioned-heat-conduction/reference-results/dirichlet-openfoam_neumann-openfoam.tar.gz + partitioned-heat-conduction-3d: + tutorials: + - &partitioned-heat-conduction-3d_dirichlet-fenicsx_neumann-fenicsx + path: partitioned-heat-conduction-3d + case_combination: + - dirichlet-fenicsx + - neumann-fenicsx + max_time: 0.3 + reference_result: ./partitioned-heat-conduction-3d/reference-results/dirichlet-fenicsx_neumann-fenicsx.tar.gz + partitioned-heat-conduction-complex: tutorials: - &partitioned-heat-conduction-complex_dirichlet-fenics_neumann-fenics @@ -545,6 +555,7 @@ test_suites: - *partitioned-heat-conduction_dirichlet-fenics_neumann-fenics - *partitioned-heat-conduction_dirichlet-fenicsx_neumann-fenicsx - *partitioned-heat-conduction_dirichlet-openfoam_neumann-openfoam + - *partitioned-heat-conduction-3d_dirichlet-fenicsx_neumann-fenicsx - *partitioned-heat-conduction-complex_dirichlet-fenics_neumann-fenics - *partitioned-heat-conduction-direct_dirichlet-nutils_neumann-nutils - *partitioned-heat-conduction-overlap_left-fenics_right-fenics @@ -631,6 +642,7 @@ test_suites: tutorials: - *flow-over-heated-plate_fluid-openfoam_solid-fenicsx - *partitioned-heat-conduction_dirichlet-fenicsx_neumann-fenicsx + - *partitioned-heat-conduction-3d_dirichlet-fenicsx_neumann-fenicsx fmi-runner: tutorials: From 5dc8fe42e5353a55291db5f6c460c5c3e45a0311 Mon Sep 17 00:00:00 2001 From: preCICE Tests VM Date: Fri, 12 Jun 2026 20:47:42 +0200 Subject: [PATCH 82/94] Add reference results for partitioned-heat-conduction-3d --- .../dirichlet-fenicsx_neumann-fenicsx.tar.gz | 3 + .../reference_results.metadata | 72 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 partitioned-heat-conduction-3d/reference-results/dirichlet-fenicsx_neumann-fenicsx.tar.gz create mode 100644 partitioned-heat-conduction-3d/reference-results/reference_results.metadata diff --git a/partitioned-heat-conduction-3d/reference-results/dirichlet-fenicsx_neumann-fenicsx.tar.gz b/partitioned-heat-conduction-3d/reference-results/dirichlet-fenicsx_neumann-fenicsx.tar.gz new file mode 100644 index 000000000..3b1070796 --- /dev/null +++ b/partitioned-heat-conduction-3d/reference-results/dirichlet-fenicsx_neumann-fenicsx.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b6e18753223598093d66ed3de3187b1c3ab10b9dc0d002e4d4cde3eb1555dd7 +size 576415 diff --git a/partitioned-heat-conduction-3d/reference-results/reference_results.metadata b/partitioned-heat-conduction-3d/reference-results/reference_results.metadata new file mode 100644 index 000000000..d908ff01b --- /dev/null +++ b/partitioned-heat-conduction-3d/reference-results/reference_results.metadata @@ -0,0 +1,72 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| dirichlet-fenicsx_neumann-fenicsx.tar.gz | 2026-06-12 20:46:28 | 9b6e18753223598093d66ed3de3187b1c3ab10b9dc0d002e4d4cde3eb1555dd7 | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| FENICSX_ADAPTER_REF | v1.0.1 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | develop | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 From 635f8e9a7ccf887437cce128d511da743a114424 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Fri, 12 Jun 2026 23:39:07 +0200 Subject: [PATCH 83/94] Fix fenicsx-adapter system tests component --- tools/tests/components.yaml | 2 +- tools/tests/dockerfiles/ubuntu_2404/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/tests/components.yaml b/tools/tests/components.yaml index b9ba46e5b..35f393a3f 100644 --- a/tools/tests/components.yaml +++ b/tools/tests/components.yaml @@ -102,7 +102,7 @@ fenicsx-adapter: default: "master" FENICSX_ADAPTER_REF: description: Git ref of the fenicsx adapter to use - default: "master" + default: "main" nutils-adapter: repository: https://github.com/precice/nutils-adapter diff --git a/tools/tests/dockerfiles/ubuntu_2404/Dockerfile b/tools/tests/dockerfiles/ubuntu_2404/Dockerfile index 69f908045..39579bdc1 100644 --- a/tools/tests/dockerfiles/ubuntu_2404/Dockerfile +++ b/tools/tests/dockerfiles/ubuntu_2404/Dockerfile @@ -145,7 +145,7 @@ ARG FENICSX_ADAPTER_REF # Building fenicsx-adapter RUN python3 -m venv --system-site-packages /home/precice/venv && \ . /home/precice/venv/bin/activate && \ - pip3 install git+https://github.com/precice/fenics-adapter.git@${FENICSX_ADAPTER_REF} + pip3 install git+https://github.com/precice/fenicsx-adapter.git@${FENICSX_ADAPTER_REF} FROM precice_dependecies AS nutils_adapter COPY --from=python_bindings /home/precice/.local /home/precice/.local From eec0064e2e880ff7943b3fd34ec6a146fc50b15c Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Sat, 13 Jun 2026 13:32:30 +0200 Subject: [PATCH 84/94] Remove the .venv and __pycache__ directories at the end of Python-related tests (#835) --- tools/tests/component-templates/fenics-adapter.yaml | 4 +++- tools/tests/component-templates/fenicsx-adapter.yaml | 4 +++- tools/tests/component-templates/micro-manager.yaml | 4 +++- tools/tests/component-templates/nutils-adapter.yaml | 4 +++- tools/tests/component-templates/python-bindings.yaml | 4 +++- tools/tests/component-templates/su2-adapter.yaml | 4 +++- tools/tests/tests.yaml | 2 +- 7 files changed, 19 insertions(+), 7 deletions(-) diff --git a/tools/tests/component-templates/fenics-adapter.yaml b/tools/tests/component-templates/fenics-adapter.yaml index 220a52525..37f70f5a6 100644 --- a/tools/tests/component-templates/fenics-adapter.yaml +++ b/tools/tests/component-templates/fenics-adapter.yaml @@ -13,4 +13,6 @@ volumes: command: > /bin/bash -c "id && cd '/runs/{{ tutorial_folder }}/{{ case_folder }}' && - {{ run }} | tee system-tests_{{ case_folder }}.log 2>&1" + {{ run }} && + rm -rf .venv __pycache__ + | tee system-tests_{{ case_folder }}.log 2>&1" diff --git a/tools/tests/component-templates/fenicsx-adapter.yaml b/tools/tests/component-templates/fenicsx-adapter.yaml index c92ee0db1..3125f0ff1 100644 --- a/tools/tests/component-templates/fenicsx-adapter.yaml +++ b/tools/tests/component-templates/fenicsx-adapter.yaml @@ -13,4 +13,6 @@ volumes: command: > /bin/bash -c "id && cd '/runs/{{ tutorial_folder }}/{{ case_folder }}' && - {{ run }} | tee system-tests_{{ case_folder }}.log 2>&1" + {{ run }} && + rm -rf .venv __pycache__ + | tee system-tests_{{ case_folder }}.log 2>&1" diff --git a/tools/tests/component-templates/micro-manager.yaml b/tools/tests/component-templates/micro-manager.yaml index 1a78a1b4b..35c41e400 100644 --- a/tools/tests/component-templates/micro-manager.yaml +++ b/tools/tests/component-templates/micro-manager.yaml @@ -13,4 +13,6 @@ volumes: command: > /bin/bash -c "id && cd '/runs/{{ tutorial_folder }}/{{ case_folder }}' && - {{ run }} | tee system-tests_{{ case_folder }}.log 2>&1" + {{ run }} && + rm -rf .venv __pycache__ + | tee system-tests_{{ case_folder }}.log 2>&1" diff --git a/tools/tests/component-templates/nutils-adapter.yaml b/tools/tests/component-templates/nutils-adapter.yaml index 5893c65a7..e8abbc066 100644 --- a/tools/tests/component-templates/nutils-adapter.yaml +++ b/tools/tests/component-templates/nutils-adapter.yaml @@ -13,4 +13,6 @@ volumes: command: > /bin/bash -c "id && cd '/runs/{{ tutorial_folder }}/{{ case_folder }}' && - {{ run }} | tee system-tests_{{ case_folder }}.log 2>&1" + {{ run }} && + rm -rf .venv __pycache__ + | tee system-tests_{{ case_folder }}.log 2>&1" diff --git a/tools/tests/component-templates/python-bindings.yaml b/tools/tests/component-templates/python-bindings.yaml index 3cd65d274..3ea7ad8d0 100644 --- a/tools/tests/component-templates/python-bindings.yaml +++ b/tools/tests/component-templates/python-bindings.yaml @@ -13,4 +13,6 @@ volumes: command: > /bin/bash -c "id && cd '/runs/{{ tutorial_folder }}/{{ case_folder }}' && - {{ run }} | tee system-tests_{{ case_folder }}.log 2>&1" + {{ run }} && + rm -rf .venv __pycache__ + | tee system-tests_{{ case_folder }}.log 2>&1" diff --git a/tools/tests/component-templates/su2-adapter.yaml b/tools/tests/component-templates/su2-adapter.yaml index d466a949a..5a7abd8d0 100644 --- a/tools/tests/component-templates/su2-adapter.yaml +++ b/tools/tests/component-templates/su2-adapter.yaml @@ -13,4 +13,6 @@ volumes: command: > /bin/bash -c "id && cd '/runs/{{ tutorial_folder }}/{{ case_folder }}' && - SU2_RUN="/home/precice/SU2_RUN/bin" PYTHONPATH="/home/precice/SU2_RUN/bin:$PYTHONPATH" {{ run }} | tee system-tests_{{ case_folder }}.log 2>&1" + SU2_RUN="/home/precice/SU2_RUN/bin" PYTHONPATH="/home/precice/SU2_RUN/bin:$PYTHONPATH" {{ run }} && + rm -rf .venv __pycache__ + | tee system-tests_{{ case_folder }}.log 2>&1" diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index 0094aceb7..d33b741f6 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -719,4 +719,4 @@ test_suites: selected: tutorials: - - *two-scale-heat-conduction_macro-nutils_micro-nutils \ No newline at end of file + - *elastic-tube-1d_fluid-python_solid-python \ No newline at end of file From 732c757704dda973b921207240f6881b41d3b370 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Sat, 13 Jun 2026 17:03:51 +0200 Subject: [PATCH 85/94] Add MercuryDPM to the system tests (#836) --- channel-transport-particles/.gitignore | 2 + .../fluid-nutils/run.sh | 8 ++ .../fluid-openfoam/run.sh | 8 ++ channel-transport-particles/metadata.yaml | 10 +-- .../particles-mercurydpm/.gitignore | 1 + .../particles-mercurydpm/run.sh | 12 ++- .../particles-mercurydpm/setup-mercurydpm.sh | 0 .../fluid-nutils_particles-mercurydpm.tar.gz | 3 + ...fluid-openfoam_particles-mercurydpm.tar.gz | 3 + .../reference_results.metadata | 73 +++++++++++++++++++ .../mercurydpm-adapter.yaml | 16 ++++ tools/tests/components.yaml | 17 +++++ .../tests/dockerfiles/ubuntu_2404/Dockerfile | 13 ++++ tools/tests/reference_versions.yaml | 2 +- tools/tests/tests.yaml | 26 +++++++ 15 files changed, 187 insertions(+), 7 deletions(-) create mode 100644 channel-transport-particles/.gitignore create mode 100644 channel-transport-particles/particles-mercurydpm/.gitignore mode change 100644 => 100755 channel-transport-particles/particles-mercurydpm/setup-mercurydpm.sh create mode 100644 channel-transport-particles/reference-results/fluid-nutils_particles-mercurydpm.tar.gz create mode 100644 channel-transport-particles/reference-results/fluid-openfoam_particles-mercurydpm.tar.gz create mode 100644 channel-transport-particles/reference-results/reference_results.metadata create mode 100644 tools/tests/component-templates/mercurydpm-adapter.yaml diff --git a/channel-transport-particles/.gitignore b/channel-transport-particles/.gitignore new file mode 100644 index 000000000..d98835264 --- /dev/null +++ b/channel-transport-particles/.gitignore @@ -0,0 +1,2 @@ +# Signal file expected by the system tests specifically in uni-directional coupling +particles-participant-finished.log \ No newline at end of file diff --git a/channel-transport-particles/fluid-nutils/run.sh b/channel-transport-particles/fluid-nutils/run.sh index 8fa22ebdb..06296eb27 100755 --- a/channel-transport-particles/fluid-nutils/run.sh +++ b/channel-transport-particles/fluid-nutils/run.sh @@ -16,4 +16,12 @@ fi python3 fluid.py +# System tests: Keep the container and the respective network alive till the end. +if [[ -v PRECICE_TUTORIALS_TESTING ]]; then + echo "Waiting for the Particles participant to finish..." + if [ ! -f "../particles-participant-finished.log" ]; then + inotifywait -e create,modify,attrib --include '/particles-participant-finished\.log$' -qq .. + fi +fi + close_log diff --git a/channel-transport-particles/fluid-openfoam/run.sh b/channel-transport-particles/fluid-openfoam/run.sh index 8f55fbfa5..1ca8bf096 100755 --- a/channel-transport-particles/fluid-openfoam/run.sh +++ b/channel-transport-particles/fluid-openfoam/run.sh @@ -9,4 +9,12 @@ blockMesh ../../tools/run-openfoam.sh "$@" . ../../tools/openfoam-remove-empty-dirs.sh && openfoam_remove_empty_dirs +# System tests: Keep the container and the respective network alive till the end. +if [[ -v PRECICE_TUTORIALS_TESTING ]]; then + echo "Waiting for the Particles participant to finish..." + if [ ! -f "../particles-participant-finished.log" ]; then + inotifywait -e create,modify,attrib --include '/particles-participant-finished\.log$' -qq .. + fi +fi + close_log diff --git a/channel-transport-particles/metadata.yaml b/channel-transport-particles/metadata.yaml index b350e23d6..1fbf0a728 100644 --- a/channel-transport-particles/metadata.yaml +++ b/channel-transport-particles/metadata.yaml @@ -19,8 +19,8 @@ cases: run: ./run.sh component: openfoam-adapter - # particles-mercurydpm: - # participant: Particles - # directory: ./particles-mercurydpm - # run: ./run.sh - # component: mercurydpm-adapter \ No newline at end of file + particles-mercurydpm: + participant: Particles + directory: ./particles-mercurydpm + run: ./run.sh + component: mercurydpm-adapter \ No newline at end of file diff --git a/channel-transport-particles/particles-mercurydpm/.gitignore b/channel-transport-particles/particles-mercurydpm/.gitignore new file mode 100644 index 000000000..ac2210f18 --- /dev/null +++ b/channel-transport-particles/particles-mercurydpm/.gitignore @@ -0,0 +1 @@ +ChannelTransport* \ No newline at end of file diff --git a/channel-transport-particles/particles-mercurydpm/run.sh b/channel-transport-particles/particles-mercurydpm/run.sh index 3fa37bf74..f9f51b999 100755 --- a/channel-transport-particles/particles-mercurydpm/run.sh +++ b/channel-transport-particles/particles-mercurydpm/run.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash -set -euo pipefail +set -e -u -o pipefail + +. ../../tools/log.sh +exec > >(tee --append "$LOGFILE") 2>&1 EXE="" MERCURYDPM_BUILD_DIR="${MERCURYDPM_BUILD_DIR:-}" @@ -45,6 +48,12 @@ run_and_exit() { local cmd="$1" log "Using executable: ${cmd}" "${cmd}" + + # Necessary signal file for the system tests, + # to keep the Source container running till the end + date > ../particles-participant-finished.log + + close_log exit 0 } @@ -102,4 +111,5 @@ Hints: - MercuryDPM must be built with preCICE coupling: -D MercuryDPM_PreCICE_COUPLING="ON" EOF +close_log exit 1 diff --git a/channel-transport-particles/particles-mercurydpm/setup-mercurydpm.sh b/channel-transport-particles/particles-mercurydpm/setup-mercurydpm.sh old mode 100644 new mode 100755 diff --git a/channel-transport-particles/reference-results/fluid-nutils_particles-mercurydpm.tar.gz b/channel-transport-particles/reference-results/fluid-nutils_particles-mercurydpm.tar.gz new file mode 100644 index 000000000..a3fba4168 --- /dev/null +++ b/channel-transport-particles/reference-results/fluid-nutils_particles-mercurydpm.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a1a0ddf28692ca1e01b65150ba4a44c376fa13c076a755305519408c8e5710cc +size 781206 diff --git a/channel-transport-particles/reference-results/fluid-openfoam_particles-mercurydpm.tar.gz b/channel-transport-particles/reference-results/fluid-openfoam_particles-mercurydpm.tar.gz new file mode 100644 index 000000000..285c1c22c --- /dev/null +++ b/channel-transport-particles/reference-results/fluid-openfoam_particles-mercurydpm.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32dcaac90e5ca724fe73443e8e244a7eb0f73b03c9f33df8b0d8860a7b3a5e5f +size 93601 diff --git a/channel-transport-particles/reference-results/reference_results.metadata b/channel-transport-particles/reference-results/reference_results.metadata new file mode 100644 index 000000000..b3311dbc3 --- /dev/null +++ b/channel-transport-particles/reference-results/reference_results.metadata @@ -0,0 +1,73 @@ + + +# Reference Results + +This file contains an overview of the results over the reference results as well as the arguments used to generate them. +We also include some information on the machine used to generate them + +## List of files + +| name | time | sha256 | +|------|------|-------| +| fluid-openfoam_particles-mercurydpm.tar.gz | 2026-06-13 16:50:21 | 32dcaac90e5ca724fe73443e8e244a7eb0f73b03c9f33df8b0d8860a7b3a5e5f | +| fluid-nutils_particles-mercurydpm.tar.gz | 2026-06-13 16:50:21 | a1a0ddf28692ca1e01b65150ba4a44c376fa13c076a755305519408c8e5710cc | + +## List of arguments used to generate the files + +| name | value | +|------|------| +| PLATFORM | ubuntu_2404 | +| CALCULIX_VERSION | 2.20 | +| DUNE_VERSION | 2.9 | +| DUMUX_VERSION | 3.7 | +| OPENFOAM_EXECUTABLE | openfoam2512 | +| SU2_VERSION | 7.5.1 | +| FENICS_ADAPTER_REF | v2.3.0 | +| FENICSX_ADAPTER_REF | v1.0.1 | +| CALCULIX_ADAPTER_REF | v2.20.1 | +| DEALII_ADAPTER_REF | a421d92 | +| DUMUX_ADAPTER_REF | 3f3f54f | +| MICRO_MANAGER_REF | v0.10.1 | +| OPENFOAM_ADAPTER_REF | 2c3062c | +| PRECICE_REF | v3.4.1 | +| PYTHON_BINDINGS_REF | v3.4.0 | +| SU2_ADAPTER_REF | 5abe79b | +| TUTORIALS_REF | mercurydpm-tests | +| PRECICE_PRESET | production-audit | +| PRECICE_UID | 1003 | +| PRECICE_GID | 1003 | +## Information about the machine + +### uname -a + +Linux precice-tests 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + + +### lscpu + +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Address sizes: 45 bits physical, 48 bits virtual +Byte Order: Little Endian +CPU(s): 4 +On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel +Model name: Intel(R) Xeon(R) Gold 6130 CPU @ 2.10GHz +CPU family: 6 +Model: 85 +Thread(s) per core: 1 +Core(s) per socket: 1 +Socket(s): 4 +Stepping: 4 +BogoMIPS: 4199.99 +Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat pku ospke md_clear flush_l1d arch_capabilities +Hypervisor vendor: VMware +Virtualization type: full +L1d cache: 128 KiB (4 instances) +L1i cache: 128 KiB (4 instances) +L2 cache: 4 MiB (4 instances) +L3 cache: 88 MiB (4 instances) +NUMA node(s): 1 +NUMA node0 CPU(s): 0-3 diff --git a/tools/tests/component-templates/mercurydpm-adapter.yaml b/tools/tests/component-templates/mercurydpm-adapter.yaml new file mode 100644 index 000000000..ac085d079 --- /dev/null +++ b/tools/tests/component-templates/mercurydpm-adapter.yaml @@ -0,0 +1,16 @@ +build: + context: {{ dockerfile_context }} + args: + {% for key, value in build_arguments.items() %} + - {{key}}={{value}} + {% endfor %} + target: mercurydpm_adapter +depends_on: + prepare: + condition: service_completed_successfully +volumes: + - {{ run_directory }}:/runs +command: > + /bin/bash -c "id && + cd '/runs/{{ tutorial_folder }}/{{ case_folder }}' && + {{ run }} | tee system-tests_{{ case_folder }}.log 2>&1" diff --git a/tools/tests/components.yaml b/tools/tests/components.yaml index 35f393a3f..0a364f592 100644 --- a/tools/tests/components.yaml +++ b/tools/tests/components.yaml @@ -216,6 +216,23 @@ dumux-adapter: description: Git ref of the dumux adapter to use default: "main" +mercurydpm-adapter: + repository: https://bitbucket.org/davidscn/mercurydpm + template: component-templates/mercurydpm-adapter.yaml + build_arguments: + PLATFORM: + description: Dockerfile platform used + default: "ubuntu_2404" + PRECICE_REF: + description: Version of preCICE to use + default: "main" + PRECICE_PRESET: + description: CMake preset of preCICE + default: "production-audit" + TUTORIALS_REF: + description: Tutorial git reference to use + default: "master" + micro-manager: repository: https://github.com/precice/micro-manager template: component-templates/micro-manager.yaml diff --git a/tools/tests/dockerfiles/ubuntu_2404/Dockerfile b/tools/tests/dockerfiles/ubuntu_2404/Dockerfile index 39579bdc1..a1f0eb74a 100644 --- a/tools/tests/dockerfiles/ubuntu_2404/Dockerfile +++ b/tools/tests/dockerfiles/ubuntu_2404/Dockerfile @@ -231,6 +231,19 @@ RUN git clone https://github.com/precice/dealii-adapter.git &&\ cmake . && \ make -j $(nproc) +FROM precice_dependecies AS mercurydpm_adapter +USER precice +COPY --from=precice /home/precice/.local /home/precice/.local +WORKDIR /home/precice +ENV PATH="/home/precice/mercurydpm/build/Drivers/PreCICE:$PATH" +# The channel-transport-particles tutorial is hosted in a frozen fork of the MercuryDPM repository. +# That's why there are no arguments for version, ref, or pr. +RUN git clone https://bitbucket.org/davidscn/mercurydpm.git &&\ + cd mercurydpm && \ + git checkout channel-transport-tutorial && \ + mkdir -p build && cd build && \ + cmake -D MercuryDPM_PreCICE_COUPLING="ON" .. && \ + make -j $(nproc) ChannelTransport FROM precice_dependecies AS micro_manager USER precice diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index 6c7e69563..c7c3e4c04 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -22,7 +22,7 @@ OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 PRECICE_REF: "v3.4.1" PYTHON_BINDINGS_REF: "v3.4.0" SU2_ADAPTER_REF: "5abe79b" # develop, May 27, 2026 -TUTORIALS_REF: "develop" +TUTORIALS_REF: "mercurydpm-tests" # Additional settings PRECICE_PRESET: "production-audit" \ No newline at end of file diff --git a/tools/tests/tests.yaml b/tools/tests/tests.yaml index d33b741f6..70faade1d 100644 --- a/tools/tests/tests.yaml +++ b/tools/tests/tests.yaml @@ -48,6 +48,23 @@ test_suites: max_time: 0.5 reference_result: ./channel-transport-reaction/reference-results/fluid-fenics_chemical-fenics.tar.gz + channel-transport-particles: + tutorials: + - &channel-transport-particles_fluid-openfoam_particles-mercurydpm + path: channel-transport-particles + case_combination: + - fluid-openfoam + - particles-mercurydpm + max_time: 0.025 + reference_result: ./channel-transport-particles/reference-results/fluid-openfoam_particles-mercurydpm.tar.gz + - &channel-transport-particles_fluid-nutils_particles-mercurydpm + path: channel-transport-particles + case_combination: + - fluid-nutils + - particles-mercurydpm + max_time: 0.025 + reference_result: ./channel-transport-particles/reference-results/fluid-nutils_particles-mercurydpm.tar.gz + elastic-tube-1d: tutorials: - &elastic-tube-1d_fluid-cpp_solid-cpp @@ -528,6 +545,8 @@ test_suites: - *breaking-dam-2d_fluid-openfoam_solid-calculix - *channel-transport_fluid-openfoam_transport-nutils - *channel-transport-reaction_fluid-fenics_chemical-fenics + - *channel-transport-particles_fluid-openfoam_particles-mercurydpm + - *channel-transport-particles_fluid-nutils_particles-mercurydpm - *elastic-tube-1d_fluid-cpp_solid-cpp - *elastic-tube-1d_fluid-cpp_solid-python - *elastic-tube-1d_fluid-fortran_solid-fortran @@ -652,6 +671,11 @@ test_suites: tutorials: - *elastic-tube-1d_fluid-fortran-module_solid-fortran-module + mercurydpm-adapter: + tutorials: + - *channel-transport-particles_fluid-openfoam_particles-mercurydpm + - *channel-transport-particles_fluid-nutils_particles-mercurydpm + micro-manager: tutorials: - *two-scale-heat-conduction_macro-dumux_micro-dumux # too small values to compare @@ -661,6 +685,7 @@ test_suites: tutorials: - *channel-transport_fluid-openfoam_transport-nutils - *channel-transport_fluid-nutils_transport-nutils + - *channel-transport-particles_fluid-nutils_particles-mercurydpm - *flow-over-heated-plate_fluid-openfoam_solid-nutils - *partitioned-heat-conduction_dirichlet-nutils_neumann-nutils - *partitioned-heat-conduction-direct_dirichlet-nutils_neumann-nutils @@ -677,6 +702,7 @@ test_suites: tutorials: - *breaking-dam-2d_fluid-openfoam_solid-calculix - *channel-transport_fluid-openfoam_transport-nutils + - *channel-transport-particles_fluid-openfoam_particles-mercurydpm - *elastic-tube-3d_fluid-openfoam_solid-calculix - *flow-around-controlled-moving-cylinder_controller-fmi_fluid-openfoam_solid-python - *flow-over-heated-plate_fluid-openfoam_solid-fenics From ab68b16ba290e7f8fe9264c84a4314846383694d Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Sun, 14 Jun 2026 12:58:49 +0200 Subject: [PATCH 86/94] Add override inputs to system-tests-latest-components.yml (#839) --- .../system-tests-latest-components.yml | 76 ++++++++++++++++--- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/.github/workflows/system-tests-latest-components.yml b/.github/workflows/system-tests-latest-components.yml index 94e407abb..858b089e7 100644 --- a/.github/workflows/system-tests-latest-components.yml +++ b/.github/workflows/system-tests-latest-components.yml @@ -15,8 +15,48 @@ on: description: 'Branch to take the system tests from' default: 'develop' type: string + ref-precice: + description: 'precice Git ref (override latest)' + type: string + ref-python-bindings: + description: 'python-bindings ref' + type: string + ref-calculix-adapter: + description: 'calculix-adapter ref' + type: string + ref-dealii-adapter: + description: 'dealii-adapter ref' + type: string + ref-dumux-adapter: + description: 'dumux-adapter ref' + type: string + ref-fenics-adapter: + description: 'fenics-adapter ref' + type: string + ref-fenicsx-adapter: + description: 'fenicsx-adapter ref' + type: string + ref-micro-manager: + description: 'micro-manager ref' + type: string + ref-openfoam-adapter: + description: 'openfoam-adapter ref' + type: string + ref-su2-adapter: + description: 'su2-adapter ref' + type: string + ref-tutorials: + description: 'tutorials ref' + type: string jobs: + report-inputs: + name: Report workflow inputs + runs-on: ubuntu-latest + steps: + - id: report-inputs + name: Report inputs + uses: VeyronSakai/display-inputs-action@v0.1 gather-refs: name: Map Git branches to latest refs runs-on: ubuntu-latest @@ -24,6 +64,7 @@ jobs: ref-precice: ${{ steps.ref-precice.outputs.shorthash }} ref-python-bindings: ${{ steps.ref-python-bindings.outputs.shorthash }} ref-calculix-adapter: ${{ steps.ref-calculix-adapter.outputs.shorthash }} + ref-dealii-adapter: ${{ steps.ref-dealii-adapter.outputs.shorthash }} ref-dumux-adapter: ${{ steps.ref-dumux-adapter.outputs.shorthash }} ref-fenics-adapter: ${{ steps.ref-fenics-adapter.outputs.shorthash }} ref-fenicsx-adapter: ${{ steps.ref-fenicsx-adapter.outputs.shorthash }} @@ -53,6 +94,13 @@ jobs: owner: precice repo: calculix-adapter branch: develop + - id: ref-dealii-adapter + name: Get deal.II adapter ref + uses: nmbgeek/github-action-get-latest-commit@main + with: + owner: precice + repo: dealii-adapter + branch: develop - id: ref-dumux-adapter name: Get DuMux adapter ref uses: nmbgeek/github-action-get-latest-commit@main @@ -108,6 +156,7 @@ jobs: printf 'preCICE: ${{ steps.ref-precice.outputs.shorthash }}\n ${{ steps.ref-precice.outputs.description }}\n----------\n' printf 'Python bindings: ${{ steps.ref-python-bindings.outputs.shorthash }}\n ${{ steps.ref-python-bindings.outputs.description }}\n----------\n' printf 'CalculiX adapter: ${{ steps.ref-calculix-adapter.outputs.shorthash }}\n ${{ steps.ref-calculix-adapter.outputs.description }}\n----------\n' + printf 'deal.II adapter: ${{ steps.ref-dealii-adapter.outputs.shorthash }}\n ${{ steps.ref-dealii-adapter.outputs.description }}\n----------\n' printf 'DuMux adapter: ${{ steps.ref-dumux-adapter.outputs.shorthash }}\n ${{ steps.ref-dumux-adapter.outputs.description }}\n----------\n' printf 'FEniCS adapter: ${{ steps.ref-fenics-adapter.outputs.shorthash }}\n ${{ steps.ref-fenics-adapter.outputs.description }}\n----------\n' printf 'FEniCSx adapter: ${{ steps.ref-fenicsx-adapter.outputs.shorthash }}\n ${{ steps.ref-fenicsx-adapter.outputs.description }}\n----------\n' @@ -138,6 +187,12 @@ jobs: echo "\`\`\`" echo "${{ steps.ref-calculix-adapter.outputs.description }}" echo "\`\`\`" + echo "### deal.II adapter" + echo "Reference: [\`${{ steps.ref-dealii-adapter.outputs.shorthash }}\`](https://github.com/precice/dealii-adapter/commit/${{ steps.ref-dealii-adapter.outputs.shorthash }})" + echo "Description:" + echo "\`\`\`" + echo "${{ steps.ref-dealii-adapter.outputs.description }}" + echo "\`\`\`" echo "### DuMux adapter" echo "Reference: [\`${{ steps.ref-dumux-adapter.outputs.shorthash }}\`](https://github.com/precice/dumux-adapter/commit/${{ steps.ref-dumux-adapter.outputs.shorthash }})" echo "Description:" @@ -189,15 +244,16 @@ jobs: with: suites: ${{ inputs.suites || 'release' }} build_args: "PLATFORM:ubuntu_2404,\ - PRECICE_REF:${{ needs.gather-refs.outputs.ref-precice }},\ - PYTHON_BINDINGS_REF:${{ needs.gather-refs.outputs.ref-python-bindings }},\ - CALCULIX_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-calculix-adapter }},\ - DUMUX_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-dumux-adapter }},\ - FENICS_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-fenics-adapter }},\ - FENICSX_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-fenicsx-adapter }},\ - MICRO_MANAGER_REF:${{ needs.gather-refs.outputs.ref-micro-manager }},\ - OPENFOAM_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-openfoam-adapter }},\ - SU2_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-su2-adapter }},\ - TUTORIALS_REF:${{ needs.gather-refs.outputs.ref-tutorials }}" + PRECICE_REF:${{ inputs.ref-precice || needs.gather-refs.outputs.ref-precice }},\ + PYTHON_BINDINGS_REF:${{ inputs.ref-python-bindings || needs.gather-refs.outputs.ref-python-bindings }},\ + CALCULIX_ADAPTER_REF:${{ inputs.ref-calculix-adapter || needs.gather-refs.outputs.ref-calculix-adapter }},\ + DEALII_ADAPTER_REF:${{ inputs.ref-dealii-adapter || needs.gather-refs.outputs.ref-dealii-adapter }},\ + DUMUX_ADAPTER_REF:${{ inputs.ref-dumux-adapter || needs.gather-refs.outputs.ref-dumux-adapter }},\ + FENICS_ADAPTER_REF:${{ inputs.ref-fenics-adapter || needs.gather-refs.outputs.ref-fenics-adapter }},\ + FENICSX_ADAPTER_REF:${{ inputs.ref-fenicsx-adapter || needs.gather-refs.outputs.ref-fenicsx-adapter }},\ + MICRO_MANAGER_REF:${{ inputs.ref-micro-manager || needs.gather-refs.outputs.ref-micro-manager }},\ + OPENFOAM_ADAPTER_REF:${{ inputs.ref-openfoam-adapter || needs.gather-refs.outputs.ref-openfoam-adapter }},\ + SU2_ADAPTER_REF:${{ inputs.ref-su2-adapter || needs.gather-refs.outputs.ref-su2-adapter }},\ + TUTORIALS_REF:${{ inputs.ref-tutorials || needs.gather-refs.outputs.ref-tutorials }}" system_tests_branch: ${{ inputs.system_tests_branch || 'develop' }} log_level: "INFO" From 7fccdc3a069df7df134e214aedbf9437b01f2f9c Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Mon, 15 Jun 2026 23:21:53 +0200 Subject: [PATCH 87/94] Integrate fetching latest components into Systemtest.py (#842) --- .github/workflows/run_testsuite_workflow.yml | 9 +- .../system-tests-latest-components.yml | 211 +----------------- .github/workflows/system-tests-pr.yml | 168 +------------- changelog-entries/842.md | 1 + tools/tests/README.md | 21 +- tools/tests/components.yaml | 185 ++++++--------- tools/tests/metadata_parser/metdata.py | 15 +- tools/tests/systemtests/Systemtest.py | 40 +++- .../tests/systemtests/SystemtestArguments.py | 3 +- 9 files changed, 146 insertions(+), 507 deletions(-) create mode 100644 changelog-entries/842.md diff --git a/.github/workflows/run_testsuite_workflow.yml b/.github/workflows/run_testsuite_workflow.yml index 0ce67cd63..38a6df0a9 100644 --- a/.github/workflows/run_testsuite_workflow.yml +++ b/.github/workflows/run_testsuite_workflow.yml @@ -32,8 +32,9 @@ jobs: echo "System tests branch (tools/): ${{ inputs.system_tests_branch }}" echo "System tests log level: ${{ inputs.log_level }}" echo "Uploading the runs folder on success: ${{ inputs.upload_artifacts }}" - echo "Running the following command inside tutorials/tools/:" + echo "Running the following command inside tutorials/tools/tests/:" echo "python3 systemtests.py --build_args=${{inputs.build_args}} --suites=${{ inputs.suites}}" + echo "All workflow inputs: ${{ toJSON(github.event.inputs) }}" - name: Prepare the Markdown step summary run: | { @@ -44,10 +45,14 @@ jobs: echo "- System tests log level: \`${{ inputs.log_level }}\`" echo "- Uploading the runs folder on success: \`${{ inputs.upload_artifacts }}\`" echo "" - echo "Running the following command inside \`tutorials/tools/\`:" + echo "Running the following command inside \`tutorials/tools/tests/\`:" echo "\`\`\`" echo "python3 systemtests.py --build_args=${{inputs.build_args}} --suites=${{ inputs.suites}}" echo "\`\`\`" + echo "All workflow inputs:" + echo "\`\`\`" + echo "${{ toJSON(github.event.inputs) }}" + echo "\`\`\`" } >> "$GITHUB_STEP_SUMMARY" - name: Move LFS URL to local LFS server run: | diff --git a/.github/workflows/system-tests-latest-components.yml b/.github/workflows/system-tests-latest-components.yml index 858b089e7..f48ab4eb1 100644 --- a/.github/workflows/system-tests-latest-components.yml +++ b/.github/workflows/system-tests-latest-components.yml @@ -50,210 +50,21 @@ on: type: string jobs: - report-inputs: - name: Report workflow inputs - runs-on: ubuntu-latest - steps: - - id: report-inputs - name: Report inputs - uses: VeyronSakai/display-inputs-action@v0.1 - gather-refs: - name: Map Git branches to latest refs - runs-on: ubuntu-latest - outputs: - ref-precice: ${{ steps.ref-precice.outputs.shorthash }} - ref-python-bindings: ${{ steps.ref-python-bindings.outputs.shorthash }} - ref-calculix-adapter: ${{ steps.ref-calculix-adapter.outputs.shorthash }} - ref-dealii-adapter: ${{ steps.ref-dealii-adapter.outputs.shorthash }} - ref-dumux-adapter: ${{ steps.ref-dumux-adapter.outputs.shorthash }} - ref-fenics-adapter: ${{ steps.ref-fenics-adapter.outputs.shorthash }} - ref-fenicsx-adapter: ${{ steps.ref-fenicsx-adapter.outputs.shorthash }} - ref-micro-manager: ${{ steps.ref-micro-manager.outputs.shorthash }} - ref-openfoam-adapter: ${{ steps.ref-openfoam-adapter.outputs.shorthash }} - ref-su2-adapter: ${{ steps.ref-su2-adapter.outputs.shorthash }} - ref-tutorials: ${{ steps.ref-tutorials.outputs.shorthash }} - steps: - - id: ref-precice - name: Get preCICE ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: precice - branch: develop - - id: ref-python-bindings - name: Get Python bindings ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: python-bindings - branch: develop - - id: ref-calculix-adapter - name: Get CalculiX adapter ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: calculix-adapter - branch: develop - - id: ref-dealii-adapter - name: Get deal.II adapter ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: dealii-adapter - branch: develop - - id: ref-dumux-adapter - name: Get DuMux adapter ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: dumux-adapter - branch: develop - - id: ref-fenics-adapter - name: Get FEniCS adapter ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: fenics-adapter - branch: develop - - id: ref-fenicsx-adapter - name: Get FEniCSx adapter ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: fenicsx-adapter - branch: develop - - id: ref-micro-manager - name: Get Micro-Manager ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: micro-manager - branch: develop - - id: ref-openfoam-adapter - name: Get OpenFOAM adapter ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: openfoam-adapter - branch: develop - - id: ref-su2-adapter - name: Get SU2 adapter ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: su2-adapter - branch: develop - - id: ref-tutorials - name: Get tutorials ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: tutorials - branch: develop - - id: report-refs - name: Report Git refs - run: | - printf 'preCICE: ${{ steps.ref-precice.outputs.shorthash }}\n ${{ steps.ref-precice.outputs.description }}\n----------\n' - printf 'Python bindings: ${{ steps.ref-python-bindings.outputs.shorthash }}\n ${{ steps.ref-python-bindings.outputs.description }}\n----------\n' - printf 'CalculiX adapter: ${{ steps.ref-calculix-adapter.outputs.shorthash }}\n ${{ steps.ref-calculix-adapter.outputs.description }}\n----------\n' - printf 'deal.II adapter: ${{ steps.ref-dealii-adapter.outputs.shorthash }}\n ${{ steps.ref-dealii-adapter.outputs.description }}\n----------\n' - printf 'DuMux adapter: ${{ steps.ref-dumux-adapter.outputs.shorthash }}\n ${{ steps.ref-dumux-adapter.outputs.description }}\n----------\n' - printf 'FEniCS adapter: ${{ steps.ref-fenics-adapter.outputs.shorthash }}\n ${{ steps.ref-fenics-adapter.outputs.description }}\n----------\n' - printf 'FEniCSx adapter: ${{ steps.ref-fenicsx-adapter.outputs.shorthash }}\n ${{ steps.ref-fenicsx-adapter.outputs.description }}\n----------\n' - printf 'Micro-Manager: ${{ steps.ref-micro-manager.outputs.shorthash }}\n ${{ steps.ref-micro-manager.outputs.description }}\n----------\n' - printf 'OpenFOAM adapter: ${{ steps.ref-openfoam-adapter.outputs.shorthash }} ${{ steps.ref-openfoam-adapter.outputs.description }}\n----------\n' - printf 'SU2 adapter: ${{ steps.ref-su2-adapter.outputs.shorthash }}\n ${{ steps.ref-su2-adapter.outputs.description }}\n----------\n' - printf 'Tutorials: ${{ steps.ref-tutorials.outputs.shorthash }} ${{ steps.ref-tutorials.outputs.description }}\n----------\n' - - id: summary - name: Prepare Markdown summary - run: | - { - echo "## Git references of latest (develop) components" - echo "### preCICE" - echo "Reference: [\`${{ steps.ref-precice.outputs.shorthash }}\`](https://github.com/precice/precice/commit/${{ steps.ref-precice.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-precice.outputs.description }}" - echo "\`\`\`" - echo "### Python bindings" - echo "Reference: [\`${{ steps.ref-python-bindings.outputs.shorthash }}\`](https://github.com/precice/python-bindings/commit/${{ steps.ref-python-bindings.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-python-bindings.outputs.description }}" - echo "\`\`\`" - echo "### CalculiX adapter" - echo "Reference: [\`${{ steps.ref-calculix-adapter.outputs.shorthash }}\`](https://github.com/precice/calculix-adapter/commit/${{ steps.ref-calculix-adapter.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-calculix-adapter.outputs.description }}" - echo "\`\`\`" - echo "### deal.II adapter" - echo "Reference: [\`${{ steps.ref-dealii-adapter.outputs.shorthash }}\`](https://github.com/precice/dealii-adapter/commit/${{ steps.ref-dealii-adapter.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-dealii-adapter.outputs.description }}" - echo "\`\`\`" - echo "### DuMux adapter" - echo "Reference: [\`${{ steps.ref-dumux-adapter.outputs.shorthash }}\`](https://github.com/precice/dumux-adapter/commit/${{ steps.ref-dumux-adapter.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-dumux-adapter.outputs.description }}" - echo "\`\`\`" - echo "### FEniCS adapter" - echo "Reference: [\`${{ steps.ref-fenics-adapter.outputs.shorthash }}\`](https://github.com/precice/fenics-adapter/commit/${{ steps.ref-fenics-adapter.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-fenics-adapter.outputs.description }}" - echo "\`\`\`" - echo "### FEniCSx adapter" - echo "Reference: [\`${{ steps.ref-fenicsx-adapter.outputs.shorthash }}\`](https://github.com/precice/fenics-adapter/commit/${{ steps.ref-fenicsx-adapter.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-fenicsx-adapter.outputs.description }}" - echo "\`\`\`" - echo "### Micro-Manager" - echo "Reference: [\`${{ steps.ref-micro-manager.outputs.shorthash }}\`](https://github.com/precice/micro-manager/commit/${{ steps.ref-micro-manager.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-micro-manager.outputs.description }}" - echo "\`\`\`" - echo "### OpenFOAM adapter" - echo "Reference: [\`${{ steps.ref-openfoam-adapter.outputs.shorthash }}\`](https://github.com/precice/openfoam-adapter/commit/${{ steps.ref-openfoam-adapter.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-openfoam-adapter.outputs.description }}" - echo "\`\`\`" - echo "### SU2 adapter" - echo "Reference: [\`${{ steps.ref-su2-adapter.outputs.shorthash }}\`](https://github.com/precice/su2-adapter/commit/${{ steps.ref-su2-adapter.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-su2-adapter.outputs.description }}" - echo "\`\`\`" - echo "### Tutorials" - echo "Reference: [\`${{ steps.ref-tutorials.outputs.shorthash }}\`](https://github.com/precice/tutorials/commit/${{ steps.ref-tutorials.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-tutorials.outputs.description }}" - echo "\`\`\`" - } >> "$GITHUB_STEP_SUMMARY" - run-system-tests: name: Trigger system tests - needs: gather-refs uses: precice/tutorials/.github/workflows/run_testsuite_workflow.yml@develop with: suites: ${{ inputs.suites || 'release' }} - build_args: "PLATFORM:ubuntu_2404,\ - PRECICE_REF:${{ inputs.ref-precice || needs.gather-refs.outputs.ref-precice }},\ - PYTHON_BINDINGS_REF:${{ inputs.ref-python-bindings || needs.gather-refs.outputs.ref-python-bindings }},\ - CALCULIX_ADAPTER_REF:${{ inputs.ref-calculix-adapter || needs.gather-refs.outputs.ref-calculix-adapter }},\ - DEALII_ADAPTER_REF:${{ inputs.ref-dealii-adapter || needs.gather-refs.outputs.ref-dealii-adapter }},\ - DUMUX_ADAPTER_REF:${{ inputs.ref-dumux-adapter || needs.gather-refs.outputs.ref-dumux-adapter }},\ - FENICS_ADAPTER_REF:${{ inputs.ref-fenics-adapter || needs.gather-refs.outputs.ref-fenics-adapter }},\ - FENICSX_ADAPTER_REF:${{ inputs.ref-fenicsx-adapter || needs.gather-refs.outputs.ref-fenicsx-adapter }},\ - MICRO_MANAGER_REF:${{ inputs.ref-micro-manager || needs.gather-refs.outputs.ref-micro-manager }},\ - OPENFOAM_ADAPTER_REF:${{ inputs.ref-openfoam-adapter || needs.gather-refs.outputs.ref-openfoam-adapter }},\ - SU2_ADAPTER_REF:${{ inputs.ref-su2-adapter || needs.gather-refs.outputs.ref-su2-adapter }},\ - TUTORIALS_REF:${{ inputs.ref-tutorials || needs.gather-refs.outputs.ref-tutorials }}" + build_args: "PRECICE_REF:${{ inputs.ref-precice }},\ + PYTHON_BINDINGS_REF:${{ inputs.ref-python-bindings }},\ + CALCULIX_ADAPTER_REF:${{ inputs.ref-calculix-adapter }},\ + DEALII_ADAPTER_REF:${{ inputs.ref-dealii-adapter }},\ + DUMUX_ADAPTER_REF:${{ inputs.ref-dumux-adapter }},\ + FENICS_ADAPTER_REF:${{ inputs.ref-fenics-adapter }},\ + FENICSX_ADAPTER_REF:${{ inputs.ref-fenicsx-adapter }},\ + MICRO_MANAGER_REF:${{ inputs.ref-micro-manager }},\ + OPENFOAM_ADAPTER_REF:${{ inputs.ref-openfoam-adapter }},\ + SU2_ADAPTER_REF:${{ inputs.ref-su2-adapter }},\ + TUTORIALS_REF:${{ inputs.ref-tutorials }}" system_tests_branch: ${{ inputs.system_tests_branch || 'develop' }} log_level: "INFO" diff --git a/.github/workflows/system-tests-pr.yml b/.github/workflows/system-tests-pr.yml index 3af5fc451..98b32a349 100644 --- a/.github/workflows/system-tests-pr.yml +++ b/.github/workflows/system-tests-pr.yml @@ -5,178 +5,12 @@ on: types: [labeled] jobs: - gather-refs: - name: Map Git branches to latest refs - if: ${{ github.event.label.name == 'trigger-system-tests' }} - runs-on: ubuntu-latest - outputs: - ref-precice: ${{ steps.ref-precice.outputs.shorthash }} - ref-python-bindings: ${{ steps.ref-python-bindings.outputs.shorthash }} - ref-calculix-adapter: ${{ steps.ref-calculix-adapter.outputs.shorthash }} - ref-dumux-adapter: ${{ steps.ref-dumux-adapter.outputs.shorthash }} - ref-fenics-adapter: ${{ steps.ref-fenics-adapter.outputs.shorthash }} - ref-fenicsx-adapter: ${{ steps.ref-fenicsx-adapter.outputs.shorthash }} - ref-micro-manager: ${{ steps.ref-micro-manager.outputs.shorthash }} - ref-openfoam-adapter: ${{ steps.ref-openfoam-adapter.outputs.shorthash }} - ref-su2-adapter: ${{ steps.ref-su2-adapter.outputs.shorthash }} - steps: - - id: ref-precice - name: Get preCICE ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: precice - branch: develop - - id: ref-python-bindings - name: Get Python bindings ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: python-bindings - branch: develop - - id: ref-calculix-adapter - name: Get CalculiX adapter ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: calculix-adapter - branch: develop - - id: ref-dumux-adapter - name: Get DuMux adapter ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: dumux-adapter - branch: develop - - id: ref-fenics-adapter - name: Get FEniCS adapter ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: fenics-adapter - branch: develop - - id: ref-fenicsx-adapter - name: Get FEniCSx adapter ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: fenicsx-adapter - branch: develop - - id: ref-micro-manager - name: Get Micro-Manager ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: micro-manager - branch: develop - - id: ref-openfoam-adapter - name: Get OpenFOAM adapter ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: openfoam-adapter - branch: develop - - id: ref-su2-adapter - name: Get SU2 adapter ref - uses: nmbgeek/github-action-get-latest-commit@main - with: - owner: precice - repo: su2-adapter - branch: develop - - id: report-refs - name: Report Git refs - run: | - printf 'preCICE: ${{ steps.ref-precice.outputs.shorthash }}\n ${{ steps.ref-precice.outputs.description }}\n----------\n' - printf 'Python bindings: ${{ steps.ref-python-bindings.outputs.shorthash }}\n ${{ steps.ref-python-bindings.outputs.description }}\n----------\n' - printf 'CalculiX adapter: ${{ steps.ref-calculix-adapter.outputs.shorthash }}\n ${{ steps.ref-calculix-adapter.outputs.description }}\n----------\n' - printf 'DuMux adapter: ${{ steps.ref-dumux-adapter.outputs.shorthash }}\n ${{ steps.ref-dumux-adapter.outputs.description }}\n----------\n' - printf 'FEniCS adapter: ${{ steps.ref-fenics-adapter.outputs.shorthash }}\n ${{ steps.ref-fenics-adapter.outputs.description }}\n----------\n' - printf 'FEniCSx adapter: ${{ steps.ref-fenicsx-adapter.outputs.shorthash }}\n ${{ steps.ref-fenicsx-adapter.outputs.description }}\n----------\n' - printf 'Micro-Manager: ${{ steps.ref-micro-manager.outputs.shorthash }}\n ${{ steps.ref-micro-manager.outputs.description }}\n----------\n' - printf 'OpenFOAM adapter: ${{ steps.ref-openfoam-adapter.outputs.shorthash }} ${{ steps.ref-openfoam-adapter.outputs.description }}\n----------\n' - printf 'SU2 adapter: ${{ steps.ref-su2-adapter.outputs.shorthash }}\n ${{ steps.ref-su2-adapter.outputs.description }}\n----------\n' - - id: summary - name: Prepare Markdown summary - run: | - { - echo "## Git references of latest (develop) components" - echo "### preCICE" - echo "Reference: [\`${{ steps.ref-precice.outputs.shorthash }}\`](https://github.com/precice/precice/commit/${{ steps.ref-precice.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-precice.outputs.description }}" - echo "\`\`\`" - echo "### Python bindings" - echo "Reference: [\`${{ steps.ref-python-bindings.outputs.shorthash }}\`](https://github.com/precice/python-bindings/commit/${{ steps.ref-python-bindings.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-python-bindings.outputs.description }}" - echo "\`\`\`" - echo "### CalculiX adapter" - echo "Reference: [\`${{ steps.ref-calculix-adapter.outputs.shorthash }}\`](https://github.com/precice/calculix-adapter/commit/${{ steps.ref-calculix-adapter.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-calculix-adapter.outputs.description }}" - echo "\`\`\`" - echo "### DuMux adapter" - echo "Reference: [\`${{ steps.ref-dumux-adapter.outputs.shorthash }}\`](https://github.com/precice/dumux-adapter/commit/${{ steps.ref-dumux-adapter.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-dumux-adapter.outputs.description }}" - echo "\`\`\`" - echo "### FEniCS adapter" - echo "Reference: [\`${{ steps.ref-fenics-adapter.outputs.shorthash }}\`](https://github.com/precice/fenics-adapter/commit/${{ steps.ref-fenics-adapter.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-fenics-adapter.outputs.description }}" - echo "\`\`\`" - echo "### FEniCSx adapter" - echo "Reference: [\`${{ steps.ref-fenicsx-adapter.outputs.shorthash }}\`](https://github.com/precice/fenicsx-adapter/commit/${{ steps.ref-fenicsx-adapter.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-fenicsx-adapter.outputs.description }}" - echo "\`\`\`" - echo "### Micro-Manager" - echo "Reference: [\`${{ steps.ref-micro-manager.outputs.shorthash }}\`](https://github.com/precice/micro-manager/commit/${{ steps.ref-micro-manager.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-micro-manager.outputs.description }}" - echo "\`\`\`" - echo "### OpenFOAM adapter" - echo "Reference: [\`${{ steps.ref-openfoam-adapter.outputs.shorthash }}\`](https://github.com/precice/openfoam-adapter/commit/${{ steps.ref-openfoam-adapter.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-openfoam-adapter.outputs.description }}" - echo "\`\`\`" - echo "### SU2 adapter" - echo "Reference: [\`${{ steps.ref-su2-adapter.outputs.shorthash }}\`](https://github.com/precice/su2-adapter/commit/${{ steps.ref-su2-adapter.outputs.shorthash }})" - echo "Description:" - echo "\`\`\`" - echo "${{ steps.ref-su2-adapter.outputs.description }}" - echo "\`\`\`" - echo "### Tutorials" - echo "Reference (pull request): \`${{ github.event.pull_request.head.sha }}\`" - echo "Pull request number: [${{ github.event.number }}](https://github.com/precice/tutorials/pull/${{ github.event.number }})" - } >> "$GITHUB_STEP_SUMMARY" - run-system-tests: name: Trigger system tests if: ${{ github.event.label.name == 'trigger-system-tests' }} - needs: gather-refs uses: precice/tutorials/.github/workflows/run_testsuite_workflow.yml@develop with: suites: release - build_args: "PLATFORM:ubuntu_2404,\ - PRECICE_REF:${{ needs.gather-refs.outputs.ref-precice }},\ - PYTHON_BINDINGS_REF:${{ needs.gather-refs.outputs.ref-python-bindings }},\ - CALCULIX_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-calculix-adapter }},\ - DUMUX_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-dumux-adapter }},\ - FENICS_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-fenics-adapter }},\ - FENICSX_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-fenicsx-adapter }},\ - MICRO_MANAGER_REF:${{ needs.gather-refs.outputs.ref-micro-manager }},\ - OPENFOAM_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-openfoam-adapter }},\ - SU2_ADAPTER_REF:${{ needs.gather-refs.outputs.ref-su2-adapter }},\ - TUTORIALS_PR:${{ github.event.number }},\ - TUTORIALS_REF:${{ github.event.pull_request.head.sha }}" + build_args: "TUTORIALS_REF:${{ github.event.pull_request.head.sha }}" system_tests_branch: develop log_level: "INFO" \ No newline at end of file diff --git a/changelog-entries/842.md b/changelog-entries/842.md new file mode 100644 index 000000000..188405b7a --- /dev/null +++ b/changelog-entries/842.md @@ -0,0 +1 @@ +- Moved the resolution of latest commits from the CI workflows to the `Systemtest.py`. Workflows of other repositories that trigger this one can now only specify the build arguments that are needed. [#842](https://github.com/precice/tutorials/pull/842) diff --git a/tools/tests/README.md b/tools/tests/README.md index 374731d92..eb84b7467 100644 --- a/tools/tests/README.md +++ b/tools/tests/README.md @@ -248,32 +248,27 @@ The components mentioned in the Metadata are defined in the central `components. ```yaml openfoam-adapter: - repository: https://github.com/precice/openfoam-adapter template: component-templates/openfoam-adapter.yaml build_arguments: + PLATFORM: + default: "ubuntu_2404" PRECICE_REF: - description: Version of preCICE to use - default: "main" + repository: https://github.com/precice/precice + default: "develop" PRECICE_PRESET: - description: CMake preset of preCICE default: "production-audit" - PLATFORM: - description: Dockerfile platform used - default: "ubuntu_2404" TUTORIALS_REF: - description: Tutorial git reference to use - default: "master" + repository: https://github.com/precice/tutorials + default: "develop" OPENFOAM_EXECUTABLE: - description: exectuable of openfoam to use default: "openfoam2512" OPENFOAM_ADAPTER_REF: - description: Reference/tag of the actual OpenFOAM adapter - default: "master" + repository: https://github.com/precice/openfoam-adapter + default: "develop" ``` This `openfoam-adapter` component has the following attributes: -- `repository`: URL to the Git projects - `template`: A template for a Docker Compose service of this component - `build_arguments`: Arguments passed to the Docker Compose service (arbitrary) diff --git a/tools/tests/components.yaml b/tools/tests/components.yaml index 0a364f592..c460dd396 100644 --- a/tools/tests/components.yaml +++ b/tools/tests/components.yaml @@ -1,254 +1,213 @@ bare: # A default component used when the solver does not have any dependencies apart from preCICE itself - repository: https://github.com/precice/precice template: component-templates/bare.yaml build_arguments: # these things mean something to the docker-service PLATFORM: - description: Dockerfile platform used default: "ubuntu_2404" PRECICE_REF: - description: Version of preCICE to use - default: "main" + repository: https://github.com/precice/precice + default: "develop" PRECICE_PRESET: - description: CMake preset of preCICE default: "production-audit" TUTORIALS_REF: - description: Tutorial git reference to use - default: "master" + repository: https://github.com/precice/tutorials + default: "develop" python-bindings: - repository: https://github.com/precice/python-bindings template: component-templates/python-bindings.yaml build_arguments: PLATFORM: - description: Dockerfile platform used default: "ubuntu_2404" PRECICE_REF: - description: Version of preCICE to use - default: "main" + repository: https://github.com/precice/precice + default: "develop" PRECICE_PRESET: - description: CMake preset of preCICE default: "production-audit" TUTORIALS_REF: - description: Tutorial git reference to use - default: "master" + repository: https://github.com/precice/tutorials + default: "develop" PYTHON_BINDINGS_REF: - description: Git ref of the Python bindings to use - default: "master" + repository: https://github.com/precice/python-bindings + default: "develop" openfoam-adapter: - repository: https://github.com/precice/openfoam-adapter template: component-templates/openfoam-adapter.yaml build_arguments: PLATFORM: - description: Dockerfile platform used default: "ubuntu_2404" PRECICE_REF: - description: Version of preCICE to use - default: "main" + repository: https://github.com/precice/precice + default: "develop" PRECICE_PRESET: - description: CMake preset of preCICE default: "production-audit" TUTORIALS_REF: - description: Tutorial git reference to use - default: "master" + repository: https://github.com/precice/tutorials + default: "develop" OPENFOAM_EXECUTABLE: - description: exectuable of openfoam to use default: "openfoam2512" OPENFOAM_ADAPTER_REF: - description: Reference/tag of the OpenFOAM adapter to use - default: "master" + repository: https://github.com/precice/openfoam-adapter + default: "develop" fenics-adapter: - repository: https://github.com/precice/fenics-adapter template: component-templates/fenics-adapter.yaml build_arguments: PLATFORM: - description: Dockerfile platform used default: "ubuntu_2404" PRECICE_REF: - description: Version of preCICE to use - default: "main" + repository: https://github.com/precice/precice + default: "develop" PRECICE_PRESET: - description: CMake preset of preCICE default: "production-audit" TUTORIALS_REF: - description: Tutorial git reference to use - default: "master" + repository: https://github.com/precice/tutorials + default: "develop" PYTHON_BINDINGS_REF: - description: Git ref of the Python bindings to use - default: "master" + repository: https://github.com/precice/python-bindings + default: "develop" FENICS_ADAPTER_REF: - description: Git ref of the fenics adapter to use - default: "master" + repository: https://github.com/precice/fenics-adapter + default: "develop" fenicsx-adapter: - repository: https://github.com/precice/fenicsx-adapter template: component-templates/fenicsx-adapter.yaml build_arguments: PLATFORM: - description: Dockerfile platform used default: "ubuntu_2404" PRECICE_REF: - description: Version of preCICE to use - default: "main" + repository: https://github.com/precice/precice + default: "develop" PRECICE_PRESET: - description: CMake preset of preCICE default: "production-audit" TUTORIALS_REF: - description: Tutorial git reference to use - default: "master" + repository: https://github.com/precice/tutorials + default: "develop" PYTHON_BINDINGS_REF: - description: Git ref of the Python bindings to use - default: "master" + repository: https://github.com/precice/python-bindings + default: "develop" FENICSX_ADAPTER_REF: - description: Git ref of the fenicsx adapter to use - default: "main" + repository: https://github.com/precice/fenicsx-adapter + default: "develop" nutils-adapter: - repository: https://github.com/precice/nutils-adapter template: component-templates/nutils-adapter.yaml build_arguments: PLATFORM: - description: Dockerfile platform used default: "ubuntu_2404" PRECICE_REF: - description: Version of preCICE to use - default: "main" + repository: https://github.com/precice/precice + default: "develop" PRECICE_PRESET: - description: CMake preset of preCICE default: "production-audit" TUTORIALS_REF: - description: Tutorial git reference to use - default: "master" + repository: https://github.com/precice/tutorials + default: "develop" PYTHON_BINDINGS_REF: - description: Git ref of the Python bindings to use - default: "master" + repository: https://github.com/precice/python-bindings + default: "develop" calculix-adapter: - repository: https://github.com/precice/calculix-adapter template: component-templates/calculix-adapter.yaml build_arguments: PLATFORM: - description: Dockerfile platform used default: "ubuntu_2404" PRECICE_REF: - description: Version of preCICE to use - default: "main" + repository: https://github.com/precice/precice + default: "develop" PRECICE_PRESET: - description: CMake preset of preCICE default: "production-audit" TUTORIALS_REF: - description: Tutorial git reference to use - default: "master" + repository: https://github.com/precice/tutorials + default: "develop" CALCULIX_VERSION: - description: Version of Calculix to use default: "2.20" CALCULIX_ADAPTER_REF: - description: Version of Calculix-Adapter to use - default: "master" + repository: https://github.com/precice/calculix-adapter + default: "develop" su2-adapter: - repository: https://github.com/precice/su2-adapter template: component-templates/su2-adapter.yaml build_arguments: PLATFORM: - description: Dockerfile platform used default: "ubuntu_2404" PRECICE_REF: - description: Version of preCICE to use - default: "main" + repository: https://github.com/precice/precice + default: "develop" PRECICE_PRESET: - description: CMake preset of preCICE default: "production-audit" TUTORIALS_REF: - description: Tutorial git reference to use - default: "master" + repository: https://github.com/precice/tutorials + default: "develop" SU2_VERSION: - description: Version of SU2 to use default: "7.5.1" SU2_ADAPTER_REF: - description: Version of SU2-Adapter to use - default: "master" + repository: https://github.com/precice/su2-adapter + default: "develop" dealii-adapter: - repository: https://github.com/precice/dealii-adapter template: component-templates/dealii-adapter.yaml build_arguments: PLATFORM: - description: Dockerfile platform used default: "ubuntu_2404" PRECICE_REF: - description: Version of preCICE to use - default: "main" + repository: https://github.com/precice/precice + default: "develop" PRECICE_PRESET: - description: CMake preset of preCICE default: "production-audit" TUTORIALS_REF: - description: Tutorial git reference to use - default: "master" + repository: https://github.com/precice/tutorials + default: "develop" DEALII_ADAPTER_REF: - description: Version of deal.ii-adapter to use - default: "master" + repository: https://github.com/precice/dealii-adapter + default: "develop" dumux-adapter: - repository: https://github.com/precice/dumux-adapter template: component-templates/dumux-adapter.yaml build_arguments: PLATFORM: - description: Dockerfile platform used default: "ubuntu_2404" PRECICE_REF: - description: Version of preCICE to use - default: "main" + repository: https://github.com/precice/precice + default: "develop" PRECICE_PRESET: - description: CMake preset of preCICE default: "production-audit" TUTORIALS_REF: - description: Tutorial git reference to use - default: "master" + repository: https://github.com/precice/tutorials + default: "develop" DUNE_VERSION: - description: Version of DUNE to use default: "2.9" DUMUX_VERSION: - description: Version of DuMux to use default: "3.7" DUMUX_ADAPTER_REF: - description: Git ref of the dumux adapter to use - default: "main" + repository: https://github.com/precice/dumux-adapter + default: "develop" mercurydpm-adapter: - repository: https://bitbucket.org/davidscn/mercurydpm template: component-templates/mercurydpm-adapter.yaml build_arguments: PLATFORM: - description: Dockerfile platform used default: "ubuntu_2404" PRECICE_REF: - description: Version of preCICE to use - default: "main" + repository: https://github.com/precice/precice + default: "develop" PRECICE_PRESET: - description: CMake preset of preCICE default: "production-audit" TUTORIALS_REF: - description: Tutorial git reference to use - default: "master" + repository: https://github.com/precice/tutorials + default: "develop" micro-manager: - repository: https://github.com/precice/micro-manager template: component-templates/micro-manager.yaml build_arguments: PLATFORM: - description: Dockerfile platform used default: "ubuntu_2404" PRECICE_REF: - description: Version of preCICE to use - default: "main" + repository: https://github.com/precice/precice + default: "develop" PRECICE_PRESET: - description: CMake preset of preCICE default: "production-audit" TUTORIALS_REF: - description: Tutorial git reference to use - default: "master" + repository: https://github.com/precice/tutorials + default: "develop" MICRO_MANAGER_REF: - description: Version of Micro-Manager to use - default: "0.8.0" + repository: https://github.com/precice/micro-manager + default: "develop" diff --git a/tools/tests/metadata_parser/metdata.py b/tools/tests/metadata_parser/metdata.py index 75b5bb425..1c9b98d55 100644 --- a/tools/tests/metadata_parser/metdata.py +++ b/tools/tests/metadata_parser/metdata.py @@ -11,8 +11,8 @@ class BuildArgument: """Represents a BuildArgument needed to run the docker container""" - description: str - """The description of the parameter.""" + repository: str + """The repository corresponging to a _REF parameter.""" key: str """The name of the parameter.""" @@ -61,15 +61,14 @@ def from_components_yaml(cls, data): """ arguments = [] for argument_name, argument_dict in data['build_arguments'].items(): - # TODO maybe **params - description = argument_dict.get( - 'description', f"No description provided for {argument_name}") + repository = argument_dict.get( + 'repository', f"No repository provided for {argument_name}") key = argument_name default = argument_dict.get('default', None) value_options = argument_dict.get('value_options', None) arguments.append(BuildArgument( - description, key, value_options, default)) + repository, key, value_options, default)) return cls(arguments) @@ -97,7 +96,6 @@ class Component: name: str template: str - repository: str parameters: BuildArguments def __eq__(self, other): @@ -134,10 +132,9 @@ def from_yaml(cls, path): for component_name in data: parameters = BuildArguments.from_components_yaml( data[component_name]) - repository = data[component_name]["repository"] template = data[component_name]["template"] components.append( - Component(component_name, template, repository, parameters)) + Component(component_name, template, parameters)) return cls(components) diff --git a/tools/tests/systemtests/Systemtest.py b/tools/tests/systemtests/Systemtest.py index 93e972f2e..932b6b944 100644 --- a/tools/tests/systemtests/Systemtest.py +++ b/tools/tests/systemtests/Systemtest.py @@ -256,9 +256,21 @@ def __init_args_to_use(self): # Substitute defaults for non-provided, needed arguments for needed_param in needed_parameters: if not needed_param.key in provided_arguments: - logging.warning( - f"No argument provided for needed parameter {needed_param.key}. Substituting with {needed_param.default}") + logging.info( + f"No argument provided for needed parameter {needed_param.key}. Substituting with {needed_param.default}.") self.params_to_use[needed_param.key] = needed_param.default + if needed_param.key.endswith("_REF") and needed_param.key in provided_arguments: + logging.debug( + f"The parameter {needed_param.key} points to the repository {needed_param.repository}.") + # If a commit has already been resolved and added to the params_to_use, it will be propagated to the next test in the test suite. + # To avoid resolving the same commit again, simply check if the key has the same length as the output of _resolve_branch_ref_to_commit. + # The whole process assumes that all components use the same refs. + if len(self.params_to_use[needed_param.key]) == 40: + logging.debug( + f"Git ref {self.params_to_use[needed_param.key]} is 40 characters long and probably already a commit.") + else: + self.params_to_use[needed_param.key] = self._resolve_branch_ref_to_commit( + needed_param.repository, self.params_to_use[needed_param.key]) def __get_docker_services(self) -> Dict[str, str]: """ @@ -365,6 +377,30 @@ def _fetch_ref(self, repository: Path, ref: str): raise RuntimeError( f"An error occurred while fetching origin '{ref}': {e}. Do the values in reference_versions.yaml point to (still) valid Git refs?") + def _resolve_branch_ref_to_commit(self, repository: Path, ref: str) -> Optional[str]: + try: + git_ls_remote_output = subprocess.run([ + "git", + "ls-remote", + os.fspath(repository), + ref, + ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True, timeout=60) + + # If an invalid ref is given, git ls-remote still returns success, but no list + git_remote_refs = git_ls_remote_output.stdout.strip() + if not git_remote_refs: + raise ValueError(f"The git ref {ref} does not appear in the repository {repository}.") + + commit = git_remote_refs.split()[0] + # The output assumes a URL of the form /commits/. Works for GitHub and Bitbucket. + logging.info( + f"Resolved the git ref {ref} of the repository {repository} to {repository}/commits/{commit} .") + return commit if commit else ref + except Exception: + logging.warning( + f"Could not resolve git ref {ref} of the repository {repository} to a commit. Using the given git ref as-is.") + return ref + def _checkout_ref_in_subfolder(self, repository: Path, subfolder: Path, ref: str): try: result = subprocess.run([ diff --git a/tools/tests/systemtests/SystemtestArguments.py b/tools/tests/systemtests/SystemtestArguments.py index 2b7b3eeec..4425472bc 100644 --- a/tools/tests/systemtests/SystemtestArguments.py +++ b/tools/tests/systemtests/SystemtestArguments.py @@ -16,7 +16,8 @@ def from_args(cls, cmd_args): arguments = {} for param in params_provided: key, value = param.split(":") - arguments[key] = value + if len(value) > 0: + arguments[key] = value return cls(arguments) From a8f7ff6ee9843aa9dfdcdc59f2f31174cd4f8bba Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Mon, 15 Jun 2026 23:31:45 +0200 Subject: [PATCH 88/94] Sort components.yaml --- tools/tests/components.yaml | 108 ++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/tools/tests/components.yaml b/tools/tests/components.yaml index c460dd396..c7a4de2b0 100644 --- a/tools/tests/components.yaml +++ b/tools/tests/components.yaml @@ -12,8 +12,8 @@ bare: # A default component used when the solver does not have any dependencies repository: https://github.com/precice/tutorials default: "develop" -python-bindings: - template: component-templates/python-bindings.yaml +calculix-adapter: + template: component-templates/calculix-adapter.yaml build_arguments: PLATFORM: default: "ubuntu_2404" @@ -25,12 +25,14 @@ python-bindings: TUTORIALS_REF: repository: https://github.com/precice/tutorials default: "develop" - PYTHON_BINDINGS_REF: - repository: https://github.com/precice/python-bindings + CALCULIX_VERSION: + default: "2.20" + CALCULIX_ADAPTER_REF: + repository: https://github.com/precice/calculix-adapter default: "develop" -openfoam-adapter: - template: component-templates/openfoam-adapter.yaml +dealii-adapter: + template: component-templates/dealii-adapter.yaml build_arguments: PLATFORM: default: "ubuntu_2404" @@ -42,14 +44,12 @@ openfoam-adapter: TUTORIALS_REF: repository: https://github.com/precice/tutorials default: "develop" - OPENFOAM_EXECUTABLE: - default: "openfoam2512" - OPENFOAM_ADAPTER_REF: - repository: https://github.com/precice/openfoam-adapter + DEALII_ADAPTER_REF: + repository: https://github.com/precice/dealii-adapter default: "develop" -fenics-adapter: - template: component-templates/fenics-adapter.yaml +dumux-adapter: + template: component-templates/dumux-adapter.yaml build_arguments: PLATFORM: default: "ubuntu_2404" @@ -61,15 +61,16 @@ fenics-adapter: TUTORIALS_REF: repository: https://github.com/precice/tutorials default: "develop" - PYTHON_BINDINGS_REF: - repository: https://github.com/precice/python-bindings - default: "develop" - FENICS_ADAPTER_REF: - repository: https://github.com/precice/fenics-adapter + DUNE_VERSION: + default: "2.9" + DUMUX_VERSION: + default: "3.7" + DUMUX_ADAPTER_REF: + repository: https://github.com/precice/dumux-adapter default: "develop" -fenicsx-adapter: - template: component-templates/fenicsx-adapter.yaml +fenics-adapter: + template: component-templates/fenics-adapter.yaml build_arguments: PLATFORM: default: "ubuntu_2404" @@ -84,12 +85,12 @@ fenicsx-adapter: PYTHON_BINDINGS_REF: repository: https://github.com/precice/python-bindings default: "develop" - FENICSX_ADAPTER_REF: - repository: https://github.com/precice/fenicsx-adapter + FENICS_ADAPTER_REF: + repository: https://github.com/precice/fenics-adapter default: "develop" -nutils-adapter: - template: component-templates/nutils-adapter.yaml +fenicsx-adapter: + template: component-templates/fenicsx-adapter.yaml build_arguments: PLATFORM: default: "ubuntu_2404" @@ -104,9 +105,12 @@ nutils-adapter: PYTHON_BINDINGS_REF: repository: https://github.com/precice/python-bindings default: "develop" + FENICSX_ADAPTER_REF: + repository: https://github.com/precice/fenicsx-adapter + default: "develop" -calculix-adapter: - template: component-templates/calculix-adapter.yaml +mercurydpm-adapter: + template: component-templates/mercurydpm-adapter.yaml build_arguments: PLATFORM: default: "ubuntu_2404" @@ -118,14 +122,9 @@ calculix-adapter: TUTORIALS_REF: repository: https://github.com/precice/tutorials default: "develop" - CALCULIX_VERSION: - default: "2.20" - CALCULIX_ADAPTER_REF: - repository: https://github.com/precice/calculix-adapter - default: "develop" -su2-adapter: - template: component-templates/su2-adapter.yaml +micro-manager: + template: component-templates/micro-manager.yaml build_arguments: PLATFORM: default: "ubuntu_2404" @@ -137,14 +136,12 @@ su2-adapter: TUTORIALS_REF: repository: https://github.com/precice/tutorials default: "develop" - SU2_VERSION: - default: "7.5.1" - SU2_ADAPTER_REF: - repository: https://github.com/precice/su2-adapter + MICRO_MANAGER_REF: + repository: https://github.com/precice/micro-manager default: "develop" -dealii-adapter: - template: component-templates/dealii-adapter.yaml +nutils-adapter: + template: component-templates/nutils-adapter.yaml build_arguments: PLATFORM: default: "ubuntu_2404" @@ -156,12 +153,12 @@ dealii-adapter: TUTORIALS_REF: repository: https://github.com/precice/tutorials default: "develop" - DEALII_ADAPTER_REF: - repository: https://github.com/precice/dealii-adapter + PYTHON_BINDINGS_REF: + repository: https://github.com/precice/python-bindings default: "develop" -dumux-adapter: - template: component-templates/dumux-adapter.yaml +openfoam-adapter: + template: component-templates/openfoam-adapter.yaml build_arguments: PLATFORM: default: "ubuntu_2404" @@ -173,16 +170,14 @@ dumux-adapter: TUTORIALS_REF: repository: https://github.com/precice/tutorials default: "develop" - DUNE_VERSION: - default: "2.9" - DUMUX_VERSION: - default: "3.7" - DUMUX_ADAPTER_REF: - repository: https://github.com/precice/dumux-adapter + OPENFOAM_EXECUTABLE: + default: "openfoam2512" + OPENFOAM_ADAPTER_REF: + repository: https://github.com/precice/openfoam-adapter default: "develop" -mercurydpm-adapter: - template: component-templates/mercurydpm-adapter.yaml +python-bindings: + template: component-templates/python-bindings.yaml build_arguments: PLATFORM: default: "ubuntu_2404" @@ -194,9 +189,12 @@ mercurydpm-adapter: TUTORIALS_REF: repository: https://github.com/precice/tutorials default: "develop" + PYTHON_BINDINGS_REF: + repository: https://github.com/precice/python-bindings + default: "develop" -micro-manager: - template: component-templates/micro-manager.yaml +su2-adapter: + template: component-templates/su2-adapter.yaml build_arguments: PLATFORM: default: "ubuntu_2404" @@ -208,6 +206,8 @@ micro-manager: TUTORIALS_REF: repository: https://github.com/precice/tutorials default: "develop" - MICRO_MANAGER_REF: - repository: https://github.com/precice/micro-manager + SU2_VERSION: + default: "7.5.1" + SU2_ADAPTER_REF: + repository: https://github.com/precice/su2-adapter default: "develop" From f1ba801bb61720f9b54f59566094ce31c00a7376 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Mon, 15 Jun 2026 23:35:43 +0200 Subject: [PATCH 89/94] Remove remnants of old generate_reference_data.py --- .github/workflows/generate_reference_results_workflow.yml | 5 +---- tools/tests/README.md | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/generate_reference_results_workflow.yml b/.github/workflows/generate_reference_results_workflow.yml index 48dfd62ee..862a7268b 100644 --- a/.github/workflows/generate_reference_results_workflow.yml +++ b/.github/workflows/generate_reference_results_workflow.yml @@ -66,14 +66,11 @@ jobs: - name: Run tests run: | cd tools/tests - test -f generate_reference_results.py && export GENERATE_REF_RESULTS=generate_reference_results.py - test -f generate_reference_data.py && export GENERATE_REF_RESULTS=generate_reference_data.py - echo "Selected $GENERATE_REF_RESULTS to run" SUITES_ARGS=() if [ -n "${{ inputs.suites }}" ]; then SUITES_ARGS+=(--suites "${{ inputs.suites }}") fi - python "$GENERATE_REF_RESULTS" --log-level="${{inputs.log_level}}" "${SUITES_ARGS[@]}" + python generate_reference_results.py --log-level="${{inputs.log_level}}" "${SUITES_ARGS[@]}" cd ../../ - name: Create commit if: success() diff --git a/tools/tests/README.md b/tools/tests/README.md index eb84b7467..247eb6c70 100644 --- a/tools/tests/README.md +++ b/tools/tests/README.md @@ -115,14 +115,14 @@ In order for the systemtests to pick up the tutorial we need to define a `metada ### Adding testsuites -To add a testsuite just open the `tests.yaml` file and use the output of `python print_case_combinations.py` to add the right case combinations you want to test. Note that you can specify a `reference_result` which is not yet present. The `generate_reference_data.py` will pick that up and create it for you. +To add a testsuite just open the `tests.yaml` file and use the output of `python print_case_combinations.py` to add the right case combinations you want to test. Note that you can specify a `reference_result` which is not yet present. The `generate_reference_results.py` will pick that up and create it for you. Note that its important to carefully check the paths of the `reference_result` in order to not have typos in there. Also note that same cases in different testsuites should use the same `reference_result`. To cap the preCICE simulation time for a specific test without editing `precice-config.xml`, add an optional `max_time` (positive float, overrides ``) or `max_time_windows` (positive integer, overrides ``) field to the tutorial entry. Applies to both test runs and reference result generation. ### Generate reference results -Since we need data to compare against, you need to run `python generate_reference_data.py`. This process might take a while. +Since we need data to compare against, you need to run `python generate_reference_results.py`. This process might take a while. Please include the generated reference results in the pull request as they are strongly connected to the new testsuites. ## Adding repositories From 30b533639469f3822ef8ae22a4b3800e09758320 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Mon, 15 Jun 2026 23:40:33 +0200 Subject: [PATCH 90/94] Rename --log-level to --log_level for consistency --- .github/workflows/generate_reference_results_workflow.yml | 4 ++-- .github/workflows/run_testsuite_workflow.yml | 2 +- tools/tests/README.md | 4 ++-- tools/tests/build_docker_images.py | 4 ++-- tools/tests/generate_reference_results.py | 4 ++-- tools/tests/print_case_combinations.py | 4 ++-- tools/tests/print_metadata.py | 4 ++-- tools/tests/print_test_suites.py | 4 ++-- tools/tests/systemtests.py | 4 ++-- 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/generate_reference_results_workflow.yml b/.github/workflows/generate_reference_results_workflow.yml index 862a7268b..0c4b8e3e0 100644 --- a/.github/workflows/generate_reference_results_workflow.yml +++ b/.github/workflows/generate_reference_results_workflow.yml @@ -35,7 +35,7 @@ jobs: echo "- Commit message: \`${{ inputs.commit_msg }}\`" echo "- Run \`docker-system-prune\` before running tests: \`${{ inputs.clean_docker }}\`" echo "- Log level: \`${{ inputs.log_level }}\`" - echo "- Running \`generate_reference_results.py --suite ${{inputs.suites}} --log-level ${{inputs.log_level}}\`" + echo "- Running \`generate_reference_results.py --suite ${{inputs.suites}} --log_level ${{inputs.log_level}}\`" } >> "$GITHUB_STEP_SUMMARY" - name: Move LFS URL to local LFS server run: | @@ -70,7 +70,7 @@ jobs: if [ -n "${{ inputs.suites }}" ]; then SUITES_ARGS+=(--suites "${{ inputs.suites }}") fi - python generate_reference_results.py --log-level="${{inputs.log_level}}" "${SUITES_ARGS[@]}" + python generate_reference_results.py --log_level="${{inputs.log_level}}" "${SUITES_ARGS[@]}" cd ../../ - name: Create commit if: success() diff --git a/.github/workflows/run_testsuite_workflow.yml b/.github/workflows/run_testsuite_workflow.yml index 38a6df0a9..23659e354 100644 --- a/.github/workflows/run_testsuite_workflow.yml +++ b/.github/workflows/run_testsuite_workflow.yml @@ -79,7 +79,7 @@ jobs: - name: Run tests run: | cd tools/tests - python systemtests.py --build_args=${{ inputs.build_args}} --suites=${{ inputs.suites}} --log-level=${{ inputs.log_level}} + python systemtests.py --build_args=${{ inputs.build_args}} --suites=${{ inputs.suites}} --log_level=${{ inputs.log_level}} cd ../../ - name: Archive system test logs if: ${{ always() }} diff --git a/tools/tests/README.md b/tools/tests/README.md index 247eb6c70..35c983a1f 100644 --- a/tools/tests/README.md +++ b/tools/tests/README.md @@ -75,7 +75,7 @@ Example output: ```text Run cd tools/tests cd tools/tests - python systemtests.py --build_args=PRECICE_REF:v3.1.1,PRECICE_PRESET:production-audit,OPENFOAM_ADAPTER_REF:v1.3.0,PYTHON_BINDINGS_REF:v3.1.0,FENICS_ADAPTER_REF:v2.1.0 --suites=fenics-adapter --log-level=DEBUG + python systemtests.py --build_args=PRECICE_REF:v3.1.1,PRECICE_PRESET:production-audit,OPENFOAM_ADAPTER_REF:v1.3.0,PYTHON_BINDINGS_REF:v3.1.0,FENICS_ADAPTER_REF:v2.1.0 --suites=fenics-adapter --log_level=DEBUG cd ../../ shell: /usr/bin/bash -e {0} INFO: About to run the following systemtest in the directory /home/precice/runners_root/actions-runner-tutorial/_work/tutorials/tutorials/runs: @@ -88,7 +88,7 @@ DEBUG: Building docker image for Flow over heated plate (fluid-openfoam, solid-f DEBUG: Running tutorial Flow over heated plate (fluid-openfoam, solid-fenics) DEBUG: Running fieldcompare for Flow over heated plate (fluid-openfoam, solid-fenics) DEBUG: extracting /home/precice/runners_root/actions-runner-tutorial/_work/tutorials/tutorials/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz into /home/precice/runners_root/actions-runner-tutorial/_work/tutorials/tutorials/runs/flow-over-heated-plate_fluid-openfoam-solid-fenics_2023-11-19-211723/reference_results -Using log-level: DEBUG +Using log_level: DEBUG +---------------------------------------------------------+---------+-------------------+-----------------+-----------------------+ | systemtest | success | building time [s] | solver time [s] | fieldcompare time [s] | CRITICAL: Fieldcompare returned non zero exit code, therefore Flow over heated plate (fluid-openfoam, solid-fenics) failed diff --git a/tools/tests/build_docker_images.py b/tools/tests/build_docker_images.py index 4cb34e252..6a0d1cd07 100644 --- a/tools/tests/build_docker_images.py +++ b/tools/tests/build_docker_images.py @@ -23,7 +23,7 @@ def main(): parser.add_argument('--rundir', type=str, help='Directory to run the systemstests in.', nargs='?', const=PRECICE_TESTS_RUN_DIR, default=PRECICE_TESTS_RUN_DIR) - parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], + parser.add_argument('--log_level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], default='INFO', help='Set the logging level') # Parse the command-line arguments @@ -32,7 +32,7 @@ def main(): # Configure logging based on the provided log level logging.basicConfig(level=args.log_level, format='%(levelname)s: %(message)s') - print(f"Using log-level: {args.log_level}") + print(f"Using log_level: {args.log_level}") systemtests_to_run = [] available_tutorials = Tutorials.from_path(PRECICE_TUTORIAL_DIR) diff --git a/tools/tests/generate_reference_results.py b/tools/tests/generate_reference_results.py index a9bac5ac1..d7470c8a6 100644 --- a/tools/tests/generate_reference_results.py +++ b/tools/tests/generate_reference_results.py @@ -89,14 +89,14 @@ def main(): parser.add_argument('--suites', type=str, help='Comma-separated test suites to generate reference results for. ' 'If not specified, all suites are used.') - parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], + parser.add_argument('--log_level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], default='INFO', help='Set the logging level') args = parser.parse_args() logging.basicConfig(level=args.log_level, format='%(levelname)s: %(message)s') - print(f"Using log-level: {args.log_level}") + print(f"Using log_level: {args.log_level}") run_directory = Path(args.rundir) diff --git a/tools/tests/print_case_combinations.py b/tools/tests/print_case_combinations.py index 25e318918..a891209e0 100644 --- a/tools/tests/print_case_combinations.py +++ b/tools/tests/print_case_combinations.py @@ -8,14 +8,14 @@ def main(): parser = argparse.ArgumentParser(description='Prints available Metadata for tutorials') - parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], + parser.add_argument('--log_level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], default='INFO', help='Set the logging level') args = parser.parse_args() # Configure logging based on the provided log level logging.basicConfig(level=args.log_level, format='%(levelname)s: %(message)s') - print(f"Using log-level: {args.log_level}") + print(f"Using log_level: {args.log_level}") available_tutorials = Tutorials.from_path(PRECICE_TUTORIAL_DIR) diff --git a/tools/tests/print_metadata.py b/tools/tests/print_metadata.py index 500e549c9..9ed7a3648 100644 --- a/tools/tests/print_metadata.py +++ b/tools/tests/print_metadata.py @@ -7,14 +7,14 @@ def main(): parser = argparse.ArgumentParser(description='Prints available Metadata for tutorials') - parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], + parser.add_argument('--log_level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], default='INFO', help='Set the logging level') args = parser.parse_args() # Configure logging based on the provided log level logging.basicConfig(level=args.log_level, format='%(levelname)s: %(message)s') - print(f"Using log-level: {args.log_level}") + print(f"Using log_level: {args.log_level}") available_tutorials = Tutorials.from_path(PRECICE_TUTORIAL_DIR) print("Fount the following tutorials read from the metadata.yaml") diff --git a/tools/tests/print_test_suites.py b/tools/tests/print_test_suites.py index 6fc1e4039..14d69ef2b 100644 --- a/tools/tests/print_test_suites.py +++ b/tools/tests/print_test_suites.py @@ -9,14 +9,14 @@ def main(): parser = argparse.ArgumentParser(description='Prints available Test Suites') - parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], + parser.add_argument('--log_level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], default='INFO', help='Set the logging level') args = parser.parse_args() # Configure logging based on the provided log level logging.basicConfig(level=args.log_level, format='%(levelname)s: %(message)s') - print(f"Using log-level: {args.log_level}") + print(f"Using log_level: {args.log_level}") available_tutorials = Tutorials.from_path(PRECICE_TUTORIAL_DIR) available_testsuites = TestSuites.from_yaml( diff --git a/tools/tests/systemtests.py b/tools/tests/systemtests.py index 33771cbc6..bc56a6a84 100644 --- a/tools/tests/systemtests.py +++ b/tools/tests/systemtests.py @@ -34,7 +34,7 @@ def main(): parser.add_argument('--rundir', type=str, help='Directory to run the systemstests in.', nargs='?', const=PRECICE_TESTS_RUN_DIR, default=PRECICE_TESTS_RUN_DIR) - parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], + parser.add_argument('--log_level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], default='INFO', help='Set the logging level') # Parse the command-line arguments @@ -114,7 +114,7 @@ def _group_end() -> None: for number, systemtest in enumerate(systemtests_to_run, start=1): print(f"{number}. {systemtest}", flush=True) print(flush=True) - print(f"Using log-level: {args.log_level}", flush=True) + print(f"Using log_level: {args.log_level}", flush=True) results = [] for number, systemtest in enumerate(systemtests_to_run, start=1): From 5b654590074ecd774be946a006e1f8022b3889fc Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Tue, 16 Jun 2026 12:28:45 +0200 Subject: [PATCH 91/94] Rename the system tests GitHub Actions workflows (#843) --- ... => generate-reference-results-manual.yml} | 2 +- ...low.yml => generate-reference-results.yml} | 2 +- .github/workflows/run_testsuite_manual.yml | 42 ------------------- .../system-tests-latest-components.yml | 4 +- .github/workflows/system-tests-pr.yml | 2 +- ...estsuite_workflow.yml => system-tests.yml} | 4 +- changelog-entries/843.md | 1 + tools/tests/README.md | 24 +++++++---- 8 files changed, 24 insertions(+), 57 deletions(-) rename .github/workflows/{generate_reference_results_manual.yml => generate-reference-results-manual.yml} (94%) rename .github/workflows/{generate_reference_results_workflow.yml => generate-reference-results.yml} (98%) delete mode 100644 .github/workflows/run_testsuite_manual.yml rename .github/workflows/{run_testsuite_workflow.yml => system-tests.yml} (98%) create mode 100644 changelog-entries/843.md diff --git a/.github/workflows/generate_reference_results_manual.yml b/.github/workflows/generate-reference-results-manual.yml similarity index 94% rename from .github/workflows/generate_reference_results_manual.yml rename to .github/workflows/generate-reference-results-manual.yml index 1d0d52d08..413223f66 100644 --- a/.github/workflows/generate_reference_results_manual.yml +++ b/.github/workflows/generate-reference-results-manual.yml @@ -32,7 +32,7 @@ on: jobs: generate_reference_results_manual: - uses: ./.github/workflows/generate_reference_results_workflow.yml + uses: ./.github/workflows/generate-reference-results.yml with: from_ref: ${{ inputs.from_ref }} commit_msg: ${{ inputs.commit_msg }} diff --git a/.github/workflows/generate_reference_results_workflow.yml b/.github/workflows/generate-reference-results.yml similarity index 98% rename from .github/workflows/generate_reference_results_workflow.yml rename to .github/workflows/generate-reference-results.yml index 0c4b8e3e0..e22e28f63 100644 --- a/.github/workflows/generate_reference_results_workflow.yml +++ b/.github/workflows/generate-reference-results.yml @@ -1,4 +1,4 @@ -name: Generate reference results workflow +name: Generate reference results (called) on: workflow_call: inputs: diff --git a/.github/workflows/run_testsuite_manual.yml b/.github/workflows/run_testsuite_manual.yml deleted file mode 100644 index 25c4e820e..000000000 --- a/.github/workflows/run_testsuite_manual.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Run Testsuite (manual) # Unfortunately, we cannot modify the name: https://github.community/t/github-actions-dynamic-name-of-the-workflow-with-workflow-dispatch/150327 -on: - workflow_dispatch: - inputs: - suites: - description: 'Test suites to execute (comma-separated)' - required: true - default: 'release' - type: string - build_args: - description: 'Build arguments (override component defaults)' - default: 'PRECICE_REF:v3.4.1,TUTORIALS_REF:develop' - type: string - system_tests_branch: - description: 'Git ref for tools/tests/' - default: 'develop' - type: string - upload_artifacts: - description: 'Upload the complete case files also on success (always true for failure)' - default: 'TRUE' - type: choice - options: - - 'FALSE' - - 'TRUE' - log_level: - description: 'Logging verbosity of systemtests.py' - default: 'INFO' - type: choice - options: - - 'INFO' - - 'DEBUG' - - -jobs: - run_testsuite_manual: - uses: ./.github/workflows/run_testsuite_workflow.yml - with: - suites: ${{ inputs.suites }} - build_args: ${{ inputs.build_args }} - system_tests_branch: ${{ inputs.system_tests_branch }} - log_level: ${{ inputs.log_level }} - upload_artifacts: ${{ inputs.upload_artifacts }} diff --git a/.github/workflows/system-tests-latest-components.yml b/.github/workflows/system-tests-latest-components.yml index f48ab4eb1..4109768b7 100644 --- a/.github/workflows/system-tests-latest-components.yml +++ b/.github/workflows/system-tests-latest-components.yml @@ -1,4 +1,4 @@ -name: System tests (latest components) +name: System tests (manual/nightly) on: schedule: @@ -52,7 +52,7 @@ on: jobs: run-system-tests: name: Trigger system tests - uses: precice/tutorials/.github/workflows/run_testsuite_workflow.yml@develop + uses: precice/tutorials/.github/workflows/system-tests.yml@develop with: suites: ${{ inputs.suites || 'release' }} build_args: "PRECICE_REF:${{ inputs.ref-precice }},\ diff --git a/.github/workflows/system-tests-pr.yml b/.github/workflows/system-tests-pr.yml index 98b32a349..a2ab0fc48 100644 --- a/.github/workflows/system-tests-pr.yml +++ b/.github/workflows/system-tests-pr.yml @@ -8,7 +8,7 @@ jobs: run-system-tests: name: Trigger system tests if: ${{ github.event.label.name == 'trigger-system-tests' }} - uses: precice/tutorials/.github/workflows/run_testsuite_workflow.yml@develop + uses: precice/tutorials/.github/workflows/system-tests.yml@develop with: suites: release build_args: "TUTORIALS_REF:${{ github.event.pull_request.head.sha }}" diff --git a/.github/workflows/run_testsuite_workflow.yml b/.github/workflows/system-tests.yml similarity index 98% rename from .github/workflows/run_testsuite_workflow.yml rename to .github/workflows/system-tests.yml index 23659e354..53e6bb0a6 100644 --- a/.github/workflows/run_testsuite_workflow.yml +++ b/.github/workflows/system-tests.yml @@ -1,4 +1,4 @@ -name: Run Testsuite reusable workflow +name: System tests (called) on: workflow_call: inputs: @@ -22,7 +22,7 @@ on: default: 'TRUE' type: string jobs: - run_testsuite: + run_system_tests: runs-on: [self-hosted, linux, x64, precice-tests-vm] steps: - name: Display a quick job summary diff --git a/changelog-entries/843.md b/changelog-entries/843.md new file mode 100644 index 000000000..7b1a24b30 --- /dev/null +++ b/changelog-entries/843.md @@ -0,0 +1 @@ +- Renamed the system tests GitHub Actions workflows [#843](https://github.com/precice/tutorials/pull/843) \ No newline at end of file diff --git a/tools/tests/README.md b/tools/tests/README.md index 35c983a1f..164ed6962 100644 --- a/tools/tests/README.md +++ b/tools/tests/README.md @@ -25,9 +25,17 @@ Workflow for the preCICE v3 release testing: 3. Trigger the GitHub Actions Workflow. Until we merge the workflow to develop, this can only happen via the [GitHub CLI](https://cli.github.com/): ```bash - gh workflow run run_testsuite_manual.yml -f suites=release -f build_args="PRECICE_REF:v3.1.1,PRECICE_PRESET:production-audit,OPENFOAM_ADAPTER_REF:v1.3.0,PYTHON_BINDINGS_REF:v3.1.0,FENICS_ADAPTER_REF:v2.1.0,SU2_VERSION:7.5.1,SU2_ADAPTER_REF:64d4aff,DEALII_ADAPTER_REF:02c5d18,TUTORIALS_REF:340b447" --ref=develop + gh workflow run system-tests-latest-components.yml -f suites=release ``` + More arguments are available, for example: + + ```bash + gh workflow run system-tests-latest-components.yml -f suites=release -f build_args="PLATFORM:ubuntu2404,PRECICE_REF:develop" -f log_level="DEBUG" --ref=develop + ``` + + The `build_args` override the defaults set in `tools/tests/components.yaml`. + 4. Go to the tutorials [Actions](https://github.com/precice/tutorials/actions) page and find the running workflow 5. Check the status and the runtimes of each tutorial: @@ -54,18 +62,18 @@ To be able to fill in the right case tuple into the `tests.yaml`, you can use th ## Running the system tests on GitHub Actions -Go to Actions > [Run Testsuite (manual)](https://github.com/precice/tutorials/actions/workflows/run_testsuite_manual.yml) to see this workflow. +Go to Actions > [System tests (latest components)](https://github.com/precice/tutorials/actions/workflows/system-tests-latest-components.yml) to see this workflow. After bringing these changes to `master`, the manual triggering option should be visible on the top right. Until that happens, we can only trigger this workflow manually from the [GitHub CLI](https://github.blog/changelog/2021-04-15-github-cli-1-9-enables-you-to-work-with-github-actions-from-your-terminal/): ```shell -gh workflow run run_testsuite_manual.yml -f suites=fenics-adapter --ref=develop +gh workflow run system-tests-latest-components.yml -f suites=fenics-adapter --ref=develop ``` Another example, to use the latest releases and enable debug information of the tests: ```shell -gh workflow run run_testsuite_manual.yml -f suites=fenics-adapter -f build_args="PRECICE_REF:v3.1.1,PRECICE_PRESET:production-audit,OPENFOAM_ADAPTER_REF:v1.3.0,PYTHON_BINDINGS_REF:v3.1.0,FENICS_ADAPTER_REF:v2.1.0,SU2_VERSION:7.5.1,SU2_ADAPTER_REF:64d4aff,DEALII_ADAPTER_REF:02c5d18,TUTORIALS_REF:340b447" -f log_level=DEBUG --ref=develop +gh workflow run system-tests-latest-components.yml -f suites=fenics-adapter -f build_args="PRECICE_REF:v3.1.1,PRECICE_PRESET:production-audit,OPENFOAM_ADAPTER_REF:v1.3.0,PYTHON_BINDINGS_REF:v3.1.0,FENICS_ADAPTER_REF:v2.1.0,SU2_VERSION:7.5.1,SU2_ADAPTER_REF:64d4aff,DEALII_ADAPTER_REF:02c5d18,TUTORIALS_REF:340b447" -f log_level=DEBUG --ref=develop ``` where the `*_REF` should be a specific [commit-ish](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefcommit-ishacommit-ishalsocommittish). @@ -152,8 +160,8 @@ The multi-stage Docker build allows building each component separately from the Metadata and workflow/script files: - `.github/workflows/` - - `run_testsuite_workflow.yml`: workflow for running the tests, triggered by other workflows (e.g., other repositories) - - `run_testsuite_manual.yml`: manual triggering front-end for `run_testsuite_workflow.yml` + - `system-tests.yml`: workflow for running the tests, triggered by other workflows (e.g., other repositories) + - `system-tests-latest-components.yml`: manual triggering front-end for `system-tests.yml` - `flow-over-a-heated-plate/` - `fluid-openfoam/` - `run.sh`: describes how to execute the respective case @@ -276,10 +284,10 @@ This `openfoam-adapter` component has the following attributes: Since the docker containers are still a bit mixed in terms of capabilities and support for different build_argument combinations the following rules apply: -- A build argument ending in `_REF` refers to a git commit-ish (like a tag or commit) being used to build the image. It is important to not use branch names here as we heavily rely on Docker's build cache to speedup things. But since the input variable to the docker builder will not change, we might have wrong cache hits. +- A build argument ending in `_REF` refers to a git commit-ish (like a tag or commit) being used to build the image. - Some workflows set variables ending in `_PR`. These specify the GitHub pull request which provides the above `_REF` and can be on a fork. - A build argument ending in `_VERSION` refers to the version of a third-party dependency to use (e.g., DUNE). -- All other build_arguments are free of rules and up to the container maintainer. +- All other `build_arguments` are free of rules and up to the container maintainer. ### Component templates From 554491c8f65774905e898395101b89b720754545 Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Tue, 16 Jun 2026 16:02:34 +0200 Subject: [PATCH 92/94] Rewrite the system tests documentation (#846) --- .github/workflows/system-tests-pr.yml | 2 - tools/tests/README.md | 247 ++++++++++++-------------- tools/tests/build_docker_images.py | 4 +- tools/tests/reference_versions.yaml | 2 +- tools/tests/systemtests.py | 4 +- 5 files changed, 116 insertions(+), 143 deletions(-) diff --git a/.github/workflows/system-tests-pr.yml b/.github/workflows/system-tests-pr.yml index a2ab0fc48..a2b28f077 100644 --- a/.github/workflows/system-tests-pr.yml +++ b/.github/workflows/system-tests-pr.yml @@ -12,5 +12,3 @@ jobs: with: suites: release build_args: "TUTORIALS_REF:${{ github.event.pull_request.head.sha }}" - system_tests_branch: develop - log_level: "INFO" \ No newline at end of file diff --git a/tools/tests/README.md b/tools/tests/README.md index 164ed6962..f60eb5031 100644 --- a/tools/tests/README.md +++ b/tools/tests/README.md @@ -6,142 +6,155 @@ keywords: pages, development, tests summary: "Test complete simulations combining preCICE components of specific versions." --- -The tutorials repository hosts cases that need multiple components from the preCICE ecosystem to run. This directory provides tools that can automatically run complete simulations, using different versions of each component, and compare the results to references. While the main purpose is to run complete tests in the continuous integration workflows of preCICE, you can also run these tests on your laptop. +The tutorials repository hosts cases that need multiple components from the preCICE ecosystem to run. Te system tests automatically run tutorials combining the required components and compare the numerical results to references. While the main purpose is to run complete tests in the continuous integration workflows of preCICE, you can also run these tests on your laptop. -Read more about the system tests in the publication [System Regression Tests for the preCICE Coupling Ecosystem](https://doi.org/10.14279/eceasst.v83.2614). +## Running -[![ECEASST](https://img.shields.io/badge/DOI-10.14279%2Feceasst.v83.2614-green)](https://doi.org/10.14279/eceasst.v83.2614) +The main workflow for the user is executing the `systemtests.py` script, which is the same that the [GitHub Actions workflow](https://github.com/precice/tutorials/actions/workflows/system-tests-latest-components.yml) executes. Depending on the options given to the script, it reads in the respective metadata files and generates `docker-compose.yaml` files that can start a fully-defined coupled simulation. For arguments that are not provided, default values from `components.yaml` are used. + +### Manual and nightly runs on GitHub + +The [System tests (manual/nightly)](https://github.com/precice/tutorials/actions/workflows/system-tests-latest-components.yml) workflow executes the `release` test suite nightly and can also be triggered manually. + +On the workflow page, click `Run workflow`. The default values will execute the `release` test suite using the latest `develop` branches of every component. If you want to override the version of some component, specify it in the respective field. Commit hashes, branches, and tags are all accepted. Branches and tags will get automatically resolved to their current commit on GitHub before starting any test, and all tests will use the same version of any common component. -## Running the system tests +The available test suites are found in [`tests.yaml`](https://github.com/precice/tutorials/blob/develop/tools/tests/tests.yaml) and common values are: -The main workflow for the user is executing the `systemtests.py` script. Depending on the options given to the script, it reads in the respective metadata files and generates `docker-compose.yaml` files that can start a fully-defined coupled simulation. +- `quickstart`, `elastic-tube-1d`, or any other tutorial (see [exceptions](https://github.com/precice/tutorials/issues/448)) +- `openfoam-adapter`, `micro-manager`, `fmi-runner`, or similar test cases involving the respective component +- `precice` is a subset of cases that cover a range of preCICE features +- `release` is for all available but some very long or known to fail tests +- `extra` is for some longer tests +- `expected-to-fail`, `selected`, and `system-tests-dev` for some special cases -### Running the tests for a preCICE release +The `Use workflow from` is a default option of GitHub Actions that concerns the GHA workflow file itself. -Workflow for the preCICE v3 release testing: +### Running from a pull request -1. Collect the Git commits/tags of all components you want to test. The caching mechanism cannot detect changes based on branch names. The same effect might be encountered when rebasing and force-pushing the release branch. -2. In your terminal, navigate to the tutorials repository -3. Trigger the GitHub Actions Workflow. Until we merge the workflow to develop, this can only happen via the [GitHub CLI](https://cli.github.com/): +Several repositories include a workflow that allows triggering the system tests by adding the `trigger-system-tests` label to the pull request. The event is triggered only at the moment of adding the label, so you need to remove the label and add it again if needed. - ```bash - gh workflow run system-tests-latest-components.yml -f suites=release - ``` +See the [system tests workflow in the OpenFOAM adapter](https://github.com/precice/openfoam-adapter/blob/develop/.github/workflows/system-tests.yaml) for an example. - More arguments are available, for example: +### Running from the GitHub CLI - ```bash - gh workflow run system-tests-latest-components.yml -f suites=release -f build_args="PLATFORM:ubuntu2404,PRECICE_REF:develop" -f log_level="DEBUG" --ref=develop - ``` +The [GitHub CLI](https://cli.github.com/) allows triggering workflows of a GitHub project. For example: - The `build_args` override the defaults set in `tools/tests/components.yaml`. +```bash +gh workflow run system-tests-latest-components.yml -f suites=release +``` + +More arguments are available, for example: + +```bash +gh workflow run system-tests-latest-components.yml -f suites=release -f build_args="PLATFORM:ubuntu2404,PRECICE_REF:develop" -f log_level="DEBUG" --ref=develop +``` -4. Go to the tutorials [Actions](https://github.com/precice/tutorials/actions) page and find the running workflow -5. Check the status and the runtimes of each tutorial: +The `build_args` override the defaults set in `tools/tests/components.yaml`. - - Very small build times mean that the test is using cached container layers - - Most commonly, you will see tests failing with `Fieldcompare returned non zero exit code`. You will need to check the logs, but if the fieldcompare time is significant, this typically means that the numerical results differ above the tolerance (the test works!). +### Running locally -6. Download the build artifacts from Summary > runs. +To run locally, you will need Docker, Docker Compose, and Python 3. - - In there, inspect the per-stage logs (`system-tests-build.log`, `system-tests-run.log`, `system-tests-compare.log`). - - The produced results are in `precice-exports/`, the reference results in `reference-results-unpacked`. - - Compare using, e.g., ParaView or [fieldcompare](https://gitlab.com/dglaeser/fieldcompare): `fieldcompare dir precice-exports/ reference/`. The `--diff` option will give you `precice-exports/diff_*.vtu` files, while you can also try different tolerances with `-rtol` and `-atol`. +Navigate into the directory `tools/tests/` of the tutorials, make a Python virtual environment, and install the dependencies: -### Running specific test suites +```bash +python -m venv .venv && source .venv/bin/activate +python -m pip install -r requirements.txt +``` To test a certain test-suite defined in `tests.yaml`, use: ```bash -python3 systemtests.py --suites=fenics-adapter, +python systemtests.py --suites=quickstart ``` -To discover all tests, use `python print_test_suites.py`. +This will build and connect Docker containers and run tutorials in the `runs/` directory in the root of the tutorials. -To be able to fill in the right case tuple into the `tests.yaml`, you can use the `python3 print_case_combinations.py` script. +To clean up at the end, you might want to run a `docker system prune -a` and remove the `runs/` directory to save space. -## Running the system tests on GitHub Actions +There are also some auxiliary scripts (run without arguments): -Go to Actions > [System tests (latest components)](https://github.com/precice/tutorials/actions/workflows/system-tests-latest-components.yml) to see this workflow. +- `print_test_suites.py`: Print all test suites defined in `tests.yaml` +- `print_case_combinations.py`: Print all possible combinations of participants in a tutorial, using its `metadata.yaml`. -After bringing these changes to `master`, the manual triggering option should be visible on the top right. Until that happens, we can only trigger this workflow manually from the [GitHub CLI](https://github.blog/changelog/2021-04-15-github-cli-1-9-enables-you-to-work-with-github-actions-from-your-terminal/): +## Understanding the logs -```shell -gh workflow run system-tests-latest-components.yml -f suites=fenics-adapter --ref=develop -``` +For a local run, look into the `tutorials/runs/` directory, where you will find a time-stamped directory for every test executed. -Another example, to use the latest releases and enable debug information of the tests: +Each of these directories includes the usual tutorial case files and logs, as well as: -```shell -gh workflow run system-tests-latest-components.yml -f suites=fenics-adapter -f build_args="PRECICE_REF:v3.1.1,PRECICE_PRESET:production-audit,OPENFOAM_ADAPTER_REF:v1.3.0,PYTHON_BINDINGS_REF:v3.1.0,FENICS_ADAPTER_REF:v2.1.0,SU2_VERSION:7.5.1,SU2_ADAPTER_REF:64d4aff,DEALII_ADAPTER_REF:02c5d18,TUTORIALS_REF:340b447" -f log_level=DEBUG --ref=develop -``` +1. `system-tests-build.log`: The logs of building the respective components. +2. `system-tests-run.log`: The logs of running the simulation (intermixed, from all participants). +3. `system-tests-compare.log`: The logs for the comparison to the reference results. + +In addition, in the directories of the cases executed, you can find `system-tests-.log` files. + +### Numerical regressions + +When the tests fail at the results comparison step, this typically means that there are numerical regressions (unless something wrong went undetected in a previous step). We use [fieldcompare](https://gitlab.com/dglaeser/fieldcompare) to compare all [preCICE exports](https://precice.org/configuration-export.html) to reference results generated from a previous run. Relevant files: + +- `precice-exports/`: The coupling meshes of the test run. +- `reference-results/`: The coupling meshes of the reference run, as stored on Git LFS, expanded into `reference-results-unpacked`. +- `diff-results/`: Numerical difference of the results in the two directories (computed with `fieldcompare dir --diff precice-exports/ reference/`). These are only present on failed comparisons. + +To reproduce the comparison locally, use the [same fieldcompare command](https://github.com/precice/tutorials/blob/develop/tools/tests/docker-compose.field_compare.template.yaml): -where the `*_REF` should be a specific [commit-ish](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefcommit-ishacommit-ishalsocommittish). - -Example output: - -```text -Run cd tools/tests - cd tools/tests - python systemtests.py --build_args=PRECICE_REF:v3.1.1,PRECICE_PRESET:production-audit,OPENFOAM_ADAPTER_REF:v1.3.0,PYTHON_BINDINGS_REF:v3.1.0,FENICS_ADAPTER_REF:v2.1.0 --suites=fenics-adapter --log_level=DEBUG - cd ../../ - shell: /usr/bin/bash -e {0} -INFO: About to run the following systemtest in the directory /home/precice/runners_root/actions-runner-tutorial/_work/tutorials/tutorials/runs: - [Flow over heated plate (fluid-openfoam, solid-fenics)] -INFO: Started running Flow over heated plate (fluid-openfoam, solid-fenics), 0/1 -DEBUG: Checking out tutorials master before copying -From https://github.com/precice/tutorials - * [new branch] master -> master -DEBUG: Building docker image for Flow over heated plate (fluid-openfoam, solid-fenics) -DEBUG: Running tutorial Flow over heated plate (fluid-openfoam, solid-fenics) -DEBUG: Running fieldcompare for Flow over heated plate (fluid-openfoam, solid-fenics) -DEBUG: extracting /home/precice/runners_root/actions-runner-tutorial/_work/tutorials/tutorials/flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz into /home/precice/runners_root/actions-runner-tutorial/_work/tutorials/tutorials/runs/flow-over-heated-plate_fluid-openfoam-solid-fenics_2023-11-19-211723/reference_results -Using log_level: DEBUG -+---------------------------------------------------------+---------+-------------------+-----------------+-----------------------+ -| systemtest | success | building time [s] | solver time [s] | fieldcompare time [s] | -CRITICAL: Fieldcompare returned non zero exit code, therefore Flow over heated plate (fluid-openfoam, solid-fenics) failed -INFO: Running Flow over heated plate (fluid-openfoam, solid-fenics) took 280.5861554039875 seconds -ERROR: Failed to run Flow over heated plate (fluid-openfoam, solid-fenics) -+---------------------------------------------------------+---------+-------------------+-----------------+-----------------------+ -| Flow over heated plate (fluid-openfoam, solid-fenics) | 0 | 271.80 | 5.60 | 2.42 | -+---------------------------------------------------------+---------+-------------------+-----------------+-----------------------+ +```bash +fieldcompare dir precice-exports/ reference-results-unpacked// \ + --ignore-missing-reference-files \ + --ignore-unsupported-file-formats \ + -rtol 3e-7 ``` -In this case, building and running seems to work out, but the tests fail because the results differ from the reference results. This may be incorrect, as the previous step may have silently failed. +The differences are only shown per file, and there is no global metric or other summary (see [related discussion in fieldcompare](https://gitlab.com/dglaeser/fieldcompare/-/work_items/69)). + +Alternatively, [visualize the `precice-exports/diff_*.vtu` in ParaView](https://precice.org/configuration-export.html#visualization-with-paraview). + +## Extending -## Understanding what went wrong +### Adding new tests -The easiest way to debug a systemtest run is first to have a look at the output written into the action on GitHub. -If this does not provide enough hints, the next step is to download the generated `system_tests_run__` artifact. Note that by default this will only be generated if the systemtests fail. -Inside the archive, a test-specific subfolder like `flow-over-heated-plate_fluid-openfoam-solid-fenics_2023-11-19-211723` contains per-stage logs (`system-tests-build.log`, `system-tests-run.log`, `system-tests-compare.log`). These are a good starting point for further investigation. When fieldcompare runs with `--diff`, it writes VTK (.vtu) diff files under `precice-exports/`; if the comparison fails, those files are copied into a `diff-results/` subfolder in the same run directory (mirroring any subpaths under `precice-exports/`) so you can open them (e.g. in ParaView) to see where results differ from the reference. On successful comparisons, `diff-results/` is therefore absent. +Tests and test suites are defined in [`tests.yaml`](https://github.com/precice/tutorials/blob/develop/tools/tests/tests.yaml). By convention, every tutorial defines a test suite with the same name as its directory, and several test cases using combinations of the available participants. These test cases are later referenced by other test suites: these are typically the `release` and the test suites of different tested components. -## Adding new tests +The available cases are listed in the `metadata.yaml` of each tutorial. To add a new tutorial case as a test, add it to `metadata.yaml` and then define a test using it. Include that test in the relevant test suites. -### Adding tutorials +Use the `max_time` or `max_time_windows` parameters to restrict the runtime of the test to the first few coupling time windows, to save time. Aim for a runtime of less than a minute (assuming cached components), if possible. -In order for the systemtests to pick up the tutorial we need to define a `metadata.yaml` in the folder of the tutorial. There are a few `metadata.yaml` already present to get inspiration from. You can also have a look at the implementation details but normally the currently available ones should be easy to adopt. You can check your metadata parsing by `python print_metadata.py` and `python print_case_combinations.py` +You will need to define a reference results file. The reference results can and should be generated on GitHub using the [Generate reference results (manual)](https://github.com/precice/tutorials/actions/workflows/generate-reference-results-manual.yml) workflow for the respective test suite. You might want to temporarily set the `selected` test suite for requesting results only for a subset of test cases. -### Adding testsuites +Note that you will need to define the `TUTORIALS_REF` in the file [`reference_versions.yaml`](https://github.com/precice/tutorials/actions/workflows/generate-reference-results-manual.yml) to match the respective branch. Restore that to `develop` after that. See a [related issue](https://github.com/precice/tutorials/issues/844). -To add a testsuite just open the `tests.yaml` file and use the output of `python print_case_combinations.py` to add the right case combinations you want to test. Note that you can specify a `reference_result` which is not yet present. The `generate_reference_results.py` will pick that up and create it for you. -Note that its important to carefully check the paths of the `reference_result` in order to not have typos in there. Also note that same cases in different testsuites should use the same `reference_result`. +The results will be added to a Git LFS, but you will need special push access: just use the aforementioned GitHub Actions workflow, instead. -To cap the preCICE simulation time for a specific test without editing `precice-config.xml`, add an optional `max_time` (positive float, overrides ``) or `max_time_windows` (positive integer, overrides ``) field to the tutorial entry. Applies to both test runs and reference result generation. +### Adding new components -### Generate reference results +To add a new component, a few changes are needed: -Since we need data to compare against, you need to run `python generate_reference_results.py`. This process might take a while. -Please include the generated reference results in the pull request as they are strongly connected to the new testsuites. +1. In the `components.yaml`, add a new component, defining all the parameters that it might need. + - Add these parameters into the `reference_versions.yaml`. + - Add fields for these parameters into the `system-tests-latest-components.yml` workflow. +2. In the `dockerfiles//Dockerfile`, define a new stage for building your new component. Use the parameters you defined above. + - Defining a `_PR` variable will let you integrate the system tests into the respective component repository. +3. In the `component_templates.yaml`, define a component template. These are Jinja templates that are used by Docker Compose, and the main detail is how to run a simulation using that component. +4. Refer to the new component in the `metadata.yaml` of some tutorial and define some tests. -## Adding repositories +### Adding new repositories that can trigger the tests -If you want to trigger a testsuite from a new repository, you need to add a workflow file to that repository (under `.github/workflows/`). You can, for example, copy and adjust [the one from the OpenFOAM adapter](https://github.com/precice/openfoam-adapter/blob/develop/.github/workflows/system-tests.yaml). Then, you need a new label to trigger the workflow (e.g. `Issues`->`Labels`->`New label`). Last, in the [preCICE organization settings](https://github.com/organizations/precice/settings), you need to add the new repository to the action secret `WORKFLOW_DISPATCH_TOKEN` and to the default actions runner group. Adding the new label directly to the pull request in which you add the workflow should already trigger the system tests, compare the [pull request in the `precice` repository](https://github.com/precice/precice/pull/2052). +If you want to trigger the system tests from a new repository: + +1. Add a workflow file to that repository (under `.github/workflows/`). Example: [OpenFOAM adapter](https://github.com/precice/openfoam-adapter/blob/develop/.github/workflows/system-tests.yaml). +2. Create label as a trigger for workflow (under `Issues`->`Labels`->`New label`). +3. Give permissions: In the [preCICE organization settings](https://github.com/organizations/precice/settings), you need to add the new repository to the action secret `WORKFLOW_DISPATCH_TOKEN` and to the default actions runner group. Adding the new label directly to the pull request in which you add the workflow should already trigger the system tests. ## Implementation details -Each tutorial contains automation scripts (mainly `run.sh` and `clean.sh`), as well as metadata (`metadata.yaml`). The metadata file describes the available cases, how to run them, as well as their dependencies. A central `tests.yaml` file in this directory defines test suites, which execute different combinations of cases. The Python script `systemtests.py` executes the tests, allowing to filter for specific components or test suites. +Each tutorial contains automation scripts (mainly `run.sh` and `clean.sh`), as well as metadata (`metadata.yaml`). The metadata file describes the available cases, how to run them, as well as their dependencies. A central `tests.yaml` file defines test suites, which execute different combinations of cases. The Python script `systemtests.py` executes the tests, filter for specific test suites. + +Read more about the system tests in the publication [System Regression Tests for the preCICE Coupling Ecosystem](https://doi.org/10.14279/eceasst.v83.2614). + +[![ECEASST](https://img.shields.io/badge/DOI-10.14279%2Feceasst.v83.2614-green)](https://doi.org/10.14279/eceasst.v83.2614) -Let's dive deeper into some of these aspects. +
Expand the implementation details... ### General architecture @@ -153,7 +166,7 @@ Test steps include modifying the tutorial configuration files for the test syste Tests are executed by the `systemtests.py` script, which starts the Docker Compose. This can be executed locally, and it is the same script that GitHub Actions also execute. -The multi-stage Docker build allows building each component separately from the same Dockerfile, while Docker reuses cached layers. The Docker Compose services consider GitHub Actions Cache when building the services, although the cache is currently only updated, but not hit (see https://github.com/precice/tutorials/pull/372#issuecomment-1748335750). +The multi-stage Docker build allows building each component separately from the same Dockerfile, while Docker reuses cached layers. The Docker Compose services consider GitHub Actions Cache when building the services, although the cache is currently only updated, but not hit (see https://github.com/precice/tutorials/pull/372#issuecomment-1748335750). For this reason, we are running on a dedicated self-hosted runner. ### File structure @@ -252,7 +265,7 @@ Description: ### Components -The components mentioned in the Metadata are defined in the central `components.yaml` file. This file also specifies some arguments and their default values. These arguments can be anything, but most often they are version-related information. For example, the version of the OpenFOAM library used by the openfoam-adapter component, or the openfoam-adapter component version itself. For example: +The components mentioned in the Metadata are defined in the central `components.yaml` file. This file also specifies some arguments and their default values. These arguments can be anything, but most often they are version-related information. For example, the version of OpenFOAM used by the openfoam-adapter component, or the component version itself. For example: ```yaml openfoam-adapter: @@ -282,10 +295,11 @@ This `openfoam-adapter` component has the following attributes: #### Naming schema for build_arguments -Since the docker containers are still a bit mixed in terms of capabilities and support for different build_argument combinations the following rules apply: +The following rules apply for the `build_arguments`: - A build argument ending in `_REF` refers to a git commit-ish (like a tag or commit) being used to build the image. -- Some workflows set variables ending in `_PR`. These specify the GitHub pull request which provides the above `_REF` and can be on a fork. +- The `repository` parameter of any `_REF` argument points to the repository where the respective Git reference should be resolved. This is assumed to be consistent across components and the Dockerfile (and could be simplified). +- Some workflows set variables ending in `_PR`. These specify the GitHub pull request, which provides the above `_REF` and can be on a fork. - A build argument ending in `_VERSION` refers to the version of a third-party dependency to use (e.g., DUNE). - All other `build_arguments` are free of rules and up to the container maintainer. @@ -308,54 +322,15 @@ command: > This template defines: -- `image`: The base Docker image for this component, including a Git reference (tag), provided to the template as argument (e.g., by the `systemtests.py` script). +- `image`: The base Docker image for this component, including a Git reference (tag), provided to the template as an argument (e.g., by the `systemtests.py` script). - `depends_on`: Other services this service depends upon, typically a preparation service that fetches all components and tutorials. - `volumes`: Directories mapped between the host and the container. Apart from directories relating to the users and groups, this also defines where to run the cases. - `command`: How to run a case depending on this component, including how and where to redirect any screen output. -### Tests - -Concrete tests are specified centrally in the file `tests.yaml`. For example: - -```yaml -test_suites: - openfoam-adapter: - tutorials: - - path: flow-over-heated-plate - case_combination: - - fluid-openfoam - - solid-openfoam - reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-openfoam.tar.gz - - path: flow-over-heated-plate - case_combination: - - fluid-openfoam - - solid-fenics - reference_result: ./flow-over-heated-plate/reference-results/fluid-openfoam_solid-fenics.tar.gz - timeout: 1200 -``` - -The optional `timeout` field (in seconds) sets the maximum time for the solver run and fieldcompare phases of that specific case. If omitted, it defaults to `GLOBAL_TIMEOUT` (currently 300s (5min), overridable via the `PRECICE_SYSTEMTESTS_TIMEOUT` environment variable). - -This defines the test suite `openfoam-adapter`, with a case combination to run. - -### Generate Reference Results - -#### via GitHub workflow (recommended) - -The preferred way of adding reference results is via the manual `Generate reference results (manual)` workflow. This takes two inputs: - -- `from_ref`: branch where the new test configuration (e.g added tests, new reference_versions.yaml) is -- `commit_msg`: commit message for adding the reference results into the branch - -The workflow will checkout the `from_ref`, take the status of the systemtests of that branch and execute `python generate_reference_results.py`, upload the LFS objects into the self-hosted LFS server and add a commit with `commit_msg` onto the `from_ref` branch. - -#### manually +### Timeouts -In order to generate the reference results edit the `reference_versions.yaml` to match the required `build_arguments` otherwise passed via the cli. -Executing `generate_reference_results.py` will then generate the following files: +A `GLOBAL_TIMEOUT` is used for all operations. Its default value is 600s (5min), it is set in the beginning of [`Systemtests.py`](https://github.com/precice/tutorials/blob/develop/tools/tests/systemtests/Systemtest.py), and it can be overridden via the `PRECICE_SYSTEMTESTS_TIMEOUT` environment variable. -- all distinct `.tar.gz` defined in the `tests.yaml` -- a `reference_results.md` in the tutorial folder describing the arguments used and a sha-1 hash of the `tar.gz` archive. +Tests can define a different `timeout` in their `tests.yaml` entry, which applies to the running and results comparison steps. -The reference result archive will later be unpacked again during the systemtest and compared using `fieldcompare` -Please note that these files should always be kept in the git lfs. +
diff --git a/tools/tests/build_docker_images.py b/tools/tests/build_docker_images.py index 6a0d1cd07..da1df5a5f 100644 --- a/tools/tests/build_docker_images.py +++ b/tools/tests/build_docker_images.py @@ -19,12 +19,12 @@ def main(): parser.add_argument( '--build_args', type=str, - help='Comma-separated list of arguments provided to the components like openfoam:2102,pythonbindings:latest') + help='Comma-separated list of component build arguments (e.g., "PRECICE_REF:develop,OPENFOAM_ADAPTER_REF:develop")') parser.add_argument('--rundir', type=str, help='Directory to run the systemstests in.', nargs='?', const=PRECICE_TESTS_RUN_DIR, default=PRECICE_TESTS_RUN_DIR) parser.add_argument('--log_level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], - default='INFO', help='Set the logging level') + default='INFO', help='Set the logging level of the system tests scripts.') # Parse the command-line arguments args = parser.parse_args() diff --git a/tools/tests/reference_versions.yaml b/tools/tests/reference_versions.yaml index c7c3e4c04..6c7e69563 100644 --- a/tools/tests/reference_versions.yaml +++ b/tools/tests/reference_versions.yaml @@ -22,7 +22,7 @@ OPENFOAM_ADAPTER_REF: "2c3062c" # develop, May 27, 2026 PRECICE_REF: "v3.4.1" PYTHON_BINDINGS_REF: "v3.4.0" SU2_ADAPTER_REF: "5abe79b" # develop, May 27, 2026 -TUTORIALS_REF: "mercurydpm-tests" +TUTORIALS_REF: "develop" # Additional settings PRECICE_PRESET: "production-audit" \ No newline at end of file diff --git a/tools/tests/systemtests.py b/tools/tests/systemtests.py index bc56a6a84..3183b0ca6 100644 --- a/tools/tests/systemtests.py +++ b/tools/tests/systemtests.py @@ -30,12 +30,12 @@ def main(): parser.add_argument( '--build_args', type=str, - help='Comma-separated list of arguments provided to the components like openfoam:2102,pythonbindings:latest') + help='Comma-separated list of component build arguments (e.g., "PRECICE_REF:develop,OPENFOAM_ADAPTER_REF:develop")') parser.add_argument('--rundir', type=str, help='Directory to run the systemstests in.', nargs='?', const=PRECICE_TESTS_RUN_DIR, default=PRECICE_TESTS_RUN_DIR) parser.add_argument('--log_level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], - default='INFO', help='Set the logging level') + default='INFO', help='Set the logging level of the system tests scripts.') # Parse the command-line arguments args = parser.parse_args() From 60b6722510e1281c14c62dd6dc23e513c016093d Mon Sep 17 00:00:00 2001 From: Gerasimos Chourdakis Date: Tue, 16 Jun 2026 16:56:17 +0200 Subject: [PATCH 93/94] Enable markdown support in the details HTML tag --- tools/tests/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tests/README.md b/tools/tests/README.md index f60eb5031..620b76164 100644 --- a/tools/tests/README.md +++ b/tools/tests/README.md @@ -154,7 +154,7 @@ Read more about the system tests in the publication [System Regression Tests for [![ECEASST](https://img.shields.io/badge/DOI-10.14279%2Feceasst.v83.2614-green)](https://doi.org/10.14279/eceasst.v83.2614) -
Expand the implementation details... +
Expand the implementation details... ### General architecture From c03be66fad5ed9c9e888f6f59edf516d9ad952eb Mon Sep 17 00:00:00 2001 From: PranjalManhgaye Date: Wed, 17 Jun 2026 10:04:51 +0530 Subject: [PATCH 94/94] Rework tutorial Python dependency reproducibility for #610. Add loose constraints to tutorial requirements.txt files, PyPI-based report_tutorial_requirements.py, and root requirements-reference.txt. --- .github/pull_request_template.md | 2 + .../check-requirements-reference.yml | 32 ++ changelog-entries/749.md | 1 + .../fluid-nutils/requirements.txt | 4 +- .../fluid-nutils/requirements.txt | 4 +- .../transport-nutils/requirements.txt | 4 +- elastic-tube-1d/fluid-python/requirements.txt | 2 +- .../controller-fmi/requirements.txt | 2 +- .../solid-fenicsx/requirements.txt | 4 +- .../solid-nutils/requirements.txt | 2 +- .../solver-python/requirements.txt | 2 +- oscillator/mass-left-fmi/requirements.txt | 4 +- oscillator/mass-right-fmi/requirements.txt | 4 +- oscillator/solver-fmi/requirements.txt | 4 +- oscillator/solver-python/requirements.txt | 2 +- .../solver-fenicsx/requirements.txt | 8 +- .../solver-fenics/requirements.txt | 2 +- .../dirichlet-nutils/requirements.txt | 2 +- .../neumann-nutils/requirements.txt | 2 +- .../dirichlet-nutils/requirements.txt | 2 +- .../neumann-nutils/requirements.txt | 2 +- .../solver-fenics/requirements.txt | 2 +- .../solver-fenicsx/requirements.txt | 6 +- .../fluid1d-left-nutils/requirements.txt | 4 +- .../fluid1d-right-nutils/requirements.txt | 4 +- .../fluid-nutils/requirements.txt | 2 +- .../solid-fenics/requirements.txt | 2 +- .../solid-nutils/requirements.txt | 2 +- requirements-reference.txt | 320 ++++++++++++++++++ .../capacitor-python/requirements.txt | 2 +- resonant-circuit/coil-python/requirements.txt | 2 +- tools/report_tutorial_requirements.py | 200 +++++++++++ tools/tests/README.md | 13 + tools/validate_requirements_reference.py | 20 ++ turek-hron-fsi3/fluid-nutils/requirements.txt | 8 +- turek-hron-fsi3/solid-nutils/requirements.txt | 8 +- .../macro-nutils/requirements.txt | 2 +- .../micro-nutils/requirements.txt | 4 +- .../source-nutils/requirements.txt | 2 +- .../fluid1d-left-nutils/requirements.txt | 6 +- .../fluid1d-right-nutils/requirements.txt | 6 +- .../soil-creep-landlab/requirements.txt | 4 +- .../wolf-sheep-grass-mesa/requirements.txt | 6 +- 43 files changed, 652 insertions(+), 64 deletions(-) create mode 100644 .github/workflows/check-requirements-reference.yml create mode 100644 changelog-entries/749.md create mode 100644 requirements-reference.txt create mode 100644 tools/report_tutorial_requirements.py create mode 100644 tools/validate_requirements_reference.py diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index b5a665647..cccaa7640 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,6 +4,8 @@ TODO: Summarize and motivate the changes, link to issues, remove the checklist e - [ ] I added a summary of any user-facing changes (compared to the last release) in the `changelog-entries/.md`. +For **release PRs** (new distribution): regenerate `requirements-reference.txt` after updating tutorial `requirements.txt` constraints (`python3 tools/report_tutorial_requirements.py`). + For new tutorials or tutorial cases: - [ ] I followed the [tutorial folder structure](https://precice.org/community-contribute-to-precice.html#contributing-tutorials) diff --git a/.github/workflows/check-requirements-reference.yml b/.github/workflows/check-requirements-reference.yml new file mode 100644 index 000000000..047f53835 --- /dev/null +++ b/.github/workflows/check-requirements-reference.yml @@ -0,0 +1,32 @@ +name: Check requirements-reference +on: + push: + branches: + - master + - develop + paths: + - "**/requirements.txt" + - requirements-reference.txt + - tools/report_tutorial_requirements.py + - tools/validate_requirements_reference.py + pull_request: + branches: + - master + - develop + paths: + - "**/requirements.txt" + - requirements-reference.txt + - tools/report_tutorial_requirements.py + - tools/validate_requirements_reference.py +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.10" + - name: Install script dependency + run: pip install packaging + - name: Validate requirements-reference + run: python3 tools/validate_requirements_reference.py diff --git a/changelog-entries/749.md b/changelog-entries/749.md new file mode 100644 index 000000000..49bb2bf73 --- /dev/null +++ b/changelog-entries/749.md @@ -0,0 +1 @@ +- Add loose version constraints to tutorial `requirements.txt` files and `tools/report_tutorial_requirements.py` to generate a root-level `requirements-reference.txt` snapshot from PyPI at release time (fixes [#610](https://github.com/precice/tutorials/issues/610)). Includes GitHub Action to validate the reference file stays in sync. diff --git a/channel-transport-particles/fluid-nutils/requirements.txt b/channel-transport-particles/fluid-nutils/requirements.txt index e535d45e9..5b7dc1cf6 100644 --- a/channel-transport-particles/fluid-nutils/requirements.txt +++ b/channel-transport-particles/fluid-nutils/requirements.txt @@ -1,5 +1,5 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=7.3 numpy >1, <2 pyprecice~=3.0 -setuptools +setuptools>=69.0.0 diff --git a/channel-transport/fluid-nutils/requirements.txt b/channel-transport/fluid-nutils/requirements.txt index e535d45e9..5b7dc1cf6 100644 --- a/channel-transport/fluid-nutils/requirements.txt +++ b/channel-transport/fluid-nutils/requirements.txt @@ -1,5 +1,5 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=7.3 numpy >1, <2 pyprecice~=3.0 -setuptools +setuptools>=69.0.0 diff --git a/channel-transport/transport-nutils/requirements.txt b/channel-transport/transport-nutils/requirements.txt index 0473bd5c5..c76b36895 100644 --- a/channel-transport/transport-nutils/requirements.txt +++ b/channel-transport/transport-nutils/requirements.txt @@ -1,6 +1,6 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=7.3 numpy >1, <2 # remeshing support will be released with preCICE version 3.2 pyprecice @ git+https://github.com/precice/python-bindings.git@develop -setuptools +setuptools>=69.0.0 diff --git a/elastic-tube-1d/fluid-python/requirements.txt b/elastic-tube-1d/fluid-python/requirements.txt index 20ab73eb9..4bb3e60a1 100644 --- a/elastic-tube-1d/fluid-python/requirements.txt +++ b/elastic-tube-1d/fluid-python/requirements.txt @@ -1,3 +1,3 @@ -matplotlib +matplotlib>=3.5,<4 numpy >1, <2 pyprecice~=3.0 \ No newline at end of file diff --git a/flow-around-controlled-moving-cylinder/controller-fmi/requirements.txt b/flow-around-controlled-moving-cylinder/controller-fmi/requirements.txt index 10f7ea9df..09a2e2195 100644 --- a/flow-around-controlled-moving-cylinder/controller-fmi/requirements.txt +++ b/flow-around-controlled-moving-cylinder/controller-fmi/requirements.txt @@ -1 +1 @@ -fmiprecice \ No newline at end of file +fmiprecice~=0.2 diff --git a/flow-over-heated-plate/solid-fenicsx/requirements.txt b/flow-over-heated-plate/solid-fenicsx/requirements.txt index b36876a55..a06d7331a 100644 --- a/flow-over-heated-plate/solid-fenicsx/requirements.txt +++ b/flow-over-heated-plate/solid-fenicsx/requirements.txt @@ -1,3 +1,3 @@ -numpy -fenicsxprecice +numpy >1, <2 +fenicsxprecice~=1.0 mpi4py>=3 diff --git a/flow-over-heated-plate/solid-nutils/requirements.txt b/flow-over-heated-plate/solid-nutils/requirements.txt index 7ff7b5aa1..f22e7ac25 100644 --- a/flow-over-heated-plate/solid-nutils/requirements.txt +++ b/flow-over-heated-plate/solid-nutils/requirements.txt @@ -1,4 +1,4 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=7.3 numpy >1, <2 pyprecice~=3.0 diff --git a/oscillator-overlap/solver-python/requirements.txt b/oscillator-overlap/solver-python/requirements.txt index 9dff131c5..d61415287 100644 --- a/oscillator-overlap/solver-python/requirements.txt +++ b/oscillator-overlap/solver-python/requirements.txt @@ -1,3 +1,3 @@ numpy >1, <2 pyprecice~=3.0 -scipy +scipy>=1.10,<2 diff --git a/oscillator/mass-left-fmi/requirements.txt b/oscillator/mass-left-fmi/requirements.txt index 66239ca8f..3ff3bd9de 100644 --- a/oscillator/mass-left-fmi/requirements.txt +++ b/oscillator/mass-left-fmi/requirements.txt @@ -1,2 +1,2 @@ -fmiprecice -pandas \ No newline at end of file +fmiprecice~=0.2 +pandas>=2.0,<3 diff --git a/oscillator/mass-right-fmi/requirements.txt b/oscillator/mass-right-fmi/requirements.txt index 66239ca8f..3ff3bd9de 100644 --- a/oscillator/mass-right-fmi/requirements.txt +++ b/oscillator/mass-right-fmi/requirements.txt @@ -1,2 +1,2 @@ -fmiprecice -pandas \ No newline at end of file +fmiprecice~=0.2 +pandas>=2.0,<3 diff --git a/oscillator/solver-fmi/requirements.txt b/oscillator/solver-fmi/requirements.txt index 66239ca8f..3ff3bd9de 100644 --- a/oscillator/solver-fmi/requirements.txt +++ b/oscillator/solver-fmi/requirements.txt @@ -1,2 +1,2 @@ -fmiprecice -pandas \ No newline at end of file +fmiprecice~=0.2 +pandas>=2.0,<3 diff --git a/oscillator/solver-python/requirements.txt b/oscillator/solver-python/requirements.txt index 9dff131c5..d61415287 100644 --- a/oscillator/solver-python/requirements.txt +++ b/oscillator/solver-python/requirements.txt @@ -1,3 +1,3 @@ numpy >1, <2 pyprecice~=3.0 -scipy +scipy>=1.10,<2 diff --git a/partitioned-heat-conduction-3d/solver-fenicsx/requirements.txt b/partitioned-heat-conduction-3d/solver-fenicsx/requirements.txt index 1759af720..0c528ba4b 100644 --- a/partitioned-heat-conduction-3d/solver-fenicsx/requirements.txt +++ b/partitioned-heat-conduction-3d/solver-fenicsx/requirements.txt @@ -1,5 +1,5 @@ numpy >1, <2 -fenicsxprecice -scipy -sympy -gmsh +fenicsxprecice~=1.0 +scipy>=1.10,<2 +sympy>=1.10,<2 +gmsh>=4,<5 diff --git a/partitioned-heat-conduction-complex/solver-fenics/requirements.txt b/partitioned-heat-conduction-complex/solver-fenics/requirements.txt index 04561a50f..9c23e91cf 100644 --- a/partitioned-heat-conduction-complex/solver-fenics/requirements.txt +++ b/partitioned-heat-conduction-complex/solver-fenics/requirements.txt @@ -1,6 +1,6 @@ fenicsprecice~=2.0 numpy >1, <2 -sympy +sympy>=1.10,<2 # Assuming FEniCS from ppa:fenics-packages/fenics was installed https://fenicsproject.org/download/archive/ # Use --system-site-packages in venv diff --git a/partitioned-heat-conduction-direct/dirichlet-nutils/requirements.txt b/partitioned-heat-conduction-direct/dirichlet-nutils/requirements.txt index 7ff7b5aa1..f22e7ac25 100644 --- a/partitioned-heat-conduction-direct/dirichlet-nutils/requirements.txt +++ b/partitioned-heat-conduction-direct/dirichlet-nutils/requirements.txt @@ -1,4 +1,4 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=7.3 numpy >1, <2 pyprecice~=3.0 diff --git a/partitioned-heat-conduction-direct/neumann-nutils/requirements.txt b/partitioned-heat-conduction-direct/neumann-nutils/requirements.txt index 7ff7b5aa1..f22e7ac25 100644 --- a/partitioned-heat-conduction-direct/neumann-nutils/requirements.txt +++ b/partitioned-heat-conduction-direct/neumann-nutils/requirements.txt @@ -1,4 +1,4 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=7.3 numpy >1, <2 pyprecice~=3.0 diff --git a/partitioned-heat-conduction/dirichlet-nutils/requirements.txt b/partitioned-heat-conduction/dirichlet-nutils/requirements.txt index 7ff7b5aa1..f22e7ac25 100644 --- a/partitioned-heat-conduction/dirichlet-nutils/requirements.txt +++ b/partitioned-heat-conduction/dirichlet-nutils/requirements.txt @@ -1,4 +1,4 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=7.3 numpy >1, <2 pyprecice~=3.0 diff --git a/partitioned-heat-conduction/neumann-nutils/requirements.txt b/partitioned-heat-conduction/neumann-nutils/requirements.txt index 7ff7b5aa1..f22e7ac25 100644 --- a/partitioned-heat-conduction/neumann-nutils/requirements.txt +++ b/partitioned-heat-conduction/neumann-nutils/requirements.txt @@ -1,4 +1,4 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=7.3 numpy >1, <2 pyprecice~=3.0 diff --git a/partitioned-heat-conduction/solver-fenics/requirements.txt b/partitioned-heat-conduction/solver-fenics/requirements.txt index 190ff55e4..b7f8077bd 100644 --- a/partitioned-heat-conduction/solver-fenics/requirements.txt +++ b/partitioned-heat-conduction/solver-fenics/requirements.txt @@ -1,6 +1,6 @@ numpy >1, <2 fenicsprecice~=2.0 -scipy +scipy>=1.10,<2 # Assuming FEniCS from ppa:fenics-packages/fenics was installed https://fenicsproject.org/download/archive/ # Use --system-site-packages in venv diff --git a/partitioned-heat-conduction/solver-fenicsx/requirements.txt b/partitioned-heat-conduction/solver-fenicsx/requirements.txt index 85fc7389a..2985d2c89 100644 --- a/partitioned-heat-conduction/solver-fenicsx/requirements.txt +++ b/partitioned-heat-conduction/solver-fenicsx/requirements.txt @@ -1,5 +1,5 @@ numpy >1, <2 -fenicsxprecice -scipy -sympy +fenicsxprecice~=1.0 +scipy>=1.10,<2 +sympy>=1.10,<2 mpi4py>=3 diff --git a/partitioned-pipe-multiscale/fluid1d-left-nutils/requirements.txt b/partitioned-pipe-multiscale/fluid1d-left-nutils/requirements.txt index 758176da0..f4fc6880f 100644 --- a/partitioned-pipe-multiscale/fluid1d-left-nutils/requirements.txt +++ b/partitioned-pipe-multiscale/fluid1d-left-nutils/requirements.txt @@ -1,5 +1,5 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=9.0 numpy >1, <2 pyprecice~=3.0 -matplotlib \ No newline at end of file +matplotlib>=3.5,<4 \ No newline at end of file diff --git a/partitioned-pipe-multiscale/fluid1d-right-nutils/requirements.txt b/partitioned-pipe-multiscale/fluid1d-right-nutils/requirements.txt index 758176da0..f4fc6880f 100644 --- a/partitioned-pipe-multiscale/fluid1d-right-nutils/requirements.txt +++ b/partitioned-pipe-multiscale/fluid1d-right-nutils/requirements.txt @@ -1,5 +1,5 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=9.0 numpy >1, <2 pyprecice~=3.0 -matplotlib \ No newline at end of file +matplotlib>=3.5,<4 \ No newline at end of file diff --git a/perpendicular-flap/fluid-nutils/requirements.txt b/perpendicular-flap/fluid-nutils/requirements.txt index b81f4ff8f..ecf10833d 100644 --- a/perpendicular-flap/fluid-nutils/requirements.txt +++ b/perpendicular-flap/fluid-nutils/requirements.txt @@ -1,4 +1,4 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=6.0 numpy >1, <2 pyprecice~=3.0 diff --git a/perpendicular-flap/solid-fenics/requirements.txt b/perpendicular-flap/solid-fenics/requirements.txt index 1b61f98a2..55f045e6c 100644 --- a/perpendicular-flap/solid-fenics/requirements.txt +++ b/perpendicular-flap/solid-fenics/requirements.txt @@ -1,6 +1,6 @@ fenicsprecice~=2.2 numpy >1, <2 -matplotlib +matplotlib>=3.5,<4 # Assuming FEniCS from ppa:fenics-packages/fenics was installed https://fenicsproject.org/download/archive/ # Use --system-site-packages in venv diff --git a/perpendicular-flap/solid-nutils/requirements.txt b/perpendicular-flap/solid-nutils/requirements.txt index 8ae441ba1..cfcb0a3ae 100644 --- a/perpendicular-flap/solid-nutils/requirements.txt +++ b/perpendicular-flap/solid-nutils/requirements.txt @@ -1,4 +1,4 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils>=8.5 numpy >1, <2 pyprecice~=3.0 diff --git a/requirements-reference.txt b/requirements-reference.txt new file mode 100644 index 000000000..336056808 --- /dev/null +++ b/requirements-reference.txt @@ -0,0 +1,320 @@ +# Pinned Python dependency versions for preCICE tutorial distributions. +# Reference manifest only: tutorial run scripts keep using loose requirements.txt constraints. +# Generated by tools/report_tutorial_requirements.py — do not edit manually. +# Generated: 2026-06-16T21:08:47Z + +# channel-transport/fluid-nutils/requirements.txt +setuptools==82.0.1 +nutils==7.3 +numpy==1.26.4 +pyprecice==3.4.0 +setuptools==82.0.1 + +# channel-transport/transport-nutils/requirements.txt +setuptools==82.0.1 +nutils==7.3 +numpy==1.26.4 +pyprecice @ git+https://github.com/precice/python-bindings.git@develop +setuptools==82.0.1 + +# channel-transport-particles/fluid-nutils/requirements.txt +setuptools==82.0.1 +nutils==7.3 +numpy==1.26.4 +pyprecice==3.4.0 +setuptools==82.0.1 + +# channel-transport-reaction/chemical-fenics/requirements.txt +numpy==1.26.4 +fenicsprecice==2.3.0 +fenics-dijitso==2019.1.0 +fenics-dolfin~=2019.0 # unresolved (not on PyPI or no matching version) +fenics-ffc==2019.1.0.post0 +fenics-fiat==2019.1.0 +fenics-ufl-legacy==2022.3.0 + +# channel-transport-reaction/fluid-fenics/requirements.txt +numpy==1.26.4 +fenicsprecice==2.3.0 +fenics-dijitso==2019.1.0 +fenics-dolfin~=2019.0 # unresolved (not on PyPI or no matching version) +fenics-ffc==2019.1.0.post0 +fenics-fiat==2019.1.0 +fenics-ufl-legacy==2022.3.0 + +# elastic-tube-1d/fluid-python/requirements.txt +matplotlib==3.11.0 +numpy==1.26.4 +pyprecice==3.4.0 + +# elastic-tube-1d/solid-python/requirements.txt +numpy==1.26.4 +pyprecice==3.4.0 + +# elastic-tube-3d/solid-fenics/requirements.txt +fenicsprecice==2.3.0 +numpy==1.26.4 +fenics-dijitso==2019.1.0 +fenics-dolfin~=2019.0 # unresolved (not on PyPI or no matching version) +fenics-ffc==2019.1.0.post0 +fenics-fiat==2019.1.0 +fenics-ufl-legacy==2022.3.0 + +# flow-around-controlled-moving-cylinder/controller-fmi/requirements.txt +fmiprecice==0.2.1 + +# flow-around-controlled-moving-cylinder/solid-python/requirements.txt +pyprecice==3.4.0 +numpy==1.26.4 + +# flow-over-heated-plate/fluid-su2/requirements.txt +numpy==1.26.4 +pyprecice==3.4.0 + +# flow-over-heated-plate/solid-dunefem/requirements.txt +dune-fem==2.12.0.2 +pyprecice==3.4.0 + +# flow-over-heated-plate/solid-fenics/requirements.txt +fenicsprecice==2.3.0 +numpy==1.26.4 +fenics-dijitso==2019.1.0 +fenics-dolfin~=2019.0 # unresolved (not on PyPI or no matching version) +fenics-ffc==2019.1.0.post0 +fenics-fiat==2019.1.0 +fenics-ufl-legacy==2022.3.0 + +# flow-over-heated-plate/solid-fenicsx/requirements.txt +numpy==1.26.4 +fenicsxprecice==1.0.1 +mpi4py==4.1.2 + +# flow-over-heated-plate/solid-nutils/requirements.txt +setuptools==82.0.1 +nutils==7.3 +numpy==1.26.4 +pyprecice==3.4.0 + +# oscillator/mass-left-fmi/requirements.txt +fmiprecice==0.2.1 +pandas==2.3.3 + +# oscillator/mass-right-fmi/requirements.txt +fmiprecice==0.2.1 +pandas==2.3.3 + +# oscillator/solver-fmi/requirements.txt +fmiprecice==0.2.1 +pandas==2.3.3 + +# oscillator/solver-python/requirements.txt +numpy==1.26.4 +pyprecice==3.4.0 +scipy==1.17.1 + +# oscillator-overlap/solver-python/requirements.txt +numpy==1.26.4 +pyprecice==3.4.0 +scipy==1.17.1 + +# partitioned-heat-conduction/dirichlet-nutils/requirements.txt +setuptools==82.0.1 +nutils==7.3 +numpy==1.26.4 +pyprecice==3.4.0 + +# partitioned-heat-conduction/neumann-nutils/requirements.txt +setuptools==82.0.1 +nutils==7.3 +numpy==1.26.4 +pyprecice==3.4.0 + +# partitioned-heat-conduction/solver-fenics/requirements.txt +numpy==1.26.4 +fenicsprecice==2.3.0 +scipy==1.17.1 +fenics-dijitso==2019.1.0 +fenics-dolfin~=2019.0 # unresolved (not on PyPI or no matching version) +fenics-ffc==2019.1.0.post0 +fenics-fiat==2019.1.0 +fenics-ufl-legacy==2022.3.0 + +# partitioned-heat-conduction/solver-fenicsx/requirements.txt +numpy==1.26.4 +fenicsxprecice==1.0.1 +scipy==1.17.1 +sympy==1.14.0 +mpi4py==4.1.2 + +# partitioned-heat-conduction-3d/solver-fenicsx/requirements.txt +numpy==1.26.4 +fenicsxprecice==1.0.1 +scipy==1.17.1 +sympy==1.14.0 +gmsh==4.15.2 + +# partitioned-heat-conduction-complex/solver-fenics/requirements.txt +fenicsprecice==2.3.0 +numpy==1.26.4 +sympy==1.14.0 +fenics-dijitso==2019.1.0 +fenics-dolfin~=2019.0 # unresolved (not on PyPI or no matching version) +fenics-ffc==2019.1.0.post0 +fenics-fiat==2019.1.0 +fenics-ufl-legacy==2022.3.0 + +# partitioned-heat-conduction-direct/dirichlet-nutils/requirements.txt +setuptools==82.0.1 +nutils==7.3 +numpy==1.26.4 +pyprecice==3.4.0 + +# partitioned-heat-conduction-direct/neumann-nutils/requirements.txt +setuptools==82.0.1 +nutils==7.3 +numpy==1.26.4 +pyprecice==3.4.0 + +# partitioned-heat-conduction-overlap/solver-fenics/requirements.txt +fenicsprecice==2.3.0 +numpy==1.26.4 +fenics-dijitso==2019.1.0 +fenics-dolfin~=2019.0 # unresolved (not on PyPI or no matching version) +fenics-ffc==2019.1.0.post0 +fenics-fiat==2019.1.0 +fenics-ufl-legacy==2022.3.0 + +# partitioned-pipe-multiscale/fluid1d-left-nutils/requirements.txt +setuptools==82.0.1 +nutils==9.2 +numpy==1.26.4 +pyprecice==3.4.0 +matplotlib==3.11.0 + +# partitioned-pipe-multiscale/fluid1d-right-nutils/requirements.txt +setuptools==82.0.1 +nutils==9.2 +numpy==1.26.4 +pyprecice==3.4.0 +matplotlib==3.11.0 + +# perpendicular-flap/fluid-fake/requirements.txt +numpy==1.26.4 +pyprecice==3.4.0 + +# perpendicular-flap/fluid-nutils/requirements.txt +setuptools==82.0.1 +nutils==6.3 +numpy==1.26.4 +pyprecice==3.4.0 +treelog==1.0 + +# perpendicular-flap/fluid-su2/requirements.txt +numpy==1.26.4 +pyprecice==3.4.0 + +# perpendicular-flap/solid-fake/requirements.txt +numpy==1.26.4 +pyprecice==3.4.0 + +# perpendicular-flap/solid-fenics/requirements.txt +fenicsprecice==2.3.0 +numpy==1.26.4 +matplotlib==3.11.0 +fenics-dijitso==2019.1.0 +fenics-dolfin~=2019.0 # unresolved (not on PyPI or no matching version) +fenics-ffc==2019.1.0.post0 +fenics-fiat==2019.1.0 +fenics-ufl-legacy==2022.3.0 + +# perpendicular-flap/solid-nutils/requirements.txt +setuptools==82.0.1 +nutils==9.2 +numpy==1.26.4 +pyprecice==3.4.0 + +# resonant-circuit/capacitor-python/requirements.txt +numpy==1.26.4 +scipy==1.17.1 +pyprecice==3.4.0 + +# resonant-circuit/coil-python/requirements.txt +numpy==1.26.4 +scipy==1.17.1 +pyprecice==3.4.0 + +# turek-hron-fsi3/fluid-nutils/requirements.txt +setuptools==82.0.1 +nutils==9.2 +numpy==1.26.4 +pyprecice==3.4.0 +meshio==5.3.5 +gmsh==4.15.2 +matplotlib==3.11.0 + +# turek-hron-fsi3/solid-nutils/requirements.txt +setuptools==82.0.1 +nutils==9.2 +numpy==1.26.4 +pyprecice==3.4.0 +meshio==5.3.5 +gmsh==4.15.2 +matplotlib==3.11.0 + +# two-scale-heat-conduction/macro-nutils/requirements.txt +setuptools==82.0.1 +nutils==7.3 +numpy==1.26.4 +pyprecice==3.4.0 + +# two-scale-heat-conduction/micro-nutils/requirements.txt +setuptools==82.0.1 +nutils==7.3 +numpy==1.26.4 +pyprecice==3.4.0 +micro-manager-precice==0.10.1 + +# volume-coupled-diffusion/solver-fenics/requirements.txt +fenicsprecice==2.3.0 +numpy==1.26.4 +fenics-dijitso==2019.1.0 +fenics-dolfin~=2019.0 # unresolved (not on PyPI or no matching version) +fenics-ffc==2019.1.0.post0 +fenics-fiat==2019.1.0 +fenics-ufl-legacy==2022.3.0 + +# volume-coupled-flow/source-nutils/requirements.txt +setuptools==82.0.1 +nutils==7.3 +numpy==1.26.4 +pyprecice==3.4.0 + +# water-hammer/fluid1d-left-nutils/requirements.txt +setuptools==82.0.1 +nutils==9.2 +numpy==1.26.4 +pyprecice==3.4.0 +matplotlib==3.11.0 +treelog==1.0 + +# water-hammer/fluid1d-right-nutils/requirements.txt +setuptools==82.0.1 +nutils==9.2 +numpy==1.26.4 +pyprecice==3.4.0 +matplotlib==3.11.0 +treelog==1.0 + +# wolf-sheep-soil-creep/soil-creep-landlab/requirements.txt +numpy==1.26.4 +matplotlib==3.11.0 +landlab==2.11.0 +pyprecice==3.4.0 + +# wolf-sheep-soil-creep/wolf-sheep-grass-mesa/requirements.txt +numpy==1.26.4 +matplotlib==3.11.0 +mesa==3.5.1 +pyprecice==3.4.0 +networkx==3.6.1 + diff --git a/resonant-circuit/capacitor-python/requirements.txt b/resonant-circuit/capacitor-python/requirements.txt index 2b8edf2ba..814a5c7dc 100644 --- a/resonant-circuit/capacitor-python/requirements.txt +++ b/resonant-circuit/capacitor-python/requirements.txt @@ -1,3 +1,3 @@ numpy >1, <2 -scipy +scipy>=1.10,<2 pyprecice~=3.0 diff --git a/resonant-circuit/coil-python/requirements.txt b/resonant-circuit/coil-python/requirements.txt index 2b8edf2ba..814a5c7dc 100644 --- a/resonant-circuit/coil-python/requirements.txt +++ b/resonant-circuit/coil-python/requirements.txt @@ -1,3 +1,3 @@ numpy >1, <2 -scipy +scipy>=1.10,<2 pyprecice~=3.0 diff --git a/tools/report_tutorial_requirements.py b/tools/report_tutorial_requirements.py new file mode 100644 index 000000000..e85e6a2fe --- /dev/null +++ b/tools/report_tutorial_requirements.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +""" +Report resolved Python dependency versions for tutorial requirements.txt files. + +Scans every tutorial requirements.txt (excluding tools/tests/), queries PyPI for +the latest version satisfying each constraint, and writes requirements-reference.txt. + +Run from the repository root: + python3 tools/report_tutorial_requirements.py + python3 tools/report_tutorial_requirements.py --check +""" +from __future__ import annotations + +import argparse +import json +import re +import subprocess +import sys +import urllib.error +import urllib.request +from datetime import datetime, timezone +from pathlib import Path + +from packaging.requirements import Requirement +from packaging.specifiers import SpecifierSet +from packaging.utils import canonicalize_name +from packaging.version import Version + +REPO_ROOT = Path(__file__).resolve().parent.parent +REFERENCE_FILE = REPO_ROOT / "requirements-reference.txt" +EXCLUDED_RELATIVE = { + "tools/tests/requirements.txt", + # Not a pip requirements file (lists stdlib os, unpinned vtk); see develop naming quirk. + "flow-over-heated-plate/plot-final-interface-temperature-requirements.txt", +} + +_PYPI_VERSIONS_CACHE: dict[str, list[Version]] = {} +_RESOLVED_CACHE: dict[str, str | None] = {} + +_HEADER = """\ +# Pinned Python dependency versions for preCICE tutorial distributions. +# Reference manifest only: tutorial run scripts keep using loose requirements.txt constraints. +# Generated by tools/report_tutorial_requirements.py — do not edit manually. +# Generated: {timestamp} + +""" + + +def discover_requirements_files() -> list[Path]: + """Return git-tracked tutorial requirements.txt files only.""" + result = subprocess.run( + ["git", "ls-files", "--", "**/requirements.txt"], + cwd=REPO_ROOT, + capture_output=True, + text=True, + check=True, + ) + files: list[Path] = [] + for line in result.stdout.splitlines(): + relative = line.strip() + if not relative or relative in EXCLUDED_RELATIVE: + continue + files.append(REPO_ROOT / relative) + return sorted(files) + + +def parse_requirement_line(line: str) -> str | None: + stripped = line.strip() + if not stripped or stripped.startswith("#"): + return None + if " #" in stripped: + stripped = stripped.split(" #", 1)[0].strip() + return stripped or None + + +def fetch_pypi_versions(package_name: str) -> list[Version]: + canonical = canonicalize_name(package_name) + if canonical in _PYPI_VERSIONS_CACHE: + return _PYPI_VERSIONS_CACHE[canonical] + + url = f"https://pypi.org/pypi/{package_name}/json" + request = urllib.request.Request(url, headers={"Accept": "application/json"}) + try: + with urllib.request.urlopen(request, timeout=30) as response: + payload = json.load(response) + except urllib.error.HTTPError as exc: + if exc.code == 404: + return [] + raise + + versions: list[Version] = [] + for version_str in payload.get("releases", {}): + try: + versions.append(Version(version_str)) + except Exception: + continue + _PYPI_VERSIONS_CACHE[canonical] = versions + return versions + + +def resolve_pypi_version(requirement: Requirement) -> str | None: + cache_key = str(requirement) + if cache_key in _RESOLVED_CACHE: + return _RESOLVED_CACHE[cache_key] + + if requirement.url: + _RESOLVED_CACHE[cache_key] = None + return None + + versions = fetch_pypi_versions(requirement.name) + if not versions: + _RESOLVED_CACHE[cache_key] = None + return None + + specifier = requirement.specifier if str(requirement.specifier) else SpecifierSet() + compatible = [version for version in versions if specifier.contains(version, prereleases=False)] + if not compatible: + _RESOLVED_CACHE[cache_key] = None + return None + resolved = str(max(compatible)) + _RESOLVED_CACHE[cache_key] = resolved + return resolved + + +def format_resolved_requirement(requirement_line: str) -> str: + requirement = Requirement(requirement_line) + if requirement.url: + return requirement_line + + resolved = resolve_pypi_version(requirement) + if resolved is None: + return f"{requirement_line} # unresolved (not on PyPI or no matching version)" + return f"{requirement.name}=={resolved}" + + +def generate_reference_text() -> str: + timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + sections: list[str] = [_HEADER.format(timestamp=timestamp)] + + for req_file in discover_requirements_files(): + relative = req_file.relative_to(REPO_ROOT).as_posix() + sections.append(f"# {relative}\n") + for line in req_file.read_text().splitlines(): + requirement_line = parse_requirement_line(line) + if requirement_line is None: + continue + sections.append(format_resolved_requirement(requirement_line) + "\n") + sections.append("\n") + + return "".join(sections) + + +def normalize_for_check(text: str) -> str: + """Ignore generation timestamp when comparing reference files.""" + return re.sub(r"^# Generated: .*\n", "# Generated: \n", text, count=1, flags=re.MULTILINE) + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Report resolved tutorial Python dependencies from PyPI" + ) + parser.add_argument( + "--check", + action="store_true", + help="Exit 1 if requirements-reference.txt is out of date", + ) + parser.add_argument( + "--output", + type=Path, + default=REFERENCE_FILE, + help="Output path (default: requirements-reference.txt at repo root)", + ) + args = parser.parse_args() + + generated = generate_reference_text() + + if args.check: + if not args.output.exists(): + print( + f"ERROR: {args.output} not found. Run: python3 tools/report_tutorial_requirements.py", + file=sys.stderr, + ) + return 1 + current = args.output.read_text() + if normalize_for_check(current) != normalize_for_check(generated): + print( + f"ERROR: {args.output} is out of date. Run: python3 tools/report_tutorial_requirements.py", + file=sys.stderr, + ) + return 1 + print(f"OK: {args.output} is up to date") + return 0 + + args.output.write_text(generated) + print(f"Wrote {args.output}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/tests/README.md b/tools/tests/README.md index 620b76164..c225a57f8 100644 --- a/tools/tests/README.md +++ b/tools/tests/README.md @@ -207,6 +207,19 @@ User-facing tools: - `build_docker_images.py`: Build the Docker images for each test - `generate_reference_results.py`: Executes the system tests with the versions defined in `reference_versions.yaml` and generates the reference data archives, with the names described in `tests.yaml`. (should only be used by the CI Pipeline) +### Tutorial Python dependencies (issue [#610](https://github.com/precice/tutorials/issues/610)) + +Each Python-based tutorial case has a `requirements.txt` with **loose version constraints** (e.g. `numpy >1, <2`, `pyprecice~=3.0`) so the latest compatible packages can be installed during development. + +For **distribution releases**, regenerate the root-level `requirements-reference.txt` lockfile manifest. This records the latest PyPI versions that satisfy each tutorial's constraints at release time, without pinning every tutorial file: + +```bash +pip install packaging +python3 tools/report_tutorial_requirements.py +``` + +CI validates the reference file with `python3 tools/validate_requirements_reference.py` (or `--check` on the report script). + Implementation scripts: - `tools/tests/` diff --git a/tools/validate_requirements_reference.py b/tools/validate_requirements_reference.py new file mode 100644 index 000000000..ecb0bbc70 --- /dev/null +++ b/tools/validate_requirements_reference.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +""" +Validate that requirements-reference.txt matches tutorial requirements.txt constraints. + +Delegates to report_tutorial_requirements.py --check. +""" +import subprocess +import sys +from pathlib import Path + +SCRIPT = Path(__file__).parent / "report_tutorial_requirements.py" + + +def main() -> int: + result = subprocess.run([sys.executable, str(SCRIPT), "--check"], check=False) + return result.returncode + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/turek-hron-fsi3/fluid-nutils/requirements.txt b/turek-hron-fsi3/fluid-nutils/requirements.txt index de8073acb..f324f08fe 100644 --- a/turek-hron-fsi3/fluid-nutils/requirements.txt +++ b/turek-hron-fsi3/fluid-nutils/requirements.txt @@ -1,7 +1,7 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=9.0 numpy >1, <2 pyprecice~=3.0 -meshio -gmsh -matplotlib +meshio>=5,<6 +gmsh>=4,<5 +matplotlib>=3.5,<4 diff --git a/turek-hron-fsi3/solid-nutils/requirements.txt b/turek-hron-fsi3/solid-nutils/requirements.txt index de8073acb..f324f08fe 100644 --- a/turek-hron-fsi3/solid-nutils/requirements.txt +++ b/turek-hron-fsi3/solid-nutils/requirements.txt @@ -1,7 +1,7 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=9.0 numpy >1, <2 pyprecice~=3.0 -meshio -gmsh -matplotlib +meshio>=5,<6 +gmsh>=4,<5 +matplotlib>=3.5,<4 diff --git a/two-scale-heat-conduction/macro-nutils/requirements.txt b/two-scale-heat-conduction/macro-nutils/requirements.txt index 7ff7b5aa1..f22e7ac25 100644 --- a/two-scale-heat-conduction/macro-nutils/requirements.txt +++ b/two-scale-heat-conduction/macro-nutils/requirements.txt @@ -1,4 +1,4 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=7.3 numpy >1, <2 pyprecice~=3.0 diff --git a/two-scale-heat-conduction/micro-nutils/requirements.txt b/two-scale-heat-conduction/micro-nutils/requirements.txt index 6bcbfdaff..8256d3119 100644 --- a/two-scale-heat-conduction/micro-nutils/requirements.txt +++ b/two-scale-heat-conduction/micro-nutils/requirements.txt @@ -1,5 +1,5 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=7.3 numpy >1, <2 pyprecice~=3.0 -micro-manager-precice +micro-manager-precice~=0.10 diff --git a/volume-coupled-flow/source-nutils/requirements.txt b/volume-coupled-flow/source-nutils/requirements.txt index 7ff7b5aa1..f22e7ac25 100644 --- a/volume-coupled-flow/source-nutils/requirements.txt +++ b/volume-coupled-flow/source-nutils/requirements.txt @@ -1,4 +1,4 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=7.3 numpy >1, <2 pyprecice~=3.0 diff --git a/water-hammer/fluid1d-left-nutils/requirements.txt b/water-hammer/fluid1d-left-nutils/requirements.txt index 721a30444..34177a257 100644 --- a/water-hammer/fluid1d-left-nutils/requirements.txt +++ b/water-hammer/fluid1d-left-nutils/requirements.txt @@ -1,6 +1,6 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=9.0 numpy >1, <2 pyprecice~=3.0 -matplotlib -treelog \ No newline at end of file +matplotlib>=3.5,<4 +treelog>=1,<2 \ No newline at end of file diff --git a/water-hammer/fluid1d-right-nutils/requirements.txt b/water-hammer/fluid1d-right-nutils/requirements.txt index 721a30444..34177a257 100644 --- a/water-hammer/fluid1d-right-nutils/requirements.txt +++ b/water-hammer/fluid1d-right-nutils/requirements.txt @@ -1,6 +1,6 @@ -setuptools # required by nutils +setuptools>=69.0.0 # required by nutils nutils~=9.0 numpy >1, <2 pyprecice~=3.0 -matplotlib -treelog \ No newline at end of file +matplotlib>=3.5,<4 +treelog>=1,<2 \ No newline at end of file diff --git a/wolf-sheep-soil-creep/soil-creep-landlab/requirements.txt b/wolf-sheep-soil-creep/soil-creep-landlab/requirements.txt index 16efd4ddd..b414e0236 100644 --- a/wolf-sheep-soil-creep/soil-creep-landlab/requirements.txt +++ b/wolf-sheep-soil-creep/soil-creep-landlab/requirements.txt @@ -1,4 +1,4 @@ numpy >1, <2 -matplotlib -landlab +matplotlib>=3.5,<4 +landlab>=2.0,<3 pyprecice~=3.0 diff --git a/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/requirements.txt b/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/requirements.txt index f9ccca730..355f9e10d 100644 --- a/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/requirements.txt +++ b/wolf-sheep-soil-creep/wolf-sheep-grass-mesa/requirements.txt @@ -1,5 +1,5 @@ numpy >1, <2 -matplotlib -mesa>=3 +matplotlib>=3.5,<4 +mesa>=3,<4 pyprecice~=3.0 -networkx +networkx>=3,<4