diff --git a/partitioned-pipe-multiscale/README.md b/partitioned-pipe-multiscale/README.md new file mode 100644 index 000000000..ffc197e5c --- /dev/null +++ b/partitioned-pipe-multiscale/README.md @@ -0,0 +1,201 @@ +--- +title: Partitioned Pipe — Geometric Axial Multiscale +keywords: OpenFOAM, Nutils, preCICE, geometric multiscale, fluid +summary: The Partitioned Pipe — Geometric Axial Multiscale tutorial couples a 1D pipe model with a 3D CFD pipe using preCICE. +--- + +## Setup + +We solve a simple **partitioned pipe problem** using a 1D–3D coupling approach. +In this tutorial, the computational domain is split into two coupled regions: a 1D pipe section and a 3D pipe section. +The coupling is performed using **preCICE**. + +In the following, `1D` denotes the reduced-order domain (e.g., a Nutils solver) and `3D` denotes the full 3D CFD domain (e.g., OpenFOAM). + +The problem consists of a straight pipe of length + +$$ +L = 40~\text{m} +$$ + +and diameter + +$$ +D = 10~\text{m}. +$$ + +We partition the domain at + +$$ +y_c = 20~\text{m}, +$$ + +where the coupling interface is located. +The **1D domain** solves the flow equations using Nutils, while the **3D domain** is solved using OpenFOAM. +Both solvers are coupled via preCICE by exchanging the **pressure** and **axial velocity** at the interface. + +Two coupling directions are possible: + +- **1D → 3D**: The 1D solver provides the interface velocity to the 3D solver, which responds with pressure. +- **3D → 1D**: The 3D solver provides the velocity, and the 1D solver returns the pressure. + +The outlet pressure is set to + +$$ +p_\text{in} = 98100~\text{Pa}. +$$ + +For the **3D → 1D** coupling, the 3D inlet velocity is prescribed as a **parabolic (Poiseuille)** profile with a bulk velocity of + +$$ +u_\text{in} = 0.1~\text{m/s}. +$$ + +implemented using a `codedFixedValue` boundary condition. This ensures a physically realistic velocity distribution consistent with the 1D model. + +For the **1D → 3D** coupling, the inlet velocity is set to + +$$ +u_\text{in} = 0.1~\text{m/s}. +$$ + +## Configuration + +preCICE configuration for the 1D-3D simulation (image generated using the [precice-config-visualizer](https://precice.org/tooling-config-visualization.html)): + +![preCICE configuration visualization 1D-3D](images/tutorials-partitioned-pipe-multiscale-1d3d-config.png) + +preCICE configuration for the 3D-1D simulation: + +![preCICE configuration visualization 3D-1D](images/tutorials-partitioned-pipe-multiscale-3d1d-config.png) + +## Available solvers + +- OpenFOAM (**pimpleFoam**). An incompressible/transient OpenFOAM solver. See the [OpenFOAM adapter documentation](https://precice.org/adapter-openfoam-overview.html). +- Nutils. A Python-based finite element framework. For more information, see the [Nutils adapter documentation](https://precice.org/adapter-nutils.html) + +## Running the Simulation + +First, select which coupling you want to run. This sets the correct `precice-config.xml` symlink: + +```bash +# Choose one (1D → 3D or 3D → 1D) +./setcase.sh 1d3d +# or +./setcase.sh 3d1d +``` + +Open **two terminals** and start the corresponding participants for your chosen setup. + +### Example A — 1D → 3D coupling + +Terminal 1: + +```bash +cd fluid1d-left +./run.sh +``` + +Terminal 2: + +```bash +cd fluid3d-right +./run.sh +``` + +### Example B — 3D → 1D coupling + +Terminal 1: + +```bash +cd fluid3d-left +./run.sh +``` + +Terminal 2: + +```bash +cd fluid1d-right +./run.sh +``` + +> Tip: If you switch coupling direction later, rerun `./setcase.sh` with the other option before launching the participants. + +## Visualization + +The output of the coupled simulation is written into the folders `fluid1d-left`, `fluid1d-right`, `fluid3d-left`, and `fluid3d-right`, depending on which coupling direction (`1d3d` or `3d1d`) you selected. + +### 3D domain (OpenFOAM) + +For the 3D participant, all simulation results are stored in the time directories inside the respective case folder (e.g., `fluid3d-right/`). +You can visualize the flow field and pressure distribution using **ParaView** by opening the case file: + +```bash +paraview fluid3d-right/fluid3d-right.foam +``` + +or, for the left domain if applicable: + +```bash +paraview fluid3d-left/fluid3d-left.foam +``` + +Typical fields to inspect include: + +- `p` – pressure +- `U` – velocity + +We also record pressure and velocity at fixed points each time step using the OpenFOAM `probes` function object. + +**Probe setup (excerpt):** + +```c +#includeEtc "caseDicts/postProcessing/probes/probes.cfg" + +fields (p U); +probeLocations +( + (0 0 20) + (0 0 40) +); +// For the left 3D domain use instead: +// probeLocations ((0 0 0) (0 0 20)); +``` + +**Output location:** + +- `fluid3d-right/postProcessing/probes/0/p` +- `fluid3d-right/postProcessing/probes/0/U` + +### 1D domain (Nutils) + +The 1D solver writes a `watchpoint.txt` with semicolon-separated time series: + +```text +time; p_in; u_in; p_out; u_out; p_mid; u_mid +``` + +where: + +- `p_in`, `u_in` → pressure and velocity at the inlet of the 1D domain +- `p_out`, `u_out` → pressure and velocity at the outlet of the 1D domain +- `p_mid`, `u_mid` → pressure and velocity at the midpoint of the 1D domain + +The 1D solver also writes a `final_fields.txt with space-separated values: + +```text +x u p +``` + +They correspond to the axial position, velocity and pressure at the last time-step, it is, at a time of 20 s. + +### Example visualization + +![Pressure distribution along the main axis in the 3D-1D Coupled Pipe](images/tutorials-partitioned-pipe-multiscale-3d1d-pressure-distribution.png) + +**Pressure along the pipe centerline.** The pressure decreases nearly linearly from **≈12.8 Pa** at the 3D inlet to **0 Pa** at the 1D outlet, consistent with steady, laminar Poiseuille flow. The 3D (0–20 m) and 1D (20–40 m) sections connect smoothly at the coupling interface. + +![Velocity at the 3D coupling interface in the 3D-1D Coupled Pipe](images/tutorials-partitioned-pipe-multiscale-3d1d-velocityProfileInterface3d.png) + +**Parabolic velocity profile at the 3D outlet / coupling interface (y = 20 m).** +The profile is Poiseuille-like with bulk velocity **0.1 m/s**; consequently the **centerline velocity is ≈ 0.2 m/s** (≈ 2 × bulk) and vanishes at the wall (no-slip). This is the velocity state at the interface used for coupling to the 1D domain. diff --git a/partitioned-pipe-multiscale/clean-tutorial.sh b/partitioned-pipe-multiscale/clean-tutorial.sh new file mode 100755 index 000000000..5dbb6c639 --- /dev/null +++ b/partitioned-pipe-multiscale/clean-tutorial.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env sh +set -e -u + +# shellcheck disable=SC1091 +. ../tools/cleaning-tools.sh + +clean_tutorial . +clean_precice_logs . +rm -fv ./*.log +rm -fv ./*.vtu + diff --git a/partitioned-pipe-multiscale/fluid1d-left/clean.sh b/partitioned-pipe-multiscale/fluid1d-left/clean.sh new file mode 100755 index 000000000..37fdf7bb2 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid1d-left/clean.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env sh +set -e -u + +. ../../tools/cleaning-tools.sh + +rm -f ./results/Fluid1D_* +clean_precice_logs . +clean_case_logs . \ No newline at end of file diff --git a/partitioned-pipe-multiscale/fluid1d-left/requirements.txt b/partitioned-pipe-multiscale/fluid1d-left/requirements.txt new file mode 100644 index 000000000..e8f4e85da --- /dev/null +++ b/partitioned-pipe-multiscale/fluid1d-left/requirements.txt @@ -0,0 +1,5 @@ +setuptools # required by nutils +nutils==7 +numpy >1, <2 +pyprecice~=3.0 +matplotlib \ No newline at end of file diff --git a/partitioned-pipe-multiscale/fluid1d-left/run.sh b/partitioned-pipe-multiscale/fluid1d-left/run.sh new file mode 100755 index 000000000..c70a00114 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid1d-left/run.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +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 + +NUTILS_RICHOUTPUT=no python3 ../solver-fluid1d/Fluid1D.py side=Left + +close_log diff --git a/partitioned-pipe-multiscale/fluid1d-right/clean.sh b/partitioned-pipe-multiscale/fluid1d-right/clean.sh new file mode 100755 index 000000000..37fdf7bb2 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid1d-right/clean.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env sh +set -e -u + +. ../../tools/cleaning-tools.sh + +rm -f ./results/Fluid1D_* +clean_precice_logs . +clean_case_logs . \ No newline at end of file diff --git a/partitioned-pipe-multiscale/fluid1d-right/requirements.txt b/partitioned-pipe-multiscale/fluid1d-right/requirements.txt new file mode 100644 index 000000000..e8f4e85da --- /dev/null +++ b/partitioned-pipe-multiscale/fluid1d-right/requirements.txt @@ -0,0 +1,5 @@ +setuptools # required by nutils +nutils==7 +numpy >1, <2 +pyprecice~=3.0 +matplotlib \ No newline at end of file diff --git a/partitioned-pipe-multiscale/fluid1d-right/run.sh b/partitioned-pipe-multiscale/fluid1d-right/run.sh new file mode 100755 index 000000000..be96928df --- /dev/null +++ b/partitioned-pipe-multiscale/fluid1d-right/run.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +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 + +NUTILS_RICHOUTPUT=no python3 ../solver-fluid1d/Fluid1D.py side=Right + +close_log diff --git a/partitioned-pipe-multiscale/fluid3d-left/0/U b/partitioned-pipe-multiscale/fluid3d-left/0/U new file mode 100644 index 000000000..7b2467e54 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-left/0/U @@ -0,0 +1,61 @@ +FoamFile +{ + version 2.0; + format ascii; + class volVectorField; + location "0"; + object U; +} + +dimensions [0 1 -1 0 0 0 0]; + +internalField uniform (0 0 0); + +boundaryField +{ + inlet + { + type codedFixedValue; + value uniform (0 0 0); // dummy fallback + name parabolicInlet; + + code + #{ + // User parameters — edit these: + const scalar Uinlet = 0.1; // m/s (set this) + const scalar R = 5.0; // m (pipe radius) + const vector axis = vector(0,0,1); // flow axis (unit!) + const vector ctr = vector(0,0,0); // inlet center (global coords) + + // Ensure axis is unit length + const vector e = axis/mag(axis); + + // Face centres on this patch + const vectorField& Cf = patch().Cf(); + + // Reference to the patch field to write into + vectorField& Up = *this; + + forAll(Cf, i) + { + const vector x = Cf[i]; + const vector rvec = x - ctr; + // radial component: subtract axial projection + const vector rperp = rvec - (rvec & e)*e; + const scalar r = mag(rperp); + + scalar u = 2 * Uinlet * max(0.0, 1.0 - sqr(r/R)); // clip outside R + Up[i] = u * e; + } + #}; + } + outlet + { + type fixedGradient; + gradient uniform (0 0 0); + } + fixedWalls + { + type noSlip; + } +} diff --git a/partitioned-pipe-multiscale/fluid3d-left/0/p b/partitioned-pipe-multiscale/fluid3d-left/0/p new file mode 100644 index 000000000..ada3a6d88 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-left/0/p @@ -0,0 +1,31 @@ +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + object p; +} + +dimensions [0 2 -2 0 0 0 0]; + +internalField uniform 0; + +boundaryField +{ + inlet + { + type fixedGradient; + gradient uniform 0; + } + + outlet + { + type fixedValue; + value uniform 0; + } + + fixedWalls + { + type zeroGradient; + } +} diff --git a/partitioned-pipe-multiscale/fluid3d-left/clean.sh b/partitioned-pipe-multiscale/fluid3d-left/clean.sh new file mode 100755 index 000000000..b64fc5101 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-left/clean.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env sh +set -e -u + +. ../../tools/cleaning-tools.sh + +clean_openfoam . diff --git a/partitioned-pipe-multiscale/fluid3d-left/constant/transportProperties b/partitioned-pipe-multiscale/fluid3d-left/constant/transportProperties new file mode 100644 index 000000000..cd4e5ca98 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-left/constant/transportProperties @@ -0,0 +1,12 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant"; + object transportProperties; +} + +transportModel Newtonian; + +nu nu [ 0 2 -1 0 0 0 0 ] 10; diff --git a/partitioned-pipe-multiscale/fluid3d-left/constant/turbulenceProperties b/partitioned-pipe-multiscale/fluid3d-left/constant/turbulenceProperties new file mode 100644 index 000000000..2c55a8b28 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-left/constant/turbulenceProperties @@ -0,0 +1,10 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant"; + object turbulenceProperties; +} + +simulationType laminar; diff --git a/partitioned-pipe-multiscale/fluid3d-left/run.sh b/partitioned-pipe-multiscale/fluid3d-left/run.sh new file mode 100755 index 000000000..d88fced00 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-left/run.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -e -u + +. ../../tools/log.sh +exec > >(tee --append "$LOGFILE") 2>&1 + +blockMesh + +../../tools/run-openfoam.sh "$@" +. ../../tools/openfoam-remove-empty-dirs.sh && openfoam_remove_empty_dirs + +close_log \ No newline at end of file diff --git a/partitioned-pipe-multiscale/fluid3d-left/system/blockMeshDict b/partitioned-pipe-multiscale/fluid3d-left/system/blockMeshDict new file mode 100644 index 000000000..9912e3f9b --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-left/system/blockMeshDict @@ -0,0 +1,100 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant/polyMesh"; + object blockMeshDict; +} + +convertToMeters 1; + + +zmin 0; +zmax 20; + +xcells 10; // per block (5 blocks) +ycells 10; // per block (5 blocks) +zcells 20; + +vertices +( + (-3.535534 -3.535534 $zmin) // 0 + ( 3.535534 -3.535534 $zmin) // 1 + ( 3.535534 3.535534 $zmin) // 2 + (-3.535534 3.535534 $zmin) // 3 + (-1.414214 -1.414214 $zmin) // 4 + ( 1.414214 -1.414214 $zmin) // 5 + ( 1.414214 1.414214 $zmin) // 6 + (-1.414214 1.414214 $zmin) // 7 + + (-3.535534 -3.535534 $zmax) // 8 + ( 3.535534 -3.535534 $zmax) // 9 + ( 3.535534 3.535534 $zmax) // 10 + (-3.535534 3.535534 $zmax) // 11 + (-1.414214 -1.414214 $zmax) // 12 + ( 1.414214 -1.414214 $zmax) // 13 + ( 1.414214 1.414214 $zmax) // 14 + (-1.414214 1.414214 $zmax) // 15 +); + +blocks +( +hex (0 1 5 4 8 9 13 12) pipe ($xcells $ycells $zcells) simpleGrading (1 5 1) +hex (1 2 6 5 9 10 14 13) pipe ($xcells $ycells $zcells) simpleGrading (1 5 1) +hex (2 3 7 6 10 11 15 14) pipe ($xcells $ycells $zcells) simpleGrading (1 5 1) +hex (3 0 4 7 11 8 12 15) pipe ($xcells $ycells $zcells) simpleGrading (1 5 1) +hex (4 5 6 7 12 13 14 15) pipe ($xcells $ycells $zcells) simpleGrading (1 1 1) +); + +edges +( +arc 0 1 ( 0 -5 $zmin) +arc 1 2 ( 5 0 $zmin) +arc 2 3 ( 0 5 $zmin) +arc 3 0 (-5 0 $zmin) +arc 4 5 ( 0 -1.5 $zmin) +arc 5 6 ( 1.5 0 $zmin) +arc 6 7 ( 0 1.5 $zmin) +arc 7 4 (-1.5 0 $zmin) + +arc 8 9 ( 0 -5 $zmax) +arc 9 10 ( 5 0 $zmax) +arc 10 11 ( 0 5 $zmax) +arc 11 8 (-5 0 $zmax) +arc 12 13 ( 0 -1.5 $zmax) +arc 13 14 (1.5 0 $zmax) +arc 14 15 ( 0 1.5 $zmax) +arc 15 12 (-1.5 0 $zmax) +); + + +patches +( + + patch fixedWalls + ( + (0 1 9 8) + (1 2 10 9) + (2 3 11 10) + (3 0 8 11) + ) + + patch inlet + ( + (0 1 5 4) + (1 2 6 5) + (2 3 7 6) + (3 0 4 7) + (4 5 6 7) + ) + + patch outlet + ( + (8 9 13 12) + (9 10 14 13) + (10 11 15 14) + (11 8 12 15) + (12 13 14 15) + ) +); diff --git a/partitioned-pipe-multiscale/fluid3d-left/system/controlDict b/partitioned-pipe-multiscale/fluid3d-left/system/controlDict new file mode 100644 index 000000000..473572057 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-left/system/controlDict @@ -0,0 +1,46 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object controlDict; +} + +application pimpleFoam; + +startFrom startTime; + +startTime 0; + +stopAt endTime; + +endTime 20.0; + +deltaT 0.01; + +writeControl adjustableRunTime; + +writeInterval 0.1; + +purgeWrite 0; + +writeFormat ascii; + +writePrecision 6; + +writeCompression off; + +timeFormat general; + +timePrecision 6; + +functions +{ + preCICE_Adapter + { + type preciceAdapterFunctionObject; + libs ("libpreciceAdapterFunctionObject.so"); + } + #includeFunc probes +} diff --git a/partitioned-pipe-multiscale/fluid3d-left/system/decomposeParDict b/partitioned-pipe-multiscale/fluid3d-left/system/decomposeParDict new file mode 100644 index 000000000..ad99cf6c5 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-left/system/decomposeParDict @@ -0,0 +1,16 @@ +FoamFile { + version 2.0; + class dictionary; + object decomposeParDict; + format ascii; +} + +numberOfSubdomains 2; + +method simple; + +simpleCoeffs +{ + n (2 1 1); + delta 0.001; +} diff --git a/partitioned-pipe-multiscale/fluid3d-left/system/fvSchemes b/partitioned-pipe-multiscale/fluid3d-left/system/fvSchemes new file mode 100644 index 000000000..a22010ae4 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-left/system/fvSchemes @@ -0,0 +1,40 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object fvSchemes; +} + +ddtSchemes +{ + default Euler; +} + +gradSchemes +{ + default Gauss linear; +} + +divSchemes +{ + default none; + div(phi,U) bounded Gauss upwind; + div((nuEff*dev2(T(grad(U))))) Gauss linear; +} + +laplacianSchemes +{ + default Gauss linear corrected; +} + +interpolationSchemes +{ + default linear; +} + +snGradSchemes +{ + default corrected; +} diff --git a/partitioned-pipe-multiscale/fluid3d-left/system/fvSolution b/partitioned-pipe-multiscale/fluid3d-left/system/fvSolution new file mode 100644 index 000000000..ff341d211 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-left/system/fvSolution @@ -0,0 +1,72 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object fvSolution; +} + +solvers +{ + + p + { + solver PCG; + preconditioner DIC; + tolerance 1e-8; + relTol 1.0e-3; + } + + pFinal + { + $p; + relTol 0; + } + + pcorr + { + $p; + } + + pcorrFinal + { + $pcorr; + relTol 0; + } + + Phi + { + $p; + } + + "(U|cellDisplacement)" + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-6; + relTol 1e-4; + minIter 2; + } + + "(U|cellDisplacement)Final" + { + $U; + relTol 0; + } +} + +PIMPLE +{ + nCorrectors 4; + nNonOrthogonalCorrectors 1; + consistent true; + correctPhi true; + momentumPredictor true; + nOuterCorrectors 1; +} + +potentialFlow +{ + nNonOrthogonalCorrectors 10; +} diff --git a/partitioned-pipe-multiscale/fluid3d-left/system/preciceDict b/partitioned-pipe-multiscale/fluid3d-left/system/preciceDict new file mode 100644 index 000000000..35af1214d --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-left/system/preciceDict @@ -0,0 +1,33 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object preciceDict; +} + +preciceConfig "../precice-config.xml"; + +participant Fluid3DLeft; + +modules (FF); + +interfaces +{ + Interface1 + { + mesh Fluid3DLeft-Mesh; + patches (outlet); + + readData + ( + Pressure + ); + + writeData + ( + Velocity + ); + }; +}; \ No newline at end of file diff --git a/partitioned-pipe-multiscale/fluid3d-left/system/probes b/partitioned-pipe-multiscale/fluid3d-left/system/probes new file mode 100644 index 000000000..65fc5b32b --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-left/system/probes @@ -0,0 +1,11 @@ +#includeEtc "caseDicts/postProcessing/probes/probes.cfg" + +fields (p U); +probeLocations +( + (0 0 0) + (0 0 20) + +); + +// ************************************************************************* // diff --git a/partitioned-pipe-multiscale/fluid3d-right/0/U b/partitioned-pipe-multiscale/fluid3d-right/0/U new file mode 100644 index 000000000..ff2facc9d --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-right/0/U @@ -0,0 +1,30 @@ +FoamFile +{ + version 2.0; + format ascii; + class volVectorField; + location "0"; + object U; +} + +dimensions [0 1 -1 0 0 0 0]; + +internalField uniform (0 0 0); + +boundaryField +{ + inlet + { + type fixedValue; + value uniform (0 0 0); + } + outlet + { + type fixedGradient; + gradient uniform (0 0 0); + } + fixedWalls + { + type noSlip; + } +} diff --git a/partitioned-pipe-multiscale/fluid3d-right/0/p b/partitioned-pipe-multiscale/fluid3d-right/0/p new file mode 100644 index 000000000..ca74a0962 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-right/0/p @@ -0,0 +1,30 @@ +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + object p; +} + +dimensions [0 2 -2 0 0 0 0]; + +internalField uniform 0; + +boundaryField +{ + inlet + { + type fixedFluxExtrapolatedPressure;; + } + + outlet + { + type fixedValue; + value uniform 0; + } + + fixedWalls + { + type zeroGradient; + } +} diff --git a/partitioned-pipe-multiscale/fluid3d-right/clean.sh b/partitioned-pipe-multiscale/fluid3d-right/clean.sh new file mode 100755 index 000000000..b64fc5101 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-right/clean.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env sh +set -e -u + +. ../../tools/cleaning-tools.sh + +clean_openfoam . diff --git a/partitioned-pipe-multiscale/fluid3d-right/constant/transportProperties b/partitioned-pipe-multiscale/fluid3d-right/constant/transportProperties new file mode 100644 index 000000000..cd4e5ca98 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-right/constant/transportProperties @@ -0,0 +1,12 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant"; + object transportProperties; +} + +transportModel Newtonian; + +nu nu [ 0 2 -1 0 0 0 0 ] 10; diff --git a/partitioned-pipe-multiscale/fluid3d-right/constant/turbulenceProperties b/partitioned-pipe-multiscale/fluid3d-right/constant/turbulenceProperties new file mode 100644 index 000000000..2c55a8b28 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-right/constant/turbulenceProperties @@ -0,0 +1,10 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant"; + object turbulenceProperties; +} + +simulationType laminar; diff --git a/partitioned-pipe-multiscale/fluid3d-right/run.sh b/partitioned-pipe-multiscale/fluid3d-right/run.sh new file mode 100755 index 000000000..d88fced00 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-right/run.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -e -u + +. ../../tools/log.sh +exec > >(tee --append "$LOGFILE") 2>&1 + +blockMesh + +../../tools/run-openfoam.sh "$@" +. ../../tools/openfoam-remove-empty-dirs.sh && openfoam_remove_empty_dirs + +close_log \ No newline at end of file diff --git a/partitioned-pipe-multiscale/fluid3d-right/system/blockMeshDict b/partitioned-pipe-multiscale/fluid3d-right/system/blockMeshDict new file mode 100644 index 000000000..3c4aa2bf9 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-right/system/blockMeshDict @@ -0,0 +1,100 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant/polyMesh"; + object blockMeshDict; +} + +convertToMeters 1; + + +zmin 20; +zmax 40; + +xcells 10; // per block (5 blocks) +ycells 10; // per block (5 blocks) +zcells 20; + +vertices +( + (-3.535534 -3.535534 $zmin) // 0 + ( 3.535534 -3.535534 $zmin) // 1 + ( 3.535534 3.535534 $zmin) // 2 + (-3.535534 3.535534 $zmin) // 3 + (-1.414214 -1.414214 $zmin) // 4 + ( 1.414214 -1.414214 $zmin) // 5 + ( 1.414214 1.414214 $zmin) // 6 + (-1.414214 1.414214 $zmin) // 7 + + (-3.535534 -3.535534 $zmax) // 8 + ( 3.535534 -3.535534 $zmax) // 9 + ( 3.535534 3.535534 $zmax) // 10 + (-3.535534 3.535534 $zmax) // 11 + (-1.414214 -1.414214 $zmax) // 12 + ( 1.414214 -1.414214 $zmax) // 13 + ( 1.414214 1.414214 $zmax) // 14 + (-1.414214 1.414214 $zmax) // 15 +); + +blocks +( +hex (0 1 5 4 8 9 13 12) pipe ($xcells $ycells $zcells) simpleGrading (1 5 1) +hex (1 2 6 5 9 10 14 13) pipe ($xcells $ycells $zcells) simpleGrading (1 5 1) +hex (2 3 7 6 10 11 15 14) pipe ($xcells $ycells $zcells) simpleGrading (1 5 1) +hex (3 0 4 7 11 8 12 15) pipe ($xcells $ycells $zcells) simpleGrading (1 5 1) +hex (4 5 6 7 12 13 14 15) pipe ($xcells $ycells $zcells) edgeGrading (1 1 1 1 1 1 1 1 1 1 1 1) +); + +edges +( +arc 0 1 ( 0 -5 $zmin) +arc 1 2 ( 5 0 $zmin) +arc 2 3 ( 0 5 $zmin) +arc 3 0 (-5 0 $zmin) +arc 4 5 ( 0 -1.5 $zmin) +arc 5 6 ( 1.5 0 $zmin) +arc 6 7 ( 0 1.5 $zmin) +arc 7 4 (-1.5 0 $zmin) + +arc 8 9 ( 0 -5 $zmax) +arc 9 10 ( 5 0 $zmax) +arc 10 11 ( 0 5 $zmax) +arc 11 8 (-5 0 $zmax) +arc 12 13 ( 0 -1.5 $zmax) +arc 13 14 (1.5 0 $zmax) +arc 14 15 ( 0 1.5 $zmax) +arc 15 12 (-1.5 0 $zmax) +); + + +patches +( + + patch fixedWalls + ( + (0 1 9 8) + (1 2 10 9) + (2 3 11 10) + (3 0 8 11) + ) + + patch inlet + ( + (0 1 5 4) + (1 2 6 5) + (2 3 7 6) + (3 0 4 7) + (4 5 6 7) + ) + + patch outlet + ( + (8 9 13 12) + (9 10 14 13) + (10 11 15 14) + (11 8 12 15) + (12 13 14 15) + ) +); diff --git a/partitioned-pipe-multiscale/fluid3d-right/system/controlDict b/partitioned-pipe-multiscale/fluid3d-right/system/controlDict new file mode 100644 index 000000000..473572057 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-right/system/controlDict @@ -0,0 +1,46 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object controlDict; +} + +application pimpleFoam; + +startFrom startTime; + +startTime 0; + +stopAt endTime; + +endTime 20.0; + +deltaT 0.01; + +writeControl adjustableRunTime; + +writeInterval 0.1; + +purgeWrite 0; + +writeFormat ascii; + +writePrecision 6; + +writeCompression off; + +timeFormat general; + +timePrecision 6; + +functions +{ + preCICE_Adapter + { + type preciceAdapterFunctionObject; + libs ("libpreciceAdapterFunctionObject.so"); + } + #includeFunc probes +} diff --git a/partitioned-pipe-multiscale/fluid3d-right/system/decomposeParDict b/partitioned-pipe-multiscale/fluid3d-right/system/decomposeParDict new file mode 100644 index 000000000..ad99cf6c5 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-right/system/decomposeParDict @@ -0,0 +1,16 @@ +FoamFile { + version 2.0; + class dictionary; + object decomposeParDict; + format ascii; +} + +numberOfSubdomains 2; + +method simple; + +simpleCoeffs +{ + n (2 1 1); + delta 0.001; +} diff --git a/partitioned-pipe-multiscale/fluid3d-right/system/fvSchemes b/partitioned-pipe-multiscale/fluid3d-right/system/fvSchemes new file mode 100644 index 000000000..a22010ae4 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-right/system/fvSchemes @@ -0,0 +1,40 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object fvSchemes; +} + +ddtSchemes +{ + default Euler; +} + +gradSchemes +{ + default Gauss linear; +} + +divSchemes +{ + default none; + div(phi,U) bounded Gauss upwind; + div((nuEff*dev2(T(grad(U))))) Gauss linear; +} + +laplacianSchemes +{ + default Gauss linear corrected; +} + +interpolationSchemes +{ + default linear; +} + +snGradSchemes +{ + default corrected; +} diff --git a/partitioned-pipe-multiscale/fluid3d-right/system/fvSolution b/partitioned-pipe-multiscale/fluid3d-right/system/fvSolution new file mode 100644 index 000000000..7e4fe2724 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-right/system/fvSolution @@ -0,0 +1,74 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object fvSolution; +} + +solvers +{ + + p + { + + solver PCG; + preconditioner DIC; + tolerance 1e-8; + relTol 1.0e-3; + } + + pFinal + { + $p; + relTol 0; + } + + pcorr + { + $p; + } + + pcorrFinal + { + $pcorr; + relTol 0; + } + + Phi + { + $p; + } + + "(U|cellDisplacement)" + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-6; + relTol 1e-4; + minIter 2; + } + + "(U|cellDisplacement)Final" + { + $U; + relTol 0; + } +} + +PIMPLE +{ + nCorrectors 4; + nNonOrthogonalCorrectors 1; + consistent true; + correctPhi true; + momentumPredictor true; + nOuterCorrectors 1; +} + + +potentialFlow +{ + nNonOrthogonalCorrectors 10; +} diff --git a/partitioned-pipe-multiscale/fluid3d-right/system/preciceDict b/partitioned-pipe-multiscale/fluid3d-right/system/preciceDict new file mode 100644 index 000000000..5eac33448 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-right/system/preciceDict @@ -0,0 +1,33 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object preciceDict; +} + +preciceConfig "../precice-config.xml"; + +participant Fluid3DRight; + +modules (FF); + +interfaces +{ + Interface1 + { + mesh Fluid3DRight-Mesh; + patches (inlet); + + readData + ( + Velocity + ); + + writeData + ( + Pressure + ); + }; +}; \ No newline at end of file diff --git a/partitioned-pipe-multiscale/fluid3d-right/system/probes b/partitioned-pipe-multiscale/fluid3d-right/system/probes new file mode 100644 index 000000000..3eb4343d8 --- /dev/null +++ b/partitioned-pipe-multiscale/fluid3d-right/system/probes @@ -0,0 +1,11 @@ +#includeEtc "caseDicts/postProcessing/probes/probes.cfg" + +fields (p U); +probeLocations +( + (0 0 40) + (0 0 20) + +); + +// ************************************************************************* // diff --git a/partitioned-pipe-multiscale/images/tutorials-partitioned-pipe-multiscale-1d3d-config.png b/partitioned-pipe-multiscale/images/tutorials-partitioned-pipe-multiscale-1d3d-config.png new file mode 100644 index 000000000..c27a2ae34 Binary files /dev/null and b/partitioned-pipe-multiscale/images/tutorials-partitioned-pipe-multiscale-1d3d-config.png differ diff --git a/partitioned-pipe-multiscale/images/tutorials-partitioned-pipe-multiscale-3d1d-config.png b/partitioned-pipe-multiscale/images/tutorials-partitioned-pipe-multiscale-3d1d-config.png new file mode 100644 index 000000000..c27ecc05e Binary files /dev/null and b/partitioned-pipe-multiscale/images/tutorials-partitioned-pipe-multiscale-3d1d-config.png differ diff --git a/partitioned-pipe-multiscale/images/tutorials-partitioned-pipe-multiscale-3d1d-pressure-distribution.png b/partitioned-pipe-multiscale/images/tutorials-partitioned-pipe-multiscale-3d1d-pressure-distribution.png new file mode 100644 index 000000000..386f06cee Binary files /dev/null and b/partitioned-pipe-multiscale/images/tutorials-partitioned-pipe-multiscale-3d1d-pressure-distribution.png differ diff --git a/partitioned-pipe-multiscale/images/tutorials-partitioned-pipe-multiscale-3d1d-velocityProfileInterface3d.png b/partitioned-pipe-multiscale/images/tutorials-partitioned-pipe-multiscale-3d1d-velocityProfileInterface3d.png new file mode 100644 index 000000000..ed9553418 Binary files /dev/null and b/partitioned-pipe-multiscale/images/tutorials-partitioned-pipe-multiscale-3d1d-velocityProfileInterface3d.png differ diff --git a/partitioned-pipe-multiscale/precice-config-1d-3d.xml b/partitioned-pipe-multiscale/precice-config-1d-3d.xml new file mode 100644 index 000000000..3a59e1971 --- /dev/null +++ b/partitioned-pipe-multiscale/precice-config-1d-3d.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/partitioned-pipe-multiscale/precice-config-3d-1d.xml b/partitioned-pipe-multiscale/precice-config-3d-1d.xml new file mode 100644 index 000000000..c5311d7de --- /dev/null +++ b/partitioned-pipe-multiscale/precice-config-3d-1d.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/partitioned-pipe-multiscale/precice-config.xml b/partitioned-pipe-multiscale/precice-config.xml new file mode 120000 index 000000000..77b789565 --- /dev/null +++ b/partitioned-pipe-multiscale/precice-config.xml @@ -0,0 +1 @@ +precice-config-1d-3d.xml \ No newline at end of file diff --git a/partitioned-pipe-multiscale/set-case.sh b/partitioned-pipe-multiscale/set-case.sh new file mode 100755 index 000000000..1472a28de --- /dev/null +++ b/partitioned-pipe-multiscale/set-case.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -e -u + +if [ "$1" = "1d3d" ]; then + ln -sf precice-config-1d-3d.xml precice-config.xml + echo "Switched to 1D–3D configuration." +elif [ "$1" = "3d1d" ]; then + ln -sf precice-config-3d-1d.xml precice-config.xml + echo "Switched to 3D–1D configuration." +else + echo "Usage: ./set-case.sh [1d3d|3d1d]" +fi + diff --git a/partitioned-pipe-multiscale/solver-fluid1d/Fluid1D.py b/partitioned-pipe-multiscale/solver-fluid1d/Fluid1D.py new file mode 100644 index 000000000..9afa9aa48 --- /dev/null +++ b/partitioned-pipe-multiscale/solver-fluid1d/Fluid1D.py @@ -0,0 +1,163 @@ +import numpy as np +import matplotlib.pyplot as plt +import treelog +import precice +from nutils import mesh, function, solver, cli + + +def main(nelems=400, dt=0.01, theta=0.5, u_in=0.1, p_out=0, rho=1, nu=10, R=5, side="Left"): + """ + 1D fluid solver coupled via preCICE. + """ + + # --- preCICE setup --- + + if side == "Left": + participant_name = "Fluid1DLeft" + mesh_name = "Fluid1DLeft-Mesh" + read_data = "Pressure" + write_data = "Velocity" + elif side == "Right": + participant_name = "Fluid1DRight" + mesh_name = "Fluid1DRight-Mesh" + read_data = "Velocity" + write_data = "Pressure" + else: + raise Exception('invalid side {!r}'.format(side)) + 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 = [[0.0, 0.0, 20.0]] # Define a single coupling point + vertex_ids = participant.set_mesh_vertices(mesh_name, positions) + + participant.initialize() + precice_dt = participant.get_max_time_step_size() + + # --- Nutils domain setup --- + + domain, geom = mesh.rectilinear([np.linspace(0, 40, nelems + 1)]) + if side == "Left": + domain = domain[:nelems // 2] + elif side == "Right": + domain = domain[nelems // 2:] + else: + raise Exception('invalid side {!r}'.format(side)) + + ns = function.Namespace() + ns.x = geom + ns.dt = dt + ns.θ = theta + ns.ρ = rho + ns.ν = nu + ns.R = R + ns.uin = u_in + ns.pout = p_out + ns.α = '8 ν / (R^2)' # Simple wall friction term + + # Define basis functions: velocity (vector, degree 2), pressure (scalar, degree 1) + ns.ubasis, ns.pbasis = function.chain([ + domain.basis('std', degree=2).vector(domain.ndims), + domain.basis('std', degree=1) + ]) + + # Define trial and previous-step functions + ns.u_i = 'ubasis_ni ?lhs_n' + ns.u0_i = 'ubasis_ni ?lhs0_n' + ns.p = 'pbasis_n ?lhs_n' + ns.p0 = 'pbasis_n ?lhs0_n' + ns.utheta_i = 'θ u_i + (1 - θ) u0_i' + + # --- Weak form residuals --- + + # Momentum conservation + res = domain.integral( + 'ubasis_ni (ρ (u_i - u0_i) / dt + ρ utheta_i utheta_j,j + α utheta_i + p_,i) d:x' @ ns, + degree=4 + ) + + # Mass conservation + res += domain.integral('pbasis_n utheta_i,i d:x' @ ns, degree=4) + + if side == "Left": + bc0 = domain.boundary['left'].integral('(u_0 - uin)^2 d:x' @ ns, degree=4) + elif side == "Right": + bc0 = domain.boundary['right'].integral('(p - pout)^2 d:x' @ ns, degree=4) + + # Initial condition + lhs0 = np.zeros(res.shape) + + # Time-stepping + t = 0.0 + bezier = domain.sample('bezier', 2) + + f = open("watchpoint.txt", "w") + + # --- Time loop with preCICE coupling --- + while participant.is_coupling_ongoing(): + + # Read data from other solver via preCICE + data_read = participant.read_data(mesh_name, read_data, vertex_ids, precice_dt) + + if side == "Left": + ns.pOut = data_read[0] + bc = bc0 + domain.boundary['right'].integral('(p - pOut)^2 d:x' @ ns, degree=4) + elif side == "Right": + ns.uIn = data_read[0][2] + bc = bc0 + domain.boundary['left'].integral('(u_0 - uIn)^2 d:x' @ ns, degree=4) + cons = solver.optimize('lhs', bc, droptol=1e-14) + + # Write checkpoint if required by preCICE + if participant.requires_writing_checkpoint(): + lhscheckpoint = lhs0 + + # Solve nonlinear system (Newton's method) + with treelog.context('stokes'): + lhs = solver.newton( + 'lhs', + residual=res, + constrain=cons, + arguments=dict(lhs0=lhs0), + lhs0=lhs0 + ).solve(tol=1e-8) + + # Evaluate fields for visualization/debugging + x, p, u = bezier.eval(['x_i', 'p', 'u_i'] @ ns, lhs=lhs) + + # Send data to 3D solver + if side == "Left": + value_written = [[0, 0, u[-1][0]]] + elif side == "Right": + value_written = [[p[0]]] + participant.write_data(mesh_name, write_data, vertex_ids, value_written) + + # Advance in pseudo-time + participant.advance(precice_dt) + precice_dt = participant.get_max_time_step_size() + + # Restore checkpoint if needed + if participant.requires_reading_checkpoint(): + lhs0 = lhscheckpoint + else: + # Update old solution + lhs0 = lhs + # Save probe values (time, inlet pressure, inlet velocity, outlet + # pressure, outlet velocity, pressure at the middle, velocity at the + # middle) + mid = len(p) // 2 + x, p, u = bezier.eval(['x_i', 'p', 'u_i'] @ ns, lhs=lhs) + f.write("%e; %e; %e; %e; %e; %e; %e\n" % + (t, p[0], u[0], p[-1], u[-1], p[mid], u[mid])) + + f.flush() + + t += precice_dt # Advance pseudo time + + # --- Finalization --- + participant.finalize() + np.savetxt("final_fields.txt", np.column_stack([x, u, p]), header="x u p") + f.close() + + +if __name__ == '__main__': + cli.run(main)