Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
2486383
Water Hammer Tutorial
franiqui Aug 5, 2025
23f2f8d
Changed files
franiqui Aug 6, 2025
47e2198
Update thermodynamicProperties
franiqui Oct 6, 2025
ee3036d
Update fvSchemes
franiqui Oct 6, 2025
b602a51
Update fvSolution
franiqui Oct 6, 2025
a722414
Update probes
franiqui Oct 6, 2025
8348272
Update thermodynamicProperties
franiqui Oct 6, 2025
3a49db6
Update fvSchemes
franiqui Oct 6, 2025
02bc955
Update fvSolution
franiqui Oct 6, 2025
e59daa4
Update probes
franiqui Oct 6, 2025
caba83e
Update probesDict
franiqui Oct 6, 2025
647cc1b
Update thermodynamicProperties
franiqui Oct 6, 2025
359966d
Update fvSchemes
franiqui Oct 6, 2025
a9ebbdf
Update fvSolution
franiqui Oct 6, 2025
516a4ba
Update probes
franiqui Oct 6, 2025
ec8a328
Update probesDict
franiqui Oct 6, 2025
f9b3a5d
Update thermodynamicProperties
franiqui Oct 6, 2025
d8e9408
Update fvSchemes
franiqui Oct 6, 2025
8d7204e
Update fvSolution
franiqui Oct 6, 2025
2618cbe
Update probes
franiqui Oct 6, 2025
65fb3d3
Update probesDict
franiqui Oct 6, 2025
eba2d5e
Update and rename README.md to README.txt
franiqui Oct 6, 2025
43e14b7
Remove obsolete plot files
franiqui Oct 6, 2025
e79dafa
Update precice-config.xml for 3D–3D case
franiqui Oct 6, 2025
e01128c
Refactor/update
franiqui Oct 20, 2025
877d12e
Restore water-hammer files from before e01128cd8
franiqui Oct 20, 2025
bdd79ea
Deleted ENTIRE Water Hammer case
franiqui Oct 20, 2025
e02da82
STARTED AGAIN TUTORIAL
franiqui Oct 20, 2025
2f31599
Changed participant name Fluid1DLeft
franiqui Oct 20, 2025
88cc0c6
Changed ymin and ymax of 3D cases
franiqui Oct 20, 2025
18c0427
Water Hammer Tutorial - New structure with only 1D-3D coupling
franiqui Oct 23, 2025
1d5c577
Updated structure on the Water Hammer tutorial
franiqui Oct 23, 2025
4bd38d1
Updated clean.sh and run.sh to work in the current tutorial structure
franiqui Oct 24, 2025
d671761
Suggested changes
franiqui Oct 27, 2025
2d679a9
Suggested Changes and Updated solver-fluid1d/ directory
franiqui Oct 27, 2025
4d59e72
Merge branch 'develop' into WaterHammerTutorial
MakisH Feb 11, 2026
d767ee0
Use configuration reader in dumux-adapter (#713)
Fujikawas Feb 16, 2026
2503017
Adapt system tests setup for fenics-adapter (#712)
MakisH Feb 18, 2026
a54b756
Addition of 1d-1d and 3d-3d
franiqui Feb 20, 2026
8e9fae7
Modification of multiscale-spread-profile to multiscale-cross-section…
franiqui Feb 23, 2026
75382aa
Merge branch 'develop' into WaterHammerTutorial
franiqui Feb 23, 2026
8eb95b0
Severity > debug & point to files in README.md
franiqui Feb 23, 2026
903a819
Merge branch 'WaterHammerTutorial' of github.com:franiqui/tutorials i…
franiqui Feb 23, 2026
bf826ac
In line equations
franiqui Feb 23, 2026
46be037
Addition of visualization scripts
franiqui Feb 28, 2026
7bea75d
Addition of visualization script to plot outlet pressure in README.md
franiqui Mar 1, 2026
9a89090
Addition of PR to changelog-entries
franiqui Mar 2, 2026
e5f87d1
Update changelog entry
MakisH Mar 9, 2026
264de16
Rename watchpoint.txt to probes.txt
MakisH Mar 9, 2026
f2ca8b7
Minor fixes in plot-pressure.py
MakisH Mar 9, 2026
f3aeadc
Suggested changes
franiqui Mar 9, 2026
86bdd2a
Merge branch 'develop' into WaterHammerTutorial
MakisH Mar 9, 2026
7de2f87
Update water-hammer/fluid1d-left-nutils/run.sh
franiqui Mar 10, 2026
6cf4471
Update water-hammer/fluid1d-right-nutils/run.sh
franiqui Mar 10, 2026
d572cec
Fixing of plot-pressure.py
franiqui Mar 10, 2026
b2b6433
Updated README.md
franiqui Mar 10, 2026
d75b21c
Removal of \, on README
franiqui Mar 10, 2026
7f4c366
Fix of plot-pressure.py
franiqui Mar 10, 2026
2ffe048
Minor tweaks in the README
MakisH Mar 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions water-hammer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
title: Partitioned Water Hammer
keywords: OpenFOAM, python, preCICE, multiscale, fluid, FSI, transient
summary: The Partitioned Water Hammer tutorial simulates unsteady pressure wave propagation in pipe systems using different 1D and 3D configurations coupled via preCICE.
---

Comment thread
franiqui marked this conversation as resolved.
## Setup

This tutorial demonstrates how to model the **water hammer phenomenon** — a transient pressure surge caused by a rapid change in flow — using **partitioned coupling** with [preCICE](https://precice.org).

It supports multiple configurations:

- **1D–1D**: Python solvers on both sides
- **1D–3D**: Python 1D solver coupled to OpenFOAM
- **3D–3D**: OpenFOAM solvers on both sides

We exchange:

- **Velocity** from downstream to upstream
- **Pressure** from upstream to downstream

This allows realistic simulation of pressure wave propagation.

## Folder structure

```bash
water-hammer/
├── case-1d/ # Monolithic 1D simulation
| └── fluid1d-python-uncoupled/
├── case-3d/ # Monolithic 3D simulation
│ └── fluid3d-openfoam-uncoupled/
├── case-1d-1d/ # Partitioned 1D–1D simulation
│ ├── fluid1dleft-python/
│ └── fluid1dright-python/
├── case-1d-3d/ # Partitioned 1D–3D simulation
│ ├── fluid1d-python/
│ └── fluid3d-openfoam/
├── case-3d-3d/ # Partitioned 3D–3D simulation
│ ├── fluid3d-openfoam-left/
│ └── fluid3d-openfoam-right/
├── results/ # Output data and plots
└── README.md # This file
```

## How to use

### 1D–1D Coupling (Python–Python)

In two different terminals execute

```bash
cd case-1d-1d/fluid1dleft-python && ./run.sh
```

```bash
cd case-1d-1d/fluid1dright-python && ./run.sh
```

### 3D-3D Coupling (OpenFOAM-OpenFOAM)

In two different terminals execute

```bash
cd case-3d-3d/fluid3d-openfoam-left && ./run.sh
```

```bash
cd case-3d-3d/fluid3d-openfoam-right && ./run.sh
```

### 1D-3D Coupling (Python-OpenFOAM)

In two different terminals execute

```bash
cd case-1d-3d/fluid1d-python && ./run.sh
```

```bash
cd case-1d-3d/fluid3d-openfoam && ./run.sh
```

### 1D (Monolithic, Python)

In one terminal, execute

```bash
cd case-1d/fluid1d-python-uncoupled && ./run.sh
```

### 3D (Monolithic, OpenFOAM)

In one terminal, execute

```bash
cd case-3d/fluid3d-openfoam-uncoupled && ./run.sh
```
11 changes: 11 additions & 0 deletions water-hammer/case-1d-1d/clean-tutorial.sh
Original file line number Diff line number Diff line change
@@ -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

147 changes: 147 additions & 0 deletions water-hammer/case-1d-1d/fluid1dleft-python/Fluid1D.py
Original file line number Diff line number Diff line change
@@ -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]]
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)
8 changes: 8 additions & 0 deletions water-hammer/case-1d-1d/fluid1dleft-python/clean.sh
Original file line number Diff line number Diff line change
@@ -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 .
6 changes: 6 additions & 0 deletions water-hammer/case-1d-1d/fluid1dleft-python/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
setuptools # required by nutils
nutils==9
numpy >1, <2
pyprecice~=3.0
matplotlib
treelog
13 changes: 13 additions & 0 deletions water-hammer/case-1d-1d/fluid1dleft-python/run.sh
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading