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 for the 3D-1D simulation:
+
+
+
+## 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 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.
+
+
+
+**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)