diff --git a/water-hammer/README.md b/water-hammer/README.md
new file mode 100644
index 000000000..2d9d1a940
--- /dev/null
+++ b/water-hammer/README.md
@@ -0,0 +1,166 @@
+---
+title: Partitioned Water Hammer
+keywords: OpenFOAM, Nutils, preCICE, multiscale, fluid, transient
+summary: The Partitioned Water Hammer tutorial simulates unsteady pressure wave propagation in pipe systems using different 1D and 3D configurations coupled via preCICE.
+---
+
+## Setup
+
+We solve a **partitioned water hammer 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**.
+
+Case setup inspired by [1].
+This tutorial extends the study conducted in [2], which implemented the water hammer benchmark using a 1D-3D coupling with preCICE.
+In that study, the cross-section of the pipe was squared. It has been changed to a circular cross-section in the present tutorial.
+`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 = 1000 m` and diameter `D = 2 m`.
+We partition the domain at `y_c = 500 m`, where the coupling interface is located.
+The **1D domain** solves the unsteady compressible 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 pressure to the 3D solver, which responds with velocity.
+- **3D → 1D**: The 3D solver provides the pressure, and the 1D solver returns the velocity.
+
+The initial inlet pressure is set to `p_in = 98100 Pa`.
+The opening valve at the outlet is modeled through a prescribed outlet velocity, which increases linearly from `0` to `1 m/s` over the first `5 s` and remains constant afterwards.
+This sudden valve opening generates pressure disturbances that propagate through the pipe, resulting in the characteristic **pressure wave oscillations** known as the *water hammer* phenomenon.
+
+## 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 (image generated using the [precice-config-visualizer](https://precice.org/tooling-config-visualization.html))
+
+
+
+## Available solvers
+
+* OpenFOAM (sonicLiquidFoam). A compressible OpenFOAM solver. For more information, have a look at the [OpenFOAM adapter documentation](https://precice.org/adapter-openfoam-overview.html)
+* Nutils v9. 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 (to observe the pressure wave propagation)
+- `U` – velocity (to visualize the flow development near the coupling interface)
+
+In ParaView, you can also create line plots along the pipe centerline or use temporal filters to observe wave propagation over time.
+
+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 999 0)
+ (0 500 0)
+);
+// For the left 3D domain use instead:
+// probeLocations ((0 0 0) (0 500 0));
+```
+
+**Output location:**
+- `fluid3d-right/postProcessing/probes/0/p`
+- `fluid3d-right/postProcessing/probes/0/U`
+
+Each file contains a header (commented with `#`) and time-series columns for each probe.
+
+### 1D domain (Nutils)
+
+The 1D participant writes results to a file named `watchpoint.txt`, containing the temporal evolution of key quantities at selected spatial locations.
+
+The structure of each line in the file is:
+
+```
+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.
+
+### Example visualization
+
+
+
+Pressure evolution at the outlet of the 3D domain during the 1D–3D water hammer simulation.
+
+## References
+
+[1] C. Wang, H. Nilsson, J. Yang, *et al.*
+**1D–3D coupling for hydraulic system transient simulations.**
+*Computer Physics Communications*, 210:1–9, 2017.
+[https://doi.org/10.1016/j.cpc.2016.09.007](https://doi.org/10.1016/j.cpc.2016.09.007)
+
+[2] G. Chourdakis, B. Uekermann, G. van Zwieten, and H. van Brummelen.
+**Coupling OpenFOAM to different solvers, physics, models, and dimensions using preCICE.**
+[https://mediatum.ub.tum.de/doc/1515271/1515271.pdf](https://mediatum.ub.tum.de/doc/1515271/1515271.pdf)
diff --git a/water-hammer/clean-tutorial.sh b/water-hammer/clean-tutorial.sh
new file mode 100755
index 000000000..5dbb6c639
--- /dev/null
+++ b/water-hammer/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/water-hammer/fluid1d-left/Fluid1D.py b/water-hammer/fluid1d-left/Fluid1D.py
new file mode 100644
index 000000000..0bcfc722e
--- /dev/null
+++ b/water-hammer/fluid1d-left/Fluid1D.py
@@ -0,0 +1,147 @@
+import numpy as np
+import matplotlib.pyplot as plt
+import treelog
+from nutils import mesh, function, solver, cli
+import precice
+
+
+def main(nelems=200, dt=.005, refdensity=1e3, refpressure=101325.0, psi=1e-6, viscosity=1e-3, theta=0.5):
+
+ # --- preCICE initialization ---
+
+ participant_name = "Fluid1DLeft"
+ 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)
+ mesh_name = "Fluid1DLeft-Mesh"
+ velocity_name = "Velocity"
+ pressure_name = "Pressure"
+ positions = [[0.0, 500.0, 0.0]] # Define a single coupling point (3D format required by preCICE)
+ 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, 500, nelems + 1)]) # 1D domain from 0 to 500 with nelems elements
+
+ ns = function.Namespace()
+ ns.x = geom
+ ns.dt = dt
+ ns.θ = theta
+ ns.ρref = refdensity
+ ns.pref = refpressure
+ ns.pin = 98100 # Inlet pressure (Pa)
+ ns.μ = viscosity
+ ns.ψ = psi
+
+ # Define basis functions: velocity (vector, degree 2), density (scalar, degree 1)
+ ns.ubasis, ns.ρbasis = 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.ρ = 'ρref + ρbasis_n ?lhs_n'
+ ns.ρ0 = 'ρref + ρbasis_n ?lhs0_n'
+ ns.p = 'pref + (ρ - ρref) / ψ'
+ ns.utheta_i = 'θ u_i + (1 - θ) u0_i'
+ ns.ρtheta = 'θ ρ + (1 - θ) ρ0'
+
+ # Cauchy stress tensor: viscous + pressure
+ ns.σ_ij = 'μ (utheta_i,j + utheta_j,i) - p δ_ij'
+
+ # --- Weak form residuals ---
+
+ # Mass conservation
+ res = domain.integral(
+ 'ρbasis_n ((ρ - ρ0) / dt + (ρtheta utheta_k)_,k) d:x' @ ns,
+ degree=4
+ )
+
+ # Momentum conservation: unsteady + convective + diffusive terms
+ res += domain.integral(
+ '(ubasis_ni (((ρ u_i - ρ0 u0_i) / dt) + (ρtheta utheta_i utheta_j)_,j) + ubasis_ni,j σ_ij) d:x' @ ns,
+ degree=4
+ )
+
+ # Weakly enforced inlet pressure boundary condition
+ res += domain.boundary['left'].integral(
+ 'pin ubasis_ni n_i d:x' @ ns,
+ degree=4
+ )
+
+ # Initial condition
+ lhs0 = np.zeros(res.shape)
+
+ # Time-stepping
+ t = 0.0
+ timestep = 0
+ bezier = domain.sample('bezier', 2)
+
+ f = open("watchpoint.txt", "w")
+
+ # --- Time loop with preCICE coupling ---
+ while participant.is_coupling_ongoing():
+
+ # Read velocity data from other solver via preCICE
+ u_read = participant.read_data(mesh_name, velocity_name, vertex_ids, precice_dt)
+
+ # Constrain outlet velocity to match coupled value
+ stringintegral = f'(u_0 - ({u_read[0][1]}))^2 d:x'
+ sqr = domain.boundary['right'].integral(stringintegral @ ns, degree=4)
+ cons = solver.optimize('lhs', sqr, 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(1e-08)
+
+ # Evaluate fields for visualization/debugging
+ x, p, u, ρ = bezier.eval(['x_i', 'p', 'u_i', 'ρ'] @ ns, arguments=dict(lhs=lhs))
+
+ # Send pressure at the right boundary to the other solver
+ write_press = [[p[-1]]]
+ participant.write_data(mesh_name, pressure_name, vertex_ids, write_press)
+
+ # 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
+ timestep += timestep
+
+ # Save probe values (time, inlet pressure, inlet velocity, outlet
+ # pressure, outlet velocity, pressure at the middle, velocity at the
+ # middle)
+ 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[199], u[199]))
+ f.flush()
+
+ t += precice_dt # advance pseudo-time
+
+ # Finalize preCICE
+ participant.finalize()
+ f.close()
+
+
+if __name__ == '__main__':
+ cli.run(main)
diff --git a/water-hammer/fluid1d-left/clean.sh b/water-hammer/fluid1d-left/clean.sh
new file mode 100755
index 000000000..a4520b973
--- /dev/null
+++ b/water-hammer/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 .
diff --git a/water-hammer/fluid1d-left/requirements.txt b/water-hammer/fluid1d-left/requirements.txt
new file mode 100644
index 000000000..f93cc1b3b
--- /dev/null
+++ b/water-hammer/fluid1d-left/requirements.txt
@@ -0,0 +1,6 @@
+setuptools # required by nutils
+nutils==9
+numpy >1, <2
+pyprecice~=3.0
+matplotlib
+treelog
\ No newline at end of file
diff --git a/water-hammer/fluid1d-left/run.sh b/water-hammer/fluid1d-left/run.sh
new file mode 100755
index 000000000..e1983820e
--- /dev/null
+++ b/water-hammer/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 Fluid1D.py
+
+close_log
diff --git a/water-hammer/fluid1d-right/Fluid1D.py b/water-hammer/fluid1d-right/Fluid1D.py
new file mode 100644
index 000000000..396c190ac
--- /dev/null
+++ b/water-hammer/fluid1d-right/Fluid1D.py
@@ -0,0 +1,155 @@
+import numpy as np
+import matplotlib.pyplot as plt
+import treelog
+from nutils import mesh, function, solver, cli
+import precice
+
+
+def main(nelems=200, dt=.005, refdensity=1e3, refpressure=101325.0, psi=1e-6, viscosity=1e-3, theta=0.5):
+
+ # --- preCICE initialization ---
+
+ participant_name = "Fluid1DRight"
+ 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)
+ mesh_name = "Fluid1DRight-Mesh"
+ velocity_name = "Velocity"
+ pressure_name = "Pressure"
+ positions = [[0, 500.0, 0]]
+ vertex_ids = participant.set_mesh_vertices(mesh_name, positions)
+
+ participant.initialize()
+ precice_dt = participant.get_max_time_step_size()
+
+ # --- Nutils domain setup ---
+
+ # 1D domain from 500 to 1000 with nelems elements
+ domain, geom = mesh.rectilinear([np.linspace(500, 1000, nelems + 1)])
+
+ ns = function.Namespace()
+ ns.x = geom
+ ns.dt = dt
+ ns.θ = theta
+ ns.ρref = refdensity
+ ns.pref = refpressure
+ ns.μ = viscosity
+ ns.ψ = psi
+
+ # Define basis functions: velocity (vector, degree 2), density (scalar, degree 1)
+ ns.ubasis, ns.ρbasis = 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.ρ = 'ρref + ρbasis_n ?lhs_n'
+ ns.ρ0 = 'ρref + ρbasis_n ?lhs0_n'
+ ns.p = 'pref + (ρ - ρref) / ψ'
+ ns.utheta_i = 'θ u_i + (1 - θ) u0_i'
+ ns.ρtheta = 'θ ρ + (1 - θ) ρ0'
+
+ # Cauchy stress tensor: viscous + pressure
+ ns.σ_ij = 'μ (utheta_i,j + utheta_j,i) - p δ_ij'
+
+ # Estimate mesh size and stabilization parameter (optional)
+ ns.h = 1 / nelems
+ ns.k = 'ρ h / μ'
+
+ # --- Weak form residuals ---
+
+ # Mass conservation
+ res = domain.integral(
+ 'ρbasis_n ((ρ - ρ0) / dt + (ρtheta utheta_k)_,k) d:x' @ ns,
+ degree=4
+ )
+
+ # Momentum conservation: unsteady + convective + diffusive terms
+ res += domain.integral(
+ '(ubasis_ni (((ρ u_i - ρ0 u0_i) / dt) + (ρtheta utheta_i utheta_j)_,j) + ubasis_ni,j σ_ij) d:x' @ ns,
+ degree=4
+ )
+
+ # Outlet velocity boundary condition
+ sqr = domain.boundary['right'].integral('(u_0 - 1)^2' @ ns, degree=4)
+ cons0 = solver.optimize('lhs', sqr, droptol=1e-14)
+
+ res0 = res # Save base residual (without inlet BC)
+
+ # Initial condition
+ lhs0 = np.zeros(res.shape)
+
+ # Time-stepping
+ t = 0.0
+ timestep = 0
+ bezier = domain.sample('bezier', 2)
+
+ f = open("watchpoint.txt", "w")
+
+ # --- Time loop with preCICE coupling ---
+ while participant.is_coupling_ongoing():
+
+ # Read pressure data from other solver via preCICE
+ p_read = participant.read_data(mesh_name, pressure_name, vertex_ids, precice_dt)
+
+ # Update inlet pressure
+ ns.pin = p_read[0]
+
+ # Constrain inlet pressure
+ res = res0 + domain.boundary['left'].integral('pin ubasis_ni n_i d:x' @ ns, degree=4)
+
+ # Time-dependent velocity constraint weight (ramps up from 0 to 1 over 5 seconds)
+ cons = min(0.2 * t, 1) * cons0
+
+ # 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(1e-08)
+
+ # Evaluate fields for visualization/debugging
+ x, p, ρ, u = bezier.eval(['x_i', 'p', 'ρ', 'u_i'] @ ns, arguments=dict(lhs=lhs))
+
+ # Send velocity at the left boundary to the other solver
+ write_vel = [[0, u[0][0], 0]]
+ participant.write_data(mesh_name, velocity_name, vertex_ids, write_vel)
+
+ # 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
+ timestep += timestep
+
+ # Save probe values (time, inlet pressure, inlet velocity, outlet
+ # pressure, outlet velocity, pressure at the middle, velocity at the
+ # middle)
+ 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[199], u[199]))
+ f.flush()
+
+ t += precice_dt # Advance time
+
+ # Finalize preCICE
+ participant.finalize()
+ f.close()
+
+
+if __name__ == '__main__':
+ cli.run(main)
diff --git a/water-hammer/fluid1d-right/clean.sh b/water-hammer/fluid1d-right/clean.sh
new file mode 100755
index 000000000..a4520b973
--- /dev/null
+++ b/water-hammer/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 .
diff --git a/water-hammer/fluid1d-right/requirements.txt b/water-hammer/fluid1d-right/requirements.txt
new file mode 100644
index 000000000..f93cc1b3b
--- /dev/null
+++ b/water-hammer/fluid1d-right/requirements.txt
@@ -0,0 +1,6 @@
+setuptools # required by nutils
+nutils==9
+numpy >1, <2
+pyprecice~=3.0
+matplotlib
+treelog
\ No newline at end of file
diff --git a/water-hammer/fluid1d-right/run.sh b/water-hammer/fluid1d-right/run.sh
new file mode 100755
index 000000000..e1983820e
--- /dev/null
+++ b/water-hammer/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 Fluid1D.py
+
+close_log
diff --git a/water-hammer/fluid3d-left/0/U b/water-hammer/fluid3d-left/0/U
new file mode 100644
index 000000000..265762981
--- /dev/null
+++ b/water-hammer/fluid3d-left/0/U
@@ -0,0 +1,36 @@
+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 zeroGradient;
+ }
+ outlet
+ {
+ // outOfBounds clamp;
+ // Time-varying outlet velocity
+ type fixedValue;
+ value uniform (0 0 0);
+ }
+ fixedWalls
+ {
+ type fixedValue; // no-slip walls
+ value uniform (0 0 0);
+ }
+}
+
+
+// ************************************************************************* //
diff --git a/water-hammer/fluid3d-left/0/p b/water-hammer/fluid3d-left/0/p
new file mode 100644
index 000000000..0e3a5f099
--- /dev/null
+++ b/water-hammer/fluid3d-left/0/p
@@ -0,0 +1,35 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class volScalarField;
+ object p;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+dimensions [1 -1 -2 0 0 0 0];
+
+internalField uniform 98100;
+
+boundaryField
+{
+ inlet
+ {
+ type fixedValue;
+ value uniform 98100;
+ }
+
+ outlet
+ {
+ type zeroGradient;
+ }
+
+
+ fixedWalls
+ {
+ type zeroGradient; // no normal pressure gradient at walls
+ }
+
+}
+
+// ************************************************************************* //
diff --git a/water-hammer/fluid3d-left/clean.sh b/water-hammer/fluid3d-left/clean.sh
new file mode 100755
index 000000000..b64fc5101
--- /dev/null
+++ b/water-hammer/fluid3d-left/clean.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env sh
+set -e -u
+
+. ../../tools/cleaning-tools.sh
+
+clean_openfoam .
diff --git a/water-hammer/fluid3d-left/constant/thermodynamicProperties b/water-hammer/fluid3d-left/constant/thermodynamicProperties
new file mode 100644
index 000000000..4799912dc
--- /dev/null
+++ b/water-hammer/fluid3d-left/constant/thermodynamicProperties
@@ -0,0 +1,18 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class dictionary;
+ location "constant";
+ object thermodynamicProperties;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+rho0 rho0 [1 -3 0 0 0 0 0] 1000;
+
+p0 p0 [1 -1 -2 0 0 0 0] 101325;
+
+psi psi [0 -2 2 0 0 0 0] 1e-6;
+
+
+// ************************************************************************* //
diff --git a/water-hammer/fluid3d-left/constant/transportProperties b/water-hammer/fluid3d-left/constant/transportProperties
new file mode 100644
index 000000000..4872f8e62
--- /dev/null
+++ b/water-hammer/fluid3d-left/constant/transportProperties
@@ -0,0 +1,13 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class dictionary;
+ location "constant";
+ object transportProperties;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+mu mu [1 -1 -1 0 0 0 0] 1e-3;
+
+// ************************************************************************* //
diff --git a/water-hammer/fluid3d-left/constant/turbulenceProperties b/water-hammer/fluid3d-left/constant/turbulenceProperties
new file mode 100644
index 000000000..a74ee048f
--- /dev/null
+++ b/water-hammer/fluid3d-left/constant/turbulenceProperties
@@ -0,0 +1,13 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class dictionary;
+ location "constant";
+ object turbulenceProperties;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+simulationType laminar;
+
+// ************************************************************************* //
diff --git a/water-hammer/fluid3d-left/run.sh b/water-hammer/fluid3d-left/run.sh
new file mode 100755
index 000000000..8f55fbfa5
--- /dev/null
+++ b/water-hammer/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
diff --git a/water-hammer/fluid3d-left/system/blockMeshDict b/water-hammer/fluid3d-left/system/blockMeshDict
new file mode 100644
index 000000000..27c4389bc
--- /dev/null
+++ b/water-hammer/fluid3d-left/system/blockMeshDict
@@ -0,0 +1,100 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class dictionary;
+ location "constant/polyMesh";
+ object blockMeshDict;
+}
+
+scale 1;
+
+ymin 0;
+ymax 500;
+
+xcells 10; // per block (5 blocks)
+ycells 10; // per block (5 blocks)
+zcells 100;
+
+vertices
+(
+ (-0.707107 $ymin -0.707107) // 0
+ ( 0.707107 $ymin -0.707107) // 1
+ ( 0.707107 $ymin 0.707107) // 2
+ (-0.707107 $ymin 0.707107) // 3
+ (-0.282843 $ymin -0.282843) // 4
+ ( 0.282843 $ymin -0.282843) // 5
+ ( 0.282843 $ymin 0.282843) // 6
+ (-0.282843 $ymin 0.282843) // 7
+
+ (-0.707107 $ymax -0.707107) // 8
+ ( 0.707107 $ymax -0.707107) // 9
+ ( 0.707107 $ymax 0.707107) // 10
+ (-0.707107 $ymax 0.707107) // 11
+ (-0.282843 $ymax -0.282843) // 12
+ ( 0.282843 $ymax -0.282843) // 13
+ ( 0.282843 $ymax 0.282843) // 14
+ (-0.282843 $ymax 0.282843) // 15
+);
+
+blocks
+(
+ // right-handed along +y (unchanged)
+ hex (8 9 13 12 0 1 5 4) pipe ($xcells $ycells $zcells) edgeGrading (1 1 1 1 1 1 1 1 1 1 1 1)
+ hex (9 10 14 13 1 2 6 5) pipe ($xcells $ycells $zcells) edgeGrading (1 1 1 1 1 1 1 1 1 1 1 1)
+ hex (10 11 15 14 2 3 7 6) pipe ($xcells $ycells $zcells) edgeGrading (1 1 1 1 1 1 1 1 1 1 1 1)
+ hex (11 8 12 15 3 0 4 7) pipe ($xcells $ycells $zcells) edgeGrading (1 1 1 1 1 1 1 1 1 1 1 1)
+ hex (12 13 14 15 4 5 6 7) pipe ($xcells $ycells $zcells) edgeGrading (1 1 1 1 1 1 1 1 1 1 1 1)
+);
+
+edges
+(
+ // arcs on y = ymin plane (outer radius 1, inner 0.3)
+ arc 0 1 ( 0 $ymin -1)
+ arc 1 2 ( 1 $ymin 0)
+ arc 2 3 ( 0 $ymin 1)
+ arc 3 0 (-1 $ymin 0)
+ arc 4 5 ( 0 $ymin -0.3)
+ arc 5 6 ( 0.3 $ymin 0)
+ arc 6 7 ( 0 $ymin 0.3)
+ arc 7 4 (-0.3 $ymin 0)
+
+ // arcs on y = ymax plane
+ arc 8 9 ( 0 $ymax -1)
+ arc 9 10 ( 1 $ymax 0)
+ arc 10 11 ( 0 $ymax 1)
+ arc 11 8 (-1 $ymax 0)
+ arc 12 13 ( 0 $ymax -0.3)
+ arc 13 14 ( 0.3 $ymax 0)
+ arc 14 15 ( 0 $ymax 0.3)
+ arc 15 12 (-0.3 $ymax 0)
+);
+
+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/water-hammer/fluid3d-left/system/controlDict b/water-hammer/fluid3d-left/system/controlDict
new file mode 100644
index 000000000..8bcfbcc29
--- /dev/null
+++ b/water-hammer/fluid3d-left/system/controlDict
@@ -0,0 +1,48 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class dictionary;
+ location "system";
+ object controlDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+application sonicLiquidFoam;
+
+startFrom startTime;
+
+startTime 0;
+
+stopAt endTime;
+
+endTime 20;
+
+deltaT 0.005;
+
+writeControl adjustableRunTime;
+
+writeInterval 0.01;
+
+purgeWrite 0;
+
+writeFormat ascii;
+
+writePrecision 10;
+
+writeCompression off;
+
+timeFormat general;
+
+timePrecision 6;
+
+functions
+{
+ preCICE_Adapter
+ {
+ type preciceAdapterFunctionObject;
+ libs ("libpreciceAdapterFunctionObject.so");
+ }
+}
+
+functions {#includeFunc probes}
\ No newline at end of file
diff --git a/water-hammer/fluid3d-left/system/decomposeParDict b/water-hammer/fluid3d-left/system/decomposeParDict
new file mode 100644
index 000000000..1263d0d73
--- /dev/null
+++ b/water-hammer/fluid3d-left/system/decomposeParDict
@@ -0,0 +1,16 @@
+FoamFile {
+ version 2.0;
+ class dictionary;
+ object decomposeParDict;
+ format ascii;
+}
+
+numberOfSubdomains 2;
+
+method simple;
+
+simpleCoeffs
+{
+ n (1 1 2);
+ delta 0.001;
+}
diff --git a/water-hammer/fluid3d-left/system/fvSchemes b/water-hammer/fluid3d-left/system/fvSchemes
new file mode 100644
index 000000000..52295c84e
--- /dev/null
+++ b/water-hammer/fluid3d-left/system/fvSchemes
@@ -0,0 +1,45 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class dictionary;
+ location "system";
+ object fvSchemes;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+ddtSchemes
+{
+ //default Euler;
+ default CrankNicolson 0.45;
+}
+
+gradSchemes
+{
+ default Gauss linear;
+}
+
+divSchemes
+{
+ default none;
+ div(phi,U) Gauss limitedLinearV 1;
+ div(phid,p) Gauss limitedLinear 1;
+}
+
+laplacianSchemes
+{
+ default Gauss linear corrected;
+}
+
+interpolationSchemes
+{
+ default linear;
+}
+
+snGradSchemes
+{
+ default corrected;
+}
+
+
+// ************************************************************************* //
diff --git a/water-hammer/fluid3d-left/system/fvSolution b/water-hammer/fluid3d-left/system/fvSolution
new file mode 100644
index 000000000..db7cdcd6b
--- /dev/null
+++ b/water-hammer/fluid3d-left/system/fvSolution
@@ -0,0 +1,48 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class dictionary;
+ location "system";
+ object fvSolution;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+solvers
+{
+ "p.*"
+ {
+ solver PBiCG;
+ preconditioner DILU
+ //smoother symGaussSeidel;
+ tolerance 1e-06;
+ relTol 0.01;
+ }
+
+ "U.*"
+ {
+ $p;
+ solver PBiCG;
+ preconditioner DILU;
+ tolerance 1e-05;
+ relTol 0.1;
+ }
+
+ "rho.*"
+ {
+ solver PCG;
+ preconditioner DIC;
+ tolerance 1e-05;
+ relTol 0.1;
+ }
+}
+
+PIMPLE
+{
+ nOuterCorrectors 4;
+ nCorrectors 2;
+ nNonOrthogonalCorrectors 0;
+}
+
+
+// ************************************************************************* //
diff --git a/water-hammer/fluid3d-left/system/preciceDict b/water-hammer/fluid3d-left/system/preciceDict
new file mode 100644
index 000000000..c083f361c
--- /dev/null
+++ b/water-hammer/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
+ (
+ Velocity
+ );
+
+ writeData
+ (
+ Pressure
+ );
+ };
+};
\ No newline at end of file
diff --git a/water-hammer/fluid3d-left/system/probes b/water-hammer/fluid3d-left/system/probes
new file mode 100644
index 000000000..f4dda6c57
--- /dev/null
+++ b/water-hammer/fluid3d-left/system/probes
@@ -0,0 +1,10 @@
+#includeEtc "caseDicts/postProcessing/probes/probes.cfg"
+
+fields (p U);
+probeLocations
+(
+ (0 0 0)
+ (0 500 0)
+);
+
+// ************************************************************************* //
diff --git a/water-hammer/fluid3d-left/system/probesDict b/water-hammer/fluid3d-left/system/probesDict
new file mode 100644
index 000000000..baf4b5cb7
--- /dev/null
+++ b/water-hammer/fluid3d-left/system/probesDict
@@ -0,0 +1,24 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class dictionary;
+ object probesDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// Fields to be probed. runTime modifiable!
+fields
+(
+ p U
+);
+
+// Locations to be probed. runTime modifiable!
+probeLocations
+(
+ (0 0 0)
+ (0 499 0)
+);
+
+
+// ************************************************************************* //
diff --git a/water-hammer/fluid3d-right/0/U b/water-hammer/fluid3d-right/0/U
new file mode 100644
index 000000000..db0c4d61d
--- /dev/null
+++ b/water-hammer/fluid3d-right/0/U
@@ -0,0 +1,41 @@
+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 zeroGradient;
+ }
+ outlet
+ {
+ outOfBounds clamp;
+ // Time-varying outlet velocity
+ type uniformFixedValue;
+ uniformValue table
+ (
+ ( 0 ( 0 0 0 ) )
+ ( 5 ( 0 1 0 ) )
+ ( 100 ( 0 1 0 ) )
+ );
+ }
+ fixedWalls
+ {
+ type fixedValue; // no-slip walls
+ value uniform (0 0 0);
+ }
+}
+
+
+// ************************************************************************* //
diff --git a/water-hammer/fluid3d-right/0/p b/water-hammer/fluid3d-right/0/p
new file mode 100644
index 000000000..0e3a5f099
--- /dev/null
+++ b/water-hammer/fluid3d-right/0/p
@@ -0,0 +1,35 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class volScalarField;
+ object p;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+dimensions [1 -1 -2 0 0 0 0];
+
+internalField uniform 98100;
+
+boundaryField
+{
+ inlet
+ {
+ type fixedValue;
+ value uniform 98100;
+ }
+
+ outlet
+ {
+ type zeroGradient;
+ }
+
+
+ fixedWalls
+ {
+ type zeroGradient; // no normal pressure gradient at walls
+ }
+
+}
+
+// ************************************************************************* //
diff --git a/water-hammer/fluid3d-right/clean.sh b/water-hammer/fluid3d-right/clean.sh
new file mode 100755
index 000000000..b64fc5101
--- /dev/null
+++ b/water-hammer/fluid3d-right/clean.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env sh
+set -e -u
+
+. ../../tools/cleaning-tools.sh
+
+clean_openfoam .
diff --git a/water-hammer/fluid3d-right/constant/thermodynamicProperties b/water-hammer/fluid3d-right/constant/thermodynamicProperties
new file mode 100644
index 000000000..4799912dc
--- /dev/null
+++ b/water-hammer/fluid3d-right/constant/thermodynamicProperties
@@ -0,0 +1,18 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class dictionary;
+ location "constant";
+ object thermodynamicProperties;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+rho0 rho0 [1 -3 0 0 0 0 0] 1000;
+
+p0 p0 [1 -1 -2 0 0 0 0] 101325;
+
+psi psi [0 -2 2 0 0 0 0] 1e-6;
+
+
+// ************************************************************************* //
diff --git a/water-hammer/fluid3d-right/constant/transportProperties b/water-hammer/fluid3d-right/constant/transportProperties
new file mode 100644
index 000000000..4872f8e62
--- /dev/null
+++ b/water-hammer/fluid3d-right/constant/transportProperties
@@ -0,0 +1,13 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class dictionary;
+ location "constant";
+ object transportProperties;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+mu mu [1 -1 -1 0 0 0 0] 1e-3;
+
+// ************************************************************************* //
diff --git a/water-hammer/fluid3d-right/constant/turbulenceProperties b/water-hammer/fluid3d-right/constant/turbulenceProperties
new file mode 100644
index 000000000..a74ee048f
--- /dev/null
+++ b/water-hammer/fluid3d-right/constant/turbulenceProperties
@@ -0,0 +1,13 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class dictionary;
+ location "constant";
+ object turbulenceProperties;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+simulationType laminar;
+
+// ************************************************************************* //
diff --git a/water-hammer/fluid3d-right/run.sh b/water-hammer/fluid3d-right/run.sh
new file mode 100755
index 000000000..8f55fbfa5
--- /dev/null
+++ b/water-hammer/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
diff --git a/water-hammer/fluid3d-right/system/blockMeshDict b/water-hammer/fluid3d-right/system/blockMeshDict
new file mode 100644
index 000000000..38e63ee4c
--- /dev/null
+++ b/water-hammer/fluid3d-right/system/blockMeshDict
@@ -0,0 +1,100 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class dictionary;
+ location "constant/polyMesh";
+ object blockMeshDict;
+}
+
+scale 1;
+
+ymin 500;
+ymax 1000;
+
+xcells 10; // per block (5 blocks)
+ycells 10; // per block (5 blocks)
+zcells 100;
+
+vertices
+(
+ (-0.707107 $ymin -0.707107) // 0
+ ( 0.707107 $ymin -0.707107) // 1
+ ( 0.707107 $ymin 0.707107) // 2
+ (-0.707107 $ymin 0.707107) // 3
+ (-0.282843 $ymin -0.282843) // 4
+ ( 0.282843 $ymin -0.282843) // 5
+ ( 0.282843 $ymin 0.282843) // 6
+ (-0.282843 $ymin 0.282843) // 7
+
+ (-0.707107 $ymax -0.707107) // 8
+ ( 0.707107 $ymax -0.707107) // 9
+ ( 0.707107 $ymax 0.707107) // 10
+ (-0.707107 $ymax 0.707107) // 11
+ (-0.282843 $ymax -0.282843) // 12
+ ( 0.282843 $ymax -0.282843) // 13
+ ( 0.282843 $ymax 0.282843) // 14
+ (-0.282843 $ymax 0.282843) // 15
+);
+
+blocks
+(
+ // right-handed along +y (unchanged)
+ hex (8 9 13 12 0 1 5 4) pipe ($xcells $ycells $zcells) edgeGrading (1 1 1 1 1 1 1 1 1 1 1 1)
+ hex (9 10 14 13 1 2 6 5) pipe ($xcells $ycells $zcells) edgeGrading (1 1 1 1 1 1 1 1 1 1 1 1)
+ hex (10 11 15 14 2 3 7 6) pipe ($xcells $ycells $zcells) edgeGrading (1 1 1 1 1 1 1 1 1 1 1 1)
+ hex (11 8 12 15 3 0 4 7) pipe ($xcells $ycells $zcells) edgeGrading (1 1 1 1 1 1 1 1 1 1 1 1)
+ hex (12 13 14 15 4 5 6 7) pipe ($xcells $ycells $zcells) edgeGrading (1 1 1 1 1 1 1 1 1 1 1 1)
+);
+
+edges
+(
+ // arcs on y = ymin plane (outer radius 1, inner 0.3)
+ arc 0 1 ( 0 $ymin -1)
+ arc 1 2 ( 1 $ymin 0)
+ arc 2 3 ( 0 $ymin 1)
+ arc 3 0 (-1 $ymin 0)
+ arc 4 5 ( 0 $ymin -0.3)
+ arc 5 6 ( 0.3 $ymin 0)
+ arc 6 7 ( 0 $ymin 0.3)
+ arc 7 4 (-0.3 $ymin 0)
+
+ // arcs on y = ymax plane
+ arc 8 9 ( 0 $ymax -1)
+ arc 9 10 ( 1 $ymax 0)
+ arc 10 11 ( 0 $ymax 1)
+ arc 11 8 (-1 $ymax 0)
+ arc 12 13 ( 0 $ymax -0.3)
+ arc 13 14 ( 0.3 $ymax 0)
+ arc 14 15 ( 0 $ymax 0.3)
+ arc 15 12 (-0.3 $ymax 0)
+);
+
+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/water-hammer/fluid3d-right/system/controlDict b/water-hammer/fluid3d-right/system/controlDict
new file mode 100644
index 000000000..ebe53e7b0
--- /dev/null
+++ b/water-hammer/fluid3d-right/system/controlDict
@@ -0,0 +1,47 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class dictionary;
+ location "system";
+ object controlDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+application sonicLiquidFoam;
+
+startFrom startTime;
+
+startTime 0;
+
+stopAt endTime;
+
+endTime 8.4;
+
+deltaT 0.005;
+
+writeControl adjustableRunTime;
+
+writeInterval 1;
+
+purgeWrite 0;
+
+writeFormat ascii;
+
+writePrecision 10;
+
+writeCompression off;
+
+timeFormat general;
+
+timePrecision 6;
+
+functions
+{
+ preCICE_Adapter
+ {
+ type preciceAdapterFunctionObject;
+ libs ("libpreciceAdapterFunctionObject.so");
+ }
+ #includeFunc probes
+}
\ No newline at end of file
diff --git a/water-hammer/fluid3d-right/system/decomposeParDict b/water-hammer/fluid3d-right/system/decomposeParDict
new file mode 100644
index 000000000..1263d0d73
--- /dev/null
+++ b/water-hammer/fluid3d-right/system/decomposeParDict
@@ -0,0 +1,16 @@
+FoamFile {
+ version 2.0;
+ class dictionary;
+ object decomposeParDict;
+ format ascii;
+}
+
+numberOfSubdomains 2;
+
+method simple;
+
+simpleCoeffs
+{
+ n (1 1 2);
+ delta 0.001;
+}
diff --git a/water-hammer/fluid3d-right/system/fvSchemes b/water-hammer/fluid3d-right/system/fvSchemes
new file mode 100644
index 000000000..52295c84e
--- /dev/null
+++ b/water-hammer/fluid3d-right/system/fvSchemes
@@ -0,0 +1,45 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class dictionary;
+ location "system";
+ object fvSchemes;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+ddtSchemes
+{
+ //default Euler;
+ default CrankNicolson 0.45;
+}
+
+gradSchemes
+{
+ default Gauss linear;
+}
+
+divSchemes
+{
+ default none;
+ div(phi,U) Gauss limitedLinearV 1;
+ div(phid,p) Gauss limitedLinear 1;
+}
+
+laplacianSchemes
+{
+ default Gauss linear corrected;
+}
+
+interpolationSchemes
+{
+ default linear;
+}
+
+snGradSchemes
+{
+ default corrected;
+}
+
+
+// ************************************************************************* //
diff --git a/water-hammer/fluid3d-right/system/fvSolution b/water-hammer/fluid3d-right/system/fvSolution
new file mode 100644
index 000000000..db7cdcd6b
--- /dev/null
+++ b/water-hammer/fluid3d-right/system/fvSolution
@@ -0,0 +1,48 @@
+FoamFile
+{
+ version 2.0;
+ format ascii;
+ class dictionary;
+ location "system";
+ object fvSolution;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+solvers
+{
+ "p.*"
+ {
+ solver PBiCG;
+ preconditioner DILU
+ //smoother symGaussSeidel;
+ tolerance 1e-06;
+ relTol 0.01;
+ }
+
+ "U.*"
+ {
+ $p;
+ solver PBiCG;
+ preconditioner DILU;
+ tolerance 1e-05;
+ relTol 0.1;
+ }
+
+ "rho.*"
+ {
+ solver PCG;
+ preconditioner DIC;
+ tolerance 1e-05;
+ relTol 0.1;
+ }
+}
+
+PIMPLE
+{
+ nOuterCorrectors 4;
+ nCorrectors 2;
+ nNonOrthogonalCorrectors 0;
+}
+
+
+// ************************************************************************* //
diff --git a/water-hammer/fluid3d-right/system/preciceDict b/water-hammer/fluid3d-right/system/preciceDict
new file mode 100644
index 000000000..0dcef55ae
--- /dev/null
+++ b/water-hammer/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
+ (
+ Pressure
+ );
+
+ writeData
+ (
+ Velocity
+ );
+ };
+};
\ No newline at end of file
diff --git a/water-hammer/fluid3d-right/system/probes b/water-hammer/fluid3d-right/system/probes
new file mode 100644
index 000000000..46647cb0b
--- /dev/null
+++ b/water-hammer/fluid3d-right/system/probes
@@ -0,0 +1,10 @@
+#includeEtc "caseDicts/postProcessing/probes/probes.cfg"
+
+fields (p U);
+probeLocations
+(
+ (0 999 0)
+ (0 500 0)
+);
+
+// ************************************************************************* //
diff --git a/water-hammer/images/tutorials-waterhammer-1d3d-outlet-pressure.png b/water-hammer/images/tutorials-waterhammer-1d3d-outlet-pressure.png
new file mode 100644
index 000000000..518435cbc
Binary files /dev/null and b/water-hammer/images/tutorials-waterhammer-1d3d-outlet-pressure.png differ
diff --git a/water-hammer/images/tutorials-waterhammer-1d3d-precice-config.png b/water-hammer/images/tutorials-waterhammer-1d3d-precice-config.png
new file mode 100644
index 000000000..4ab0d7b8f
Binary files /dev/null and b/water-hammer/images/tutorials-waterhammer-1d3d-precice-config.png differ
diff --git a/water-hammer/images/tutorials-waterhammer-3d1d-precice-config.png b/water-hammer/images/tutorials-waterhammer-3d1d-precice-config.png
new file mode 100644
index 000000000..75ed55b4a
Binary files /dev/null and b/water-hammer/images/tutorials-waterhammer-3d1d-precice-config.png differ
diff --git a/water-hammer/precice-config-1d-3d.xml b/water-hammer/precice-config-1d-3d.xml
new file mode 100644
index 000000000..533fdd25d
--- /dev/null
+++ b/water-hammer/precice-config-1d-3d.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/water-hammer/precice-config-3d-1d.xml b/water-hammer/precice-config-3d-1d.xml
new file mode 100644
index 000000000..eaf6ba62c
--- /dev/null
+++ b/water-hammer/precice-config-3d-1d.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/water-hammer/precice-config.xml b/water-hammer/precice-config.xml
new file mode 120000
index 000000000..9b22aa251
--- /dev/null
+++ b/water-hammer/precice-config.xml
@@ -0,0 +1 @@
+precice-config-3d-1d.xml
\ No newline at end of file
diff --git a/water-hammer/set-case.sh b/water-hammer/set-case.sh
new file mode 100755
index 000000000..da7c0aa05
--- /dev/null
+++ b/water-hammer/set-case.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+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
+