From 825850919bdf92dd8abb86a3d31d8b709b491a33 Mon Sep 17 00:00:00 2001
From: Benjamin Uekermann <benjamin.uekermann@ipvs.uni-stuttgart.de>
Date: Thu, 17 Mar 2022 09:42:33 +0100
Subject: [PATCH 01/22] Add non-functional skeleton for volume nutils case

---
 channel-transport/clean-tutorial.sh           |   1 +
 channel-transport/fluid-nutils/clean.sh       |   6 +
 channel-transport/fluid-nutils/fluid.py       | 100 +++++++++++++++++
 channel-transport/fluid-nutils/run.sh         |   4 +
 channel-transport/precice-config.xml          |  46 ++++++++
 channel-transport/transport-nutils/clean.sh   |   6 +
 channel-transport/transport-nutils/run.sh     |   4 +
 .../transport-nutils/transport.py             | 104 ++++++++++++++++++
 8 files changed, 271 insertions(+)
 create mode 120000 channel-transport/clean-tutorial.sh
 create mode 100755 channel-transport/fluid-nutils/clean.sh
 create mode 100644 channel-transport/fluid-nutils/fluid.py
 create mode 100755 channel-transport/fluid-nutils/run.sh
 create mode 100644 channel-transport/precice-config.xml
 create mode 100755 channel-transport/transport-nutils/clean.sh
 create mode 100755 channel-transport/transport-nutils/run.sh
 create mode 100644 channel-transport/transport-nutils/transport.py

diff --git a/channel-transport/clean-tutorial.sh b/channel-transport/clean-tutorial.sh
new file mode 120000
index 000000000..4713f5092
--- /dev/null
+++ b/channel-transport/clean-tutorial.sh
@@ -0,0 +1 @@
+../tools/clean-tutorial-base.sh
\ No newline at end of file
diff --git a/channel-transport/fluid-nutils/clean.sh b/channel-transport/fluid-nutils/clean.sh
new file mode 100755
index 000000000..6893c1ea5
--- /dev/null
+++ b/channel-transport/fluid-nutils/clean.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -e -u
+
+. ../../tools/cleaning-tools.sh
+
+clean_nutils .
diff --git a/channel-transport/fluid-nutils/fluid.py b/channel-transport/fluid-nutils/fluid.py
new file mode 100644
index 000000000..2fda4589f
--- /dev/null
+++ b/channel-transport/fluid-nutils/fluid.py
@@ -0,0 +1,100 @@
+#! /usr/bin/env python3
+
+import nutils
+import treelog
+import numpy as np
+import precice
+from mpi4py import MPI
+
+
+def main():
+
+    print("Running utils")
+
+    # define the Nutils mesh
+    grid = [np.linspace(a, b, round((b - a) / size) + 1) for (a, b, size) in [(0, 1, 0.05), (-.25, 0, 0.05)]]
+    domain, geom = nutils.mesh.rectilinear(grid)
+
+    # Nutils namespace
+    ns = nutils.function.Namespace()
+    ns.x = geom
+
+    ns.basis = domain.basis('std', degree=1)  # linear finite elements
+    ns.u = 'basis_n ?lhs_n'  # solution
+    ns.dudt = 'basis_n (?lhs_n - ?lhs0_n) / ?dt'  # time derivative
+    ns.flux = 'basis_n ?fluxdofs_n'  # heat flux
+    ns.k = 100  # thermal diffusivity
+    ns.uwall = 310  # wall temperature
+
+    # define the weak form
+    res = domain.integral('(basis_n dudt + k basis_n,i u_,i) d:x' @ ns, degree=2)
+
+    # define Dirichlet boundary condition
+    sqr = domain.boundary['bottom'].integral('(u - uwall)^2 d:x' @ ns, degree=2)
+    cons = nutils.solver.optimize('lhs', sqr, droptol=1e-15)
+
+    # preCICE setup
+    interface = precice.Interface("Fluid", "../precice-config.xml", 0, 1)
+
+    # define coupling mesh
+    mesh_name = "Fluid-Mesh"
+    mesh_id = interface.get_mesh_id(mesh_name)
+    coupling_boundary = domain.boundary['top']
+    coupling_sample = coupling_boundary.sample('gauss', degree=2)  # mesh vertices at Gauss points
+    vertices = coupling_sample.eval(ns.x)
+    vertex_ids = interface.set_mesh_vertices(mesh_id, vertices)
+
+    # coupling data
+    velocity_id = interface.get_data_id("Velocity", mesh_id)
+
+    # helper functions to project heat flux to coupling boundary
+    projection_matrix = coupling_boundary.integrate(ns.eval_nm('basis_n basis_m d:x'), degree=2)
+    projection_cons = np.zeros(res.shape)
+    projection_cons[projection_matrix.rowsupp(1e-15)] = np.nan
+    def fluxdofs(v): return projection_matrix.solve(v, constrain=projection_cons)
+
+    precice_dt = interface.initialize()
+
+    cons0 = cons  # to not lose the Dirichlet BC at the bottom
+    lhs0 = np.zeros(res.shape)  # solution from previous timestep
+    timestep = 0
+    dt = 0.01
+
+    # set u = uwall as initial condition and visualize
+    sqr = domain.integral('(u - uwall)^2' @ ns, degree=2)
+    lhs0 = nutils.solver.optimize('lhs', sqr)
+    bezier = domain.sample('bezier', 2)
+    x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs0)
+    with treelog.add(treelog.DataLog()):
+        nutils.export.vtk('Fluid_0', bezier.tri, x, T=u)
+
+    while interface.is_coupling_ongoing():
+
+        # potentially adjust non-matching timestep sizes
+        dt = min(dt, precice_dt)
+
+        # solve nutils timestep
+        lhs = nutils.solver.solve_linear('lhs', res, constrain=cons, arguments=dict(lhs0=lhs0, dt=dt))
+
+        if interface.is_write_data_required(dt):
+            #velocity_values = TODO # matrix with same format as vertices above
+            interface.write_block_vector_data(velocity_id, vertex_ids, velocity_values)
+
+        # do the coupling
+        precice_dt = interface.advance(dt)
+
+        # advance variables
+        timestep += 1
+        lhs0 = lhs
+
+       if timestep % 20 == 0:  # visualize
+           bezier = domain.sample('bezier', 2)
+           x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs)
+           with treelog.add(treelog.DataLog()):
+               nutils.export.vtk('Fluid_' + str(timestep), bezier.tri, x, T=u)
+
+    interface.finalize()
+
+
+if __name__ == '__main__':
+    nutils.cli.run(main)
diff --git a/channel-transport/fluid-nutils/run.sh b/channel-transport/fluid-nutils/run.sh
new file mode 100755
index 000000000..788f40d13
--- /dev/null
+++ b/channel-transport/fluid-nutils/run.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+set -e -u
+
+python3 fluid.py
diff --git a/channel-transport/precice-config.xml b/channel-transport/precice-config.xml
new file mode 100644
index 000000000..88d00c5f2
--- /dev/null
+++ b/channel-transport/precice-config.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<precice-configuration>
+  <log>
+    <sink
+      filter="%Severity% > debug and %Rank% = 0"
+      format="---[precice] %ColorizedSeverity% %Message%"
+      enabled="true" />
+  </log>
+
+  <solver-interface dimensions="2">
+    <data:vector name="Velocity" />
+
+    <mesh name="Fluid-Mesh">
+      <use-data name="Velocity" />
+    </mesh>
+
+    <mesh name="Transport-Mesh">
+      <use-data name="Velocity" />
+    </mesh>
+
+    <participant name="Fluid">
+      <use-mesh name="Fluid-Mesh" provide="yes" />
+      <use-mesh name="Transport-Mesh" from="Transport" />
+      <write-data name="Velocity" mesh="Fluid-Mesh" />
+      <mapping:rbf-thin-plate-splines
+        direction="write"
+        from="Fluid-Mesh"
+        to="Transport-Mesh"
+        constraint="consistent" />
+    </participant>
+
+    <participant name="Transport">
+      <use-mesh name="Transport-Mesh" provide="yes" />
+      <read-data name="Velocity" mesh="Transport-Mesh" />
+    </participant>
+
+    <m2n:sockets from="Fluid" to="Transport" exchange-directory=".." />
+
+    <coupling-scheme:serial-explicit>
+      <time-window-size value="0.01" />
+      <max-time value="0.2" />
+      <participants first="Fluid" second="Transport" />
+      <exchange data="Velocity" mesh="Transport-Mesh" from="Fluid" to="Transport" />
+    </coupling-scheme:serial-explicit>
+  </solver-interface>
+</precice-configuration>
diff --git a/channel-transport/transport-nutils/clean.sh b/channel-transport/transport-nutils/clean.sh
new file mode 100755
index 000000000..6893c1ea5
--- /dev/null
+++ b/channel-transport/transport-nutils/clean.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -e -u
+
+. ../../tools/cleaning-tools.sh
+
+clean_nutils .
diff --git a/channel-transport/transport-nutils/run.sh b/channel-transport/transport-nutils/run.sh
new file mode 100755
index 000000000..ac3581bb5
--- /dev/null
+++ b/channel-transport/transport-nutils/run.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+set -e -u
+
+python3 transport.py
diff --git a/channel-transport/transport-nutils/transport.py b/channel-transport/transport-nutils/transport.py
new file mode 100644
index 000000000..69d2e7e56
--- /dev/null
+++ b/channel-transport/transport-nutils/transport.py
@@ -0,0 +1,104 @@
+#! /usr/bin/env python3
+
+import nutils
+import treelog
+import numpy as np
+import precice
+from mpi4py import MPI
+
+
+def main():
+
+    print("Running utils")
+
+    # define the Nutils mesh
+    grid = [np.linspace(a, b, round((b - a) / size) + 1) for (a, b, size) in [(0, 1, 0.05), (-.25, 0, 0.05)]]
+    domain, geom = nutils.mesh.rectilinear(grid)
+
+    # Nutils namespace
+    ns = nutils.function.Namespace()
+    ns.x = geom
+
+    ns.basis = domain.basis('std', degree=1)  # linear finite elements
+    ns.u = 'basis_n ?lhs_n'  # solution
+    ns.dudt = 'basis_n (?lhs_n - ?lhs0_n) / ?dt'  # time derivative
+    ns.flux = 'basis_n ?fluxdofs_n'  # heat flux
+    ns.k = 100  # thermal diffusivity
+    ns.uwall = 310  # wall temperature
+
+    # define the weak form
+    res = domain.integral('(basis_n dudt + k basis_n,i u_,i) d:x' @ ns, degree=2)
+
+    # define Dirichlet boundary condition
+    sqr = domain.boundary['bottom'].integral('(u - uwall)^2 d:x' @ ns, degree=2)
+    cons = nutils.solver.optimize('lhs', sqr, droptol=1e-15)
+
+    # preCICE setup
+    interface = precice.Interface("Transport", "../precice-config.xml", 0, 1)
+
+    # define coupling mesh
+    mesh_name = "Transport-Mesh"
+    mesh_id = interface.get_mesh_id(mesh_name)
+    coupling_boundary = domain.boundary['top']
+    coupling_sample = coupling_boundary.sample('gauss', degree=2)  # mesh vertices at Gauss points
+    vertices = coupling_sample.eval(ns.x)
+    vertex_ids = interface.set_mesh_vertices(mesh_id, vertices)
+
+    # coupling data
+    velocity_id = interface.get_data_id("Velocity", mesh_id)
+
+    # helper functions to project heat flux to coupling boundary
+    projection_matrix = coupling_boundary.integrate(ns.eval_nm('basis_n basis_m d:x'), degree=2)
+    projection_cons = np.zeros(res.shape)
+    projection_cons[projection_matrix.rowsupp(1e-15)] = np.nan
+    def fluxdofs(v): return projection_matrix.solve(v, constrain=projection_cons)
+
+    precice_dt = interface.initialize()
+
+    cons0 = cons  # to not lose the Dirichlet BC at the bottom
+    lhs0 = np.zeros(res.shape)  # solution from previous timestep
+    timestep = 0
+    dt = 0.01
+
+    # set u = uwall as initial condition and visualize
+    sqr = domain.integral('(u - uwall)^2' @ ns, degree=2)
+    lhs0 = nutils.solver.optimize('lhs', sqr)
+    bezier = domain.sample('bezier', 2)
+    x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs0)
+    with treelog.add(treelog.DataLog()):
+        nutils.export.vtk('Solid_0', bezier.tri, x, T=u)
+
+    while interface.is_coupling_ongoing():
+
+        # read temperature from interface
+        if interface.is_read_data_available():
+            velocity_values = interface.read_block_vector_data(velocity_id, vertex_ids)
+            velocity_function = coupling_sample.asfunction(velocity_values)
+
+            sqr = coupling_sample.integral((ns.u - velocity_function)**2)
+            cons = nutils.solver.optimize('lhs', sqr, droptol=1e-15, constrain=cons0)
+
+        # potentially adjust non-matching timestep sizes
+        dt = min(dt, precice_dt)
+
+        # solve nutils timestep
+        lhs = nutils.solver.solve_linear('lhs', res, constrain=cons, arguments=dict(lhs0=lhs0, dt=dt))
+
+        # do the coupling
+        precice_dt = interface.advance(dt)
+
+        # advance variables
+        timestep += 1
+        lhs0 = lhs
+
+        if timestep % 20 == 0:  # visualize
+            bezier = domain.sample('bezier', 2)
+            x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs)
+            with treelog.add(treelog.DataLog()):
+                nutils.export.vtk('Transport_' + str(timestep), bezier.tri, x, T=u)
+
+    interface.finalize()
+
+
+if __name__ == '__main__':
+    nutils.cli.run(main)

From 2826a46e612b473f8995be5bf2c16703a9ecd446 Mon Sep 17 00:00:00 2001
From: Gertjan van Zwieten <gertjanvanzwieten@evalf.nl>
Date: Thu, 17 Mar 2022 16:46:35 +0100
Subject: [PATCH 02/22] modify both scripts, untested

---
 channel-transport/fluid-nutils/fluid.py       | 77 ++++++++++---------
 .../transport-nutils/transport.py             | 46 +++++------
 2 files changed, 61 insertions(+), 62 deletions(-)

diff --git a/channel-transport/fluid-nutils/fluid.py b/channel-transport/fluid-nutils/fluid.py
index 2fda4589f..1c4ac5b58 100644
--- a/channel-transport/fluid-nutils/fluid.py
+++ b/channel-transport/fluid-nutils/fluid.py
@@ -1,7 +1,7 @@
 #! /usr/bin/env python3
 
-import nutils
-import treelog
+from nutils import function, mesh, cli
+import treelog as log
 import numpy as np
 import precice
 from mpi4py import MPI
@@ -12,25 +12,34 @@ def main():
     print("Running utils")
 
     # define the Nutils mesh
-    grid = [np.linspace(a, b, round((b - a) / size) + 1) for (a, b, size) in [(0, 1, 0.05), (-.25, 0, 0.05)]]
+    grid = np.linspace(0, 5, 21), np.linspace(0, 2, 9)
     domain, geom = nutils.mesh.rectilinear(grid)
+    domain = domain.withboundary(inflow='left', outflow='right', wall='top,bottom') \
+           - domain[8:13,:3].withboundary(wall='left,top,right')
+
+    gauss = domain.sample('gauss', degree=4)
 
     # Nutils namespace
     ns = nutils.function.Namespace()
     ns.x = geom
 
-    ns.basis = domain.basis('std', degree=1)  # linear finite elements
-    ns.u = 'basis_n ?lhs_n'  # solution
-    ns.dudt = 'basis_n (?lhs_n - ?lhs0_n) / ?dt'  # time derivative
-    ns.flux = 'basis_n ?fluxdofs_n'  # heat flux
-    ns.k = 100  # thermal diffusivity
-    ns.uwall = 310  # wall temperature
+    ns.ubasis = domain.basis('std', degree=2).vector(2)
+    ns.pbasis = domain.basis('std', degree=1)
+    ns.u_i = 'ubasis_ni ?u_n'  # solution
+    ns.p = 'pbasis_n ?p_n'  # solution
+    ns.dudt_i = 'ubasis_ni (?u_n - ?u0_n) / ?dt'  # time derivative
+    ns.μ = 100  # viscosity
+    ns.σ_ij = 'μ (u_i,j + u_j,i) - p δ_ij'
+    ns.uin = '10 x_1 (2 - x_1)' # inflow profile
 
     # define the weak form
-    res = domain.integral('(basis_n dudt + k basis_n,i u_,i) d:x' @ ns, degree=2)
+    ures = gauss.integral('ubasis_n,j σ_ij d:x' @ ns)
+    pres = gauss.integral('pbasis u_k,k d:x' @ ns)
 
     # define Dirichlet boundary condition
-    sqr = domain.boundary['bottom'].integral('(u - uwall)^2 d:x' @ ns, degree=2)
+    sqr = domain.boundary['inflow'].integral('(u_0 - uin)^2 d:x' @ ns, degree=2)
+    sqr += domain.boundary['inflow,outflow'].integral('u_1^2 d:x' @ ns, degree=2)
+    sqr += domain.boundary['wall'].integral('u_k u_k d:x' @ ns, degree=2)
     cons = nutils.solver.optimize('lhs', sqr, droptol=1e-15)
 
     # preCICE setup
@@ -39,20 +48,12 @@ def main():
     # define coupling mesh
     mesh_name = "Fluid-Mesh"
     mesh_id = interface.get_mesh_id(mesh_name)
-    coupling_boundary = domain.boundary['top']
-    coupling_sample = coupling_boundary.sample('gauss', degree=2)  # mesh vertices at Gauss points
-    vertices = coupling_sample.eval(ns.x)
+    vertices = gauss.eval(ns.x)
     vertex_ids = interface.set_mesh_vertices(mesh_id, vertices)
 
     # coupling data
     velocity_id = interface.get_data_id("Velocity", mesh_id)
 
-    # helper functions to project heat flux to coupling boundary
-    projection_matrix = coupling_boundary.integrate(ns.eval_nm('basis_n basis_m d:x'), degree=2)
-    projection_cons = np.zeros(res.shape)
-    projection_cons[projection_matrix.rowsupp(1e-15)] = np.nan
-    def fluxdofs(v): return projection_matrix.solve(v, constrain=projection_cons)
-
     precice_dt = interface.initialize()
 
     cons0 = cons  # to not lose the Dirichlet BC at the bottom
@@ -60,13 +61,15 @@ def fluxdofs(v): return projection_matrix.solve(v, constrain=projection_cons)
     timestep = 0
     dt = 0.01
 
-    # set u = uwall as initial condition and visualize
-    sqr = domain.integral('(u - uwall)^2' @ ns, degree=2)
-    lhs0 = nutils.solver.optimize('lhs', sqr)
-    bezier = domain.sample('bezier', 2)
-    x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs0)
-    with treelog.add(treelog.DataLog()):
-        nutils.export.vtk('Fluid_0', bezier.tri, x, T=u)
+    state = nutils.solver.solve_linear(['u', 'p'], [ures, pres]) # initial condition
+
+    # add convective term for navier-stokes
+    ures += gauss.integral('ubasis_ni (dudt_i + μ (u_i u_j)_,i) d:x' @ ns)
+
+#   bezier = domain.sample('bezier', 2)
+#   x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs0)
+#   with log.add(log.DataLog()):
+#       nutils.export.vtk('Fluid_0', bezier.tri, x, T=u)
 
     while interface.is_coupling_ongoing():
 
@@ -74,10 +77,11 @@ def fluxdofs(v): return projection_matrix.solve(v, constrain=projection_cons)
         dt = min(dt, precice_dt)
 
         # solve nutils timestep
-        lhs = nutils.solver.solve_linear('lhs', res, constrain=cons, arguments=dict(lhs0=lhs0, dt=dt))
+        state['u0'] = state['u']
+        state = nutils.solver.newton(['u', 'p'], [ures, pres], constrain=cons, arguments=state).solve(1e-10)
 
         if interface.is_write_data_required(dt):
-            #velocity_values = TODO # matrix with same format as vertices above
+            velocity_values = gauss.eval(ns.u, **state)
             interface.write_block_vector_data(velocity_id, vertex_ids, velocity_values)
 
         # do the coupling
@@ -85,13 +89,16 @@ def fluxdofs(v): return projection_matrix.solve(v, constrain=projection_cons)
 
         # advance variables
         timestep += 1
-        lhs0 = lhs
 
-       if timestep % 20 == 0:  # visualize
-           bezier = domain.sample('bezier', 2)
-           x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs)
-           with treelog.add(treelog.DataLog()):
-               nutils.export.vtk('Fluid_' + str(timestep), bezier.tri, x, T=u)
+        bezier = domain.sample('bezier', 2)
+        u = bezier.eval(ns.u, **state)
+        export.triplot('solution.png', x, numpy.linalg.norm(u, axis=1), tri=bezier.tri, hull=bezier.hull)
+
+        #if timestep % 20 == 0:  # visualize
+        #   bezier = domain.sample('bezier', 2)
+        #   x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs)
+        #   with log.add(log.DataLog()):
+        #       nutils.export.vtk('Fluid_' + str(timestep), bezier.tri, x, T=u)
 
     interface.finalize()
 
diff --git a/channel-transport/transport-nutils/transport.py b/channel-transport/transport-nutils/transport.py
index 69d2e7e56..06fc6cd7a 100644
--- a/channel-transport/transport-nutils/transport.py
+++ b/channel-transport/transport-nutils/transport.py
@@ -1,7 +1,7 @@
 #! /usr/bin/env python3
 
-import nutils
-import treelog
+from nutils import function, mesh, cli
+import treelog as log
 import numpy as np
 import precice
 from mpi4py import MPI
@@ -12,25 +12,30 @@ def main():
     print("Running utils")
 
     # define the Nutils mesh
-    grid = [np.linspace(a, b, round((b - a) / size) + 1) for (a, b, size) in [(0, 1, 0.05), (-.25, 0, 0.05)]]
+    grid = np.linspace(0, 5, 21), np.linspace(0, 2, 9)
     domain, geom = nutils.mesh.rectilinear(grid)
+    domain = domain.withboundary(inflow='left', outflow='right', wall='top,bottom') \
+           - domain[8:13,:3].withboundary(wall='left,top,right')
+
+    gauss = domain.sample('gauss', degree=2)
 
     # Nutils namespace
     ns = nutils.function.Namespace()
     ns.x = geom
-
     ns.basis = domain.basis('std', degree=1)  # linear finite elements
     ns.u = 'basis_n ?lhs_n'  # solution
     ns.dudt = 'basis_n (?lhs_n - ?lhs0_n) / ?dt'  # time derivative
-    ns.flux = 'basis_n ?fluxdofs_n'  # heat flux
+    ns.vbasis = gauss.basis().vector(2)
+    ns.velocity_i = 'vbasis_ni ?velocity_n'
     ns.k = 100  # thermal diffusivity
-    ns.uwall = 310  # wall temperature
+    ns.xblob = 1, 1
+    ns.uinit = '.5 - .5 tanh(((x_i - xblob_i) (x_i - xblob_i) - .5) / .1)'
 
     # define the weak form
-    res = domain.integral('(basis_n dudt + k basis_n,i u_,i) d:x' @ ns, degree=2)
+    res = gauss.integral('(basis_n (dudt + (velocity_i u)_,i) + k basis_n,i u_,i) d:x' @ ns)
 
     # define Dirichlet boundary condition
-    sqr = domain.boundary['bottom'].integral('(u - uwall)^2 d:x' @ ns, degree=2)
+    sqr = domain.boundary['inflow'].integral('u^2 d:x' @ ns, degree=2)
     cons = nutils.solver.optimize('lhs', sqr, droptol=1e-15)
 
     # preCICE setup
@@ -39,33 +44,24 @@ def main():
     # define coupling mesh
     mesh_name = "Transport-Mesh"
     mesh_id = interface.get_mesh_id(mesh_name)
-    coupling_boundary = domain.boundary['top']
-    coupling_sample = coupling_boundary.sample('gauss', degree=2)  # mesh vertices at Gauss points
-    vertices = coupling_sample.eval(ns.x)
+    vertices = gauss.eval(ns.x)
     vertex_ids = interface.set_mesh_vertices(mesh_id, vertices)
 
     # coupling data
     velocity_id = interface.get_data_id("Velocity", mesh_id)
 
-    # helper functions to project heat flux to coupling boundary
-    projection_matrix = coupling_boundary.integrate(ns.eval_nm('basis_n basis_m d:x'), degree=2)
-    projection_cons = np.zeros(res.shape)
-    projection_cons[projection_matrix.rowsupp(1e-15)] = np.nan
-    def fluxdofs(v): return projection_matrix.solve(v, constrain=projection_cons)
-
     precice_dt = interface.initialize()
 
-    cons0 = cons  # to not lose the Dirichlet BC at the bottom
-    lhs0 = np.zeros(res.shape)  # solution from previous timestep
     timestep = 0
     dt = 0.01
 
     # set u = uwall as initial condition and visualize
-    sqr = domain.integral('(u - uwall)^2' @ ns, degree=2)
+    sqr = domain.integral('(u - uinit)^2' @ ns, degree=2)
     lhs0 = nutils.solver.optimize('lhs', sqr)
+
     bezier = domain.sample('bezier', 2)
     x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs0)
-    with treelog.add(treelog.DataLog()):
+    with log.add(log.DataLog()):
         nutils.export.vtk('Solid_0', bezier.tri, x, T=u)
 
     while interface.is_coupling_ongoing():
@@ -73,16 +69,12 @@ def fluxdofs(v): return projection_matrix.solve(v, constrain=projection_cons)
         # read temperature from interface
         if interface.is_read_data_available():
             velocity_values = interface.read_block_vector_data(velocity_id, vertex_ids)
-            velocity_function = coupling_sample.asfunction(velocity_values)
-
-            sqr = coupling_sample.integral((ns.u - velocity_function)**2)
-            cons = nutils.solver.optimize('lhs', sqr, droptol=1e-15, constrain=cons0)
 
         # potentially adjust non-matching timestep sizes
         dt = min(dt, precice_dt)
 
         # solve nutils timestep
-        lhs = nutils.solver.solve_linear('lhs', res, constrain=cons, arguments=dict(lhs0=lhs0, dt=dt))
+        lhs = nutils.solver.solve_linear('lhs', res, constrain=cons, arguments=dict(lhs0=lhs0, dt=dt, velocity=velocity_values))
 
         # do the coupling
         precice_dt = interface.advance(dt)
@@ -94,7 +86,7 @@ def fluxdofs(v): return projection_matrix.solve(v, constrain=projection_cons)
         if timestep % 20 == 0:  # visualize
             bezier = domain.sample('bezier', 2)
             x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs)
-            with treelog.add(treelog.DataLog()):
+            with log.add(log.DataLog()):
                 nutils.export.vtk('Transport_' + str(timestep), bezier.tri, x, T=u)
 
     interface.finalize()

From 646764e87b7853fac468b48d330b8d54be891b17 Mon Sep 17 00:00:00 2001
From: Benjamin Uekermann <benjamin.uekermann@ipvs.uni-stuttgart.de>
Date: Thu, 17 Mar 2022 21:51:28 +0100
Subject: [PATCH 03/22] Make case work and tweak physics

---
 channel-transport/fluid-nutils/fluid.py       |  57 +++++++++---------
 .../tutorials-channel-transport-physics.png   | Bin 0 -> 90828 bytes
 channel-transport/precice-config.xml          |   9 +--
 .../transport-nutils/transport.py             |  48 ++++++++-------
 4 files changed, 57 insertions(+), 57 deletions(-)
 create mode 100644 channel-transport/images/tutorials-channel-transport-physics.png

diff --git a/channel-transport/fluid-nutils/fluid.py b/channel-transport/fluid-nutils/fluid.py
index 1c4ac5b58..ab7ca0b40 100644
--- a/channel-transport/fluid-nutils/fluid.py
+++ b/channel-transport/fluid-nutils/fluid.py
@@ -1,6 +1,6 @@
 #! /usr/bin/env python3
 
-from nutils import function, mesh, cli
+from nutils import function, mesh, cli, solver, export
 import treelog as log
 import numpy as np
 import precice
@@ -11,16 +11,22 @@ def main():
 
     print("Running utils")
 
+    nx = 48
+    ny = 16
+    step_start = nx // 3
+    step_end = nx // 2
+    step_hight = ny // 2
+
     # define the Nutils mesh
-    grid = np.linspace(0, 5, 21), np.linspace(0, 2, 9)
-    domain, geom = nutils.mesh.rectilinear(grid)
+    grid = np.linspace(0, 6, nx+1), np.linspace(0, 2, ny+1)
+    domain, geom = mesh.rectilinear(grid)
     domain = domain.withboundary(inflow='left', outflow='right', wall='top,bottom') \
-           - domain[8:13,:3].withboundary(wall='left,top,right')
+           - domain[step_start:step_end,:step_hight].withboundary(wall='left,top,right')
 
     gauss = domain.sample('gauss', degree=4)
 
     # Nutils namespace
-    ns = nutils.function.Namespace()
+    ns = function.Namespace()
     ns.x = geom
 
     ns.ubasis = domain.basis('std', degree=2).vector(2)
@@ -28,19 +34,19 @@ def main():
     ns.u_i = 'ubasis_ni ?u_n'  # solution
     ns.p = 'pbasis_n ?p_n'  # solution
     ns.dudt_i = 'ubasis_ni (?u_n - ?u0_n) / ?dt'  # time derivative
-    ns.μ = 100  # viscosity
+    ns.μ = 0.5  # viscosity
     ns.σ_ij = 'μ (u_i,j + u_j,i) - p δ_ij'
     ns.uin = '10 x_1 (2 - x_1)' # inflow profile
 
     # define the weak form
-    ures = gauss.integral('ubasis_n,j σ_ij d:x' @ ns)
-    pres = gauss.integral('pbasis u_k,k d:x' @ ns)
+    ures = gauss.integral('ubasis_ni,j σ_ij d:x' @ ns)
+    pres = gauss.integral('pbasis_n u_k,k d:x' @ ns)
 
     # define Dirichlet boundary condition
     sqr = domain.boundary['inflow'].integral('(u_0 - uin)^2 d:x' @ ns, degree=2)
     sqr += domain.boundary['inflow,outflow'].integral('u_1^2 d:x' @ ns, degree=2)
     sqr += domain.boundary['wall'].integral('u_k u_k d:x' @ ns, degree=2)
-    cons = nutils.solver.optimize('lhs', sqr, droptol=1e-15)
+    cons = solver.optimize(['u'], sqr, droptol=1e-15)
 
     # preCICE setup
     interface = precice.Interface("Fluid", "../precice-config.xml", 0, 1)
@@ -56,29 +62,29 @@ def main():
 
     precice_dt = interface.initialize()
 
-    cons0 = cons  # to not lose the Dirichlet BC at the bottom
-    lhs0 = np.zeros(res.shape)  # solution from previous timestep
     timestep = 0
-    dt = 0.01
+    dt = 0.005
 
-    state = nutils.solver.solve_linear(['u', 'p'], [ures, pres]) # initial condition
+    state = solver.solve_linear(('u', 'p'), (ures, pres), constrain=cons) # initial condition
 
     # add convective term for navier-stokes
-    ures += gauss.integral('ubasis_ni (dudt_i + μ (u_i u_j)_,i) d:x' @ ns)
-
-#   bezier = domain.sample('bezier', 2)
-#   x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs0)
-#   with log.add(log.DataLog()):
-#       nutils.export.vtk('Fluid_0', bezier.tri, x, T=u)
+    ures += gauss.integral('ubasis_ni (dudt_i + μ (u_i u_j)_,j) d:x' @ ns)
 
     while interface.is_coupling_ongoing():
 
+        if timestep % 1 == 0:  # visualize
+           bezier = domain.sample('bezier', 2)
+           x, u, p = bezier.eval(['x_i', 'u_i', 'p'] @ ns, **state)
+           with log.add(log.DataLog()):
+               export.vtk('Fluid_' + str(timestep), bezier.tri, x, u=u, p=p)
+        
         # potentially adjust non-matching timestep sizes
         dt = min(dt, precice_dt)
 
         # solve nutils timestep
         state['u0'] = state['u']
-        state = nutils.solver.newton(['u', 'p'], [ures, pres], constrain=cons, arguments=state).solve(1e-10)
+        state['dt'] = dt
+        state = solver.newton(('u', 'p'), (ures, pres), constrain=cons, arguments=state).solve(1e-10)
 
         if interface.is_write_data_required(dt):
             velocity_values = gauss.eval(ns.u, **state)
@@ -90,18 +96,9 @@ def main():
         # advance variables
         timestep += 1
 
-        bezier = domain.sample('bezier', 2)
-        u = bezier.eval(ns.u, **state)
-        export.triplot('solution.png', x, numpy.linalg.norm(u, axis=1), tri=bezier.tri, hull=bezier.hull)
-
-        #if timestep % 20 == 0:  # visualize
-        #   bezier = domain.sample('bezier', 2)
-        #   x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs)
-        #   with log.add(log.DataLog()):
-        #       nutils.export.vtk('Fluid_' + str(timestep), bezier.tri, x, T=u)
 
     interface.finalize()
 
 
 if __name__ == '__main__':
-    nutils.cli.run(main)
+    cli.run(main)
diff --git a/channel-transport/images/tutorials-channel-transport-physics.png b/channel-transport/images/tutorials-channel-transport-physics.png
new file mode 100644
index 0000000000000000000000000000000000000000..530822c85bee3f86bd159bc0c8f3b6a80a108e71
GIT binary patch
diff --git a/channel-transport/precice-config.xml b/channel-transport/precice-config.xml
index 88d00c5f2..184a3d216 100644
--- a/channel-transport/precice-config.xml
+++ b/channel-transport/precice-config.xml
@@ -22,8 +22,9 @@
       <use-mesh name="Fluid-Mesh" provide="yes" />
       <use-mesh name="Transport-Mesh" from="Transport" />
       <write-data name="Velocity" mesh="Fluid-Mesh" />
-      <mapping:rbf-thin-plate-splines
-        direction="write"
+      <mapping:rbf-compact-tps-c2
+	direction="write"
+        support-radius="0.4"
         from="Fluid-Mesh"
         to="Transport-Mesh"
         constraint="consistent" />
@@ -37,8 +38,8 @@
     <m2n:sockets from="Fluid" to="Transport" exchange-directory=".." />
 
     <coupling-scheme:serial-explicit>
-      <time-window-size value="0.01" />
-      <max-time value="0.2" />
+      <time-window-size value="0.005" />
+      <max-time value="1.0" />
       <participants first="Fluid" second="Transport" />
       <exchange data="Velocity" mesh="Transport-Mesh" from="Fluid" to="Transport" />
     </coupling-scheme:serial-explicit>
diff --git a/channel-transport/transport-nutils/transport.py b/channel-transport/transport-nutils/transport.py
index 06fc6cd7a..562b9a214 100644
--- a/channel-transport/transport-nutils/transport.py
+++ b/channel-transport/transport-nutils/transport.py
@@ -1,6 +1,6 @@
 #! /usr/bin/env python3
 
-from nutils import function, mesh, cli
+from nutils import function, mesh, cli, solver, export
 import treelog as log
 import numpy as np
 import precice
@@ -11,23 +11,29 @@ def main():
 
     print("Running utils")
 
+    nx = 120
+    ny = 32
+    step_start = nx // 3
+    step_end = nx // 2
+    step_hight = ny // 2
+
     # define the Nutils mesh
-    grid = np.linspace(0, 5, 21), np.linspace(0, 2, 9)
-    domain, geom = nutils.mesh.rectilinear(grid)
+    grid = np.linspace(0, 6, nx+1), np.linspace(0, 2, ny+1)
+    domain, geom = mesh.rectilinear(grid)
     domain = domain.withboundary(inflow='left', outflow='right', wall='top,bottom') \
-           - domain[8:13,:3].withboundary(wall='left,top,right')
+           - domain[step_start:step_end,:step_hight].withboundary(wall='left,top,right')
 
     gauss = domain.sample('gauss', degree=2)
 
     # Nutils namespace
-    ns = nutils.function.Namespace()
+    ns = function.Namespace(fallback_length=2)
     ns.x = geom
     ns.basis = domain.basis('std', degree=1)  # linear finite elements
     ns.u = 'basis_n ?lhs_n'  # solution
     ns.dudt = 'basis_n (?lhs_n - ?lhs0_n) / ?dt'  # time derivative
-    ns.vbasis = gauss.basis().vector(2)
-    ns.velocity_i = 'vbasis_ni ?velocity_n'
-    ns.k = 100  # thermal diffusivity
+    ns.vbasis = gauss.basis()
+    ns.velocity_i = 'vbasis_n ?velocity_ni'
+    ns.k = 0.1  # thermal diffusivity
     ns.xblob = 1, 1
     ns.uinit = '.5 - .5 tanh(((x_i - xblob_i) (x_i - xblob_i) - .5) / .1)'
 
@@ -36,7 +42,7 @@ def main():
 
     # define Dirichlet boundary condition
     sqr = domain.boundary['inflow'].integral('u^2 d:x' @ ns, degree=2)
-    cons = nutils.solver.optimize('lhs', sqr, droptol=1e-15)
+    cons = solver.optimize('lhs', sqr, droptol=1e-15)
 
     # preCICE setup
     interface = precice.Interface("Transport", "../precice-config.xml", 0, 1)
@@ -53,19 +59,21 @@ def main():
     precice_dt = interface.initialize()
 
     timestep = 0
-    dt = 0.01
+    dt = 0.005
 
     # set u = uwall as initial condition and visualize
     sqr = domain.integral('(u - uinit)^2' @ ns, degree=2)
-    lhs0 = nutils.solver.optimize('lhs', sqr)
+    lhs0 = solver.optimize('lhs', sqr)
 
-    bezier = domain.sample('bezier', 2)
-    x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs0)
-    with log.add(log.DataLog()):
-        nutils.export.vtk('Solid_0', bezier.tri, x, T=u)
 
     while interface.is_coupling_ongoing():
 
+        if timestep % 1 == 0:  # visualize
+            bezier = domain.sample('bezier', 2)
+            x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs0)
+            with log.add(log.DataLog()):
+                export.vtk('Transport_' + str(timestep), bezier.tri, x, T=u)
+        
         # read temperature from interface
         if interface.is_read_data_available():
             velocity_values = interface.read_block_vector_data(velocity_id, vertex_ids)
@@ -74,7 +82,7 @@ def main():
         dt = min(dt, precice_dt)
 
         # solve nutils timestep
-        lhs = nutils.solver.solve_linear('lhs', res, constrain=cons, arguments=dict(lhs0=lhs0, dt=dt, velocity=velocity_values))
+        lhs = solver.solve_linear('lhs', res, constrain=cons, arguments=dict(lhs0=lhs0, dt=dt, velocity=velocity_values))
 
         # do the coupling
         precice_dt = interface.advance(dt)
@@ -83,14 +91,8 @@ def main():
         timestep += 1
         lhs0 = lhs
 
-        if timestep % 20 == 0:  # visualize
-            bezier = domain.sample('bezier', 2)
-            x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs)
-            with log.add(log.DataLog()):
-                nutils.export.vtk('Transport_' + str(timestep), bezier.tri, x, T=u)
-
     interface.finalize()
 
 
 if __name__ == '__main__':
-    nutils.cli.run(main)
+    cli.run(main)

From 7f7360b22e9fcb944dcf0785128c25db262bcb3d Mon Sep 17 00:00:00 2001
From: Benjamin Uekermann <benjamin.uekermann@ipvs.uni-stuttgart.de>
Date: Thu, 17 Mar 2022 22:00:59 +0100
Subject: [PATCH 04/22] Add README

---
 channel-transport/README.md | 46 +++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)
 create mode 100644 channel-transport/README.md

diff --git a/channel-transport/README.md b/channel-transport/README.md
new file mode 100644
index 000000000..ca5ccd934
--- /dev/null
+++ b/channel-transport/README.md
@@ -0,0 +1,46 @@
+---
+title: Channel transport
+permalink: tutorials-channel-transport.html
+keywords: fluid-structure interaction, FSI, OpenFOAM, FEniCS, Nutils, deal.II, Calculix, SU2,
+summary: This testcase shows how to use preCICE for simple volume-coupled problems.
+---
+
+{% note %}
+Get the [case files of this tutorial](https://github.com/precice/tutorials/tree/master/channel-transport). Read how in the [tutorials introduction](https://www.precice.org/tutorials.html).
+{% endnote %}
+
+## Setup
+
+We model a two-dimensional incompressible fluid flowing through a channel with an obstacle. The fluid problem is coupled to a simple transport problem in a uni-directional fashion. The transport problem is initialized with a circular blob close to the inflow. The convected and diffused blob after 23 timesteps:
+
+![Flap setup](images/tutorials-channel-transport-physics.png)
+
+## Available solvers
+
+Fluid participant:
+
+* Nutils. For more information, have a look at the [Nutils adapter documentation](https://www.precice.org/adapter-nutils.html). This Nutils solver requires at least Nutils v7.0.
+
+Transport participant:
+
+* Nutils. For more information, have a look at the [Nutils adapter documentation](https://www.precice.org/adapter-nutils.html). This Nutils solver requires at least Nutils v7.0.
+
+## Running the Simulation
+
+Open two separate terminals and start one fluid and one transport participant by calling the respective run scripts `run.sh` located in the participant directory. For example:
+
+```bash
+cd fluid-nutils
+./run.sh
+```
+
+and
+
+```bash
+cd transport-nutils
+./run.sh
+```
+
+## Post-processing
+
+All solvers generate `vtk` files which can visualized using, e.g., ParaView.

From 05a3806273a0cfdf2965f9e80e767618357f9585 Mon Sep 17 00:00:00 2001
From: Benjamin Uekermann <benjamin.uekermann@ipvs.uni-stuttgart.de>
Date: Thu, 17 Mar 2022 22:03:04 +0100
Subject: [PATCH 05/22] Format Python codes

---
 channel-transport/fluid-nutils/fluid.py       | 58 +++++++++----------
 .../transport-nutils/transport.py             | 44 +++++++-------
 2 files changed, 52 insertions(+), 50 deletions(-)

diff --git a/channel-transport/fluid-nutils/fluid.py b/channel-transport/fluid-nutils/fluid.py
index ab7ca0b40..8761b58cb 100644
--- a/channel-transport/fluid-nutils/fluid.py
+++ b/channel-transport/fluid-nutils/fluid.py
@@ -18,35 +18,36 @@ def main():
     step_hight = ny // 2
 
     # define the Nutils mesh
-    grid = np.linspace(0, 6, nx+1), np.linspace(0, 2, ny+1)
+    grid = np.linspace(0, 6, nx + 1), np.linspace(0, 2, ny + 1)
     domain, geom = mesh.rectilinear(grid)
-    domain = domain.withboundary(inflow='left', outflow='right', wall='top,bottom') \
-           - domain[step_start:step_end,:step_hight].withboundary(wall='left,top,right')
+    domain = domain.withboundary(inflow="left", outflow="right", wall="top,bottom") - domain[
+        step_start:step_end, :step_hight
+    ].withboundary(wall="left,top,right")
 
-    gauss = domain.sample('gauss', degree=4)
+    gauss = domain.sample("gauss", degree=4)
 
     # Nutils namespace
     ns = function.Namespace()
     ns.x = geom
 
-    ns.ubasis = domain.basis('std', degree=2).vector(2)
-    ns.pbasis = domain.basis('std', degree=1)
-    ns.u_i = 'ubasis_ni ?u_n'  # solution
-    ns.p = 'pbasis_n ?p_n'  # solution
-    ns.dudt_i = 'ubasis_ni (?u_n - ?u0_n) / ?dt'  # time derivative
+    ns.ubasis = domain.basis("std", degree=2).vector(2)
+    ns.pbasis = domain.basis("std", degree=1)
+    ns.u_i = "ubasis_ni ?u_n"  # solution
+    ns.p = "pbasis_n ?p_n"  # solution
+    ns.dudt_i = "ubasis_ni (?u_n - ?u0_n) / ?dt"  # time derivative
     ns.μ = 0.5  # viscosity
-    ns.σ_ij = 'μ (u_i,j + u_j,i) - p δ_ij'
-    ns.uin = '10 x_1 (2 - x_1)' # inflow profile
+    ns.σ_ij = "μ (u_i,j + u_j,i) - p δ_ij"
+    ns.uin = "10 x_1 (2 - x_1)"  # inflow profile
 
     # define the weak form
-    ures = gauss.integral('ubasis_ni,j σ_ij d:x' @ ns)
-    pres = gauss.integral('pbasis_n u_k,k d:x' @ ns)
+    ures = gauss.integral("ubasis_ni,j σ_ij d:x" @ ns)
+    pres = gauss.integral("pbasis_n u_k,k d:x" @ ns)
 
     # define Dirichlet boundary condition
-    sqr = domain.boundary['inflow'].integral('(u_0 - uin)^2 d:x' @ ns, degree=2)
-    sqr += domain.boundary['inflow,outflow'].integral('u_1^2 d:x' @ ns, degree=2)
-    sqr += domain.boundary['wall'].integral('u_k u_k d:x' @ ns, degree=2)
-    cons = solver.optimize(['u'], sqr, droptol=1e-15)
+    sqr = domain.boundary["inflow"].integral("(u_0 - uin)^2 d:x" @ ns, degree=2)
+    sqr += domain.boundary["inflow,outflow"].integral("u_1^2 d:x" @ ns, degree=2)
+    sqr += domain.boundary["wall"].integral("u_k u_k d:x" @ ns, degree=2)
+    cons = solver.optimize(["u"], sqr, droptol=1e-15)
 
     # preCICE setup
     interface = precice.Interface("Fluid", "../precice-config.xml", 0, 1)
@@ -65,26 +66,26 @@ def main():
     timestep = 0
     dt = 0.005
 
-    state = solver.solve_linear(('u', 'p'), (ures, pres), constrain=cons) # initial condition
+    state = solver.solve_linear(("u", "p"), (ures, pres), constrain=cons)  # initial condition
 
     # add convective term for navier-stokes
-    ures += gauss.integral('ubasis_ni (dudt_i + μ (u_i u_j)_,j) d:x' @ ns)
+    ures += gauss.integral("ubasis_ni (dudt_i + μ (u_i u_j)_,j) d:x" @ ns)
 
     while interface.is_coupling_ongoing():
 
         if timestep % 1 == 0:  # visualize
-           bezier = domain.sample('bezier', 2)
-           x, u, p = bezier.eval(['x_i', 'u_i', 'p'] @ ns, **state)
-           with log.add(log.DataLog()):
-               export.vtk('Fluid_' + str(timestep), bezier.tri, x, u=u, p=p)
-        
+            bezier = domain.sample("bezier", 2)
+            x, u, p = bezier.eval(["x_i", "u_i", "p"] @ ns, **state)
+            with log.add(log.DataLog()):
+                export.vtk("Fluid_" + str(timestep), bezier.tri, x, u=u, p=p)
+
         # potentially adjust non-matching timestep sizes
         dt = min(dt, precice_dt)
 
         # solve nutils timestep
-        state['u0'] = state['u']
-        state['dt'] = dt
-        state = solver.newton(('u', 'p'), (ures, pres), constrain=cons, arguments=state).solve(1e-10)
+        state["u0"] = state["u"]
+        state["dt"] = dt
+        state = solver.newton(("u", "p"), (ures, pres), constrain=cons, arguments=state).solve(1e-10)
 
         if interface.is_write_data_required(dt):
             velocity_values = gauss.eval(ns.u, **state)
@@ -96,9 +97,8 @@ def main():
         # advance variables
         timestep += 1
 
-
     interface.finalize()
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     cli.run(main)
diff --git a/channel-transport/transport-nutils/transport.py b/channel-transport/transport-nutils/transport.py
index 562b9a214..507e6a0db 100644
--- a/channel-transport/transport-nutils/transport.py
+++ b/channel-transport/transport-nutils/transport.py
@@ -18,31 +18,32 @@ def main():
     step_hight = ny // 2
 
     # define the Nutils mesh
-    grid = np.linspace(0, 6, nx+1), np.linspace(0, 2, ny+1)
+    grid = np.linspace(0, 6, nx + 1), np.linspace(0, 2, ny + 1)
     domain, geom = mesh.rectilinear(grid)
-    domain = domain.withboundary(inflow='left', outflow='right', wall='top,bottom') \
-           - domain[step_start:step_end,:step_hight].withboundary(wall='left,top,right')
+    domain = domain.withboundary(inflow="left", outflow="right", wall="top,bottom") - domain[
+        step_start:step_end, :step_hight
+    ].withboundary(wall="left,top,right")
 
-    gauss = domain.sample('gauss', degree=2)
+    gauss = domain.sample("gauss", degree=2)
 
     # Nutils namespace
     ns = function.Namespace(fallback_length=2)
     ns.x = geom
-    ns.basis = domain.basis('std', degree=1)  # linear finite elements
-    ns.u = 'basis_n ?lhs_n'  # solution
-    ns.dudt = 'basis_n (?lhs_n - ?lhs0_n) / ?dt'  # time derivative
+    ns.basis = domain.basis("std", degree=1)  # linear finite elements
+    ns.u = "basis_n ?lhs_n"  # solution
+    ns.dudt = "basis_n (?lhs_n - ?lhs0_n) / ?dt"  # time derivative
     ns.vbasis = gauss.basis()
-    ns.velocity_i = 'vbasis_n ?velocity_ni'
+    ns.velocity_i = "vbasis_n ?velocity_ni"
     ns.k = 0.1  # thermal diffusivity
     ns.xblob = 1, 1
-    ns.uinit = '.5 - .5 tanh(((x_i - xblob_i) (x_i - xblob_i) - .5) / .1)'
+    ns.uinit = ".5 - .5 tanh(((x_i - xblob_i) (x_i - xblob_i) - .5) / .1)"
 
     # define the weak form
-    res = gauss.integral('(basis_n (dudt + (velocity_i u)_,i) + k basis_n,i u_,i) d:x' @ ns)
+    res = gauss.integral("(basis_n (dudt + (velocity_i u)_,i) + k basis_n,i u_,i) d:x" @ ns)
 
     # define Dirichlet boundary condition
-    sqr = domain.boundary['inflow'].integral('u^2 d:x' @ ns, degree=2)
-    cons = solver.optimize('lhs', sqr, droptol=1e-15)
+    sqr = domain.boundary["inflow"].integral("u^2 d:x" @ ns, degree=2)
+    cons = solver.optimize("lhs", sqr, droptol=1e-15)
 
     # preCICE setup
     interface = precice.Interface("Transport", "../precice-config.xml", 0, 1)
@@ -62,18 +63,17 @@ def main():
     dt = 0.005
 
     # set u = uwall as initial condition and visualize
-    sqr = domain.integral('(u - uinit)^2' @ ns, degree=2)
-    lhs0 = solver.optimize('lhs', sqr)
-
+    sqr = domain.integral("(u - uinit)^2" @ ns, degree=2)
+    lhs0 = solver.optimize("lhs", sqr)
 
     while interface.is_coupling_ongoing():
 
         if timestep % 1 == 0:  # visualize
-            bezier = domain.sample('bezier', 2)
-            x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs0)
+            bezier = domain.sample("bezier", 2)
+            x, u = bezier.eval(["x_i", "u"] @ ns, lhs=lhs0)
             with log.add(log.DataLog()):
-                export.vtk('Transport_' + str(timestep), bezier.tri, x, T=u)
-        
+                export.vtk("Transport_" + str(timestep), bezier.tri, x, T=u)
+
         # read temperature from interface
         if interface.is_read_data_available():
             velocity_values = interface.read_block_vector_data(velocity_id, vertex_ids)
@@ -82,7 +82,9 @@ def main():
         dt = min(dt, precice_dt)
 
         # solve nutils timestep
-        lhs = solver.solve_linear('lhs', res, constrain=cons, arguments=dict(lhs0=lhs0, dt=dt, velocity=velocity_values))
+        lhs = solver.solve_linear(
+            "lhs", res, constrain=cons, arguments=dict(lhs0=lhs0, dt=dt, velocity=velocity_values)
+        )
 
         # do the coupling
         precice_dt = interface.advance(dt)
@@ -94,5 +96,5 @@ def main():
     interface.finalize()
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     cli.run(main)

From 9bcfce584f51698835c0a713678831a0d92af404 Mon Sep 17 00:00:00 2001
From: Benjamin Uekermann <benjamin.uekermann@ipvs.uni-stuttgart.de>
Date: Thu, 17 Mar 2022 22:10:33 +0100
Subject: [PATCH 06/22] Tweak code comments

---
 channel-transport/fluid-nutils/fluid.py         |  9 +++++----
 channel-transport/transport-nutils/transport.py | 11 ++++++-----
 2 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/channel-transport/fluid-nutils/fluid.py b/channel-transport/fluid-nutils/fluid.py
index 8761b58cb..d2978d736 100644
--- a/channel-transport/fluid-nutils/fluid.py
+++ b/channel-transport/fluid-nutils/fluid.py
@@ -11,19 +11,20 @@ def main():
 
     print("Running utils")
 
+    # define the Nutils mesh
     nx = 48
     ny = 16
     step_start = nx // 3
     step_end = nx // 2
     step_hight = ny // 2
 
-    # define the Nutils mesh
     grid = np.linspace(0, 6, nx + 1), np.linspace(0, 2, ny + 1)
     domain, geom = mesh.rectilinear(grid)
     domain = domain.withboundary(inflow="left", outflow="right", wall="top,bottom") - domain[
         step_start:step_end, :step_hight
     ].withboundary(wall="left,top,right")
 
+    # cloud of Gauss points
     gauss = domain.sample("gauss", degree=4)
 
     # Nutils namespace
@@ -39,7 +40,7 @@ def main():
     ns.σ_ij = "μ (u_i,j + u_j,i) - p δ_ij"
     ns.uin = "10 x_1 (2 - x_1)"  # inflow profile
 
-    # define the weak form
+    # define the weak form, Stokes problem
     ures = gauss.integral("ubasis_ni,j σ_ij d:x" @ ns)
     pres = gauss.integral("pbasis_n u_k,k d:x" @ ns)
 
@@ -68,7 +69,7 @@ def main():
 
     state = solver.solve_linear(("u", "p"), (ures, pres), constrain=cons)  # initial condition
 
-    # add convective term for navier-stokes
+    # add convective term and time derivative for Navier-Stokes
     ures += gauss.integral("ubasis_ni (dudt_i + μ (u_i u_j)_,j) d:x" @ ns)
 
     while interface.is_coupling_ongoing():
@@ -82,7 +83,7 @@ def main():
         # potentially adjust non-matching timestep sizes
         dt = min(dt, precice_dt)
 
-        # solve nutils timestep
+        # solve Nutils timestep
         state["u0"] = state["u"]
         state["dt"] = dt
         state = solver.newton(("u", "p"), (ures, pres), constrain=cons, arguments=state).solve(1e-10)
diff --git a/channel-transport/transport-nutils/transport.py b/channel-transport/transport-nutils/transport.py
index 507e6a0db..a8b3f1a8b 100644
--- a/channel-transport/transport-nutils/transport.py
+++ b/channel-transport/transport-nutils/transport.py
@@ -11,19 +11,20 @@ def main():
 
     print("Running utils")
 
+    # define the Nutils mesh
     nx = 120
     ny = 32
     step_start = nx // 3
     step_end = nx // 2
     step_hight = ny // 2
 
-    # define the Nutils mesh
     grid = np.linspace(0, 6, nx + 1), np.linspace(0, 2, ny + 1)
     domain, geom = mesh.rectilinear(grid)
     domain = domain.withboundary(inflow="left", outflow="right", wall="top,bottom") - domain[
         step_start:step_end, :step_hight
     ].withboundary(wall="left,top,right")
 
+    # cloud of Gauss points
     gauss = domain.sample("gauss", degree=2)
 
     # Nutils namespace
@@ -34,9 +35,9 @@ def main():
     ns.dudt = "basis_n (?lhs_n - ?lhs0_n) / ?dt"  # time derivative
     ns.vbasis = gauss.basis()
     ns.velocity_i = "vbasis_n ?velocity_ni"
-    ns.k = 0.1  # thermal diffusivity
+    ns.k = 0.1  # diffusivity
     ns.xblob = 1, 1
-    ns.uinit = ".5 - .5 tanh(((x_i - xblob_i) (x_i - xblob_i) - .5) / .1)"
+    ns.uinit = ".5 - .5 tanh(((x_i - xblob_i) (x_i - xblob_i) - .5) / .1)"  # blob
 
     # define the weak form
     res = gauss.integral("(basis_n (dudt + (velocity_i u)_,i) + k basis_n,i u_,i) d:x" @ ns)
@@ -62,7 +63,7 @@ def main():
     timestep = 0
     dt = 0.005
 
-    # set u = uwall as initial condition and visualize
+    # set blob as initial condition
     sqr = domain.integral("(u - uinit)^2" @ ns, degree=2)
     lhs0 = solver.optimize("lhs", sqr)
 
@@ -74,7 +75,7 @@ def main():
             with log.add(log.DataLog()):
                 export.vtk("Transport_" + str(timestep), bezier.tri, x, T=u)
 
-        # read temperature from interface
+        # read velocity values from interface
         if interface.is_read_data_available():
             velocity_values = interface.read_block_vector_data(velocity_id, vertex_ids)
 

From 2f63e13a1be3cd5b94f994b22158d250f47a8bac Mon Sep 17 00:00:00 2001
From: Benjamin Uekermann <benjamin.uekermann@ipvs.uni-stuttgart.de>
Date: Thu, 17 Mar 2022 22:18:18 +0100
Subject: [PATCH 07/22] Format config

---
 channel-transport/precice-config.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/channel-transport/precice-config.xml b/channel-transport/precice-config.xml
index 184a3d216..97b9736f5 100644
--- a/channel-transport/precice-config.xml
+++ b/channel-transport/precice-config.xml
@@ -23,7 +23,7 @@
       <use-mesh name="Transport-Mesh" from="Transport" />
       <write-data name="Velocity" mesh="Fluid-Mesh" />
       <mapping:rbf-compact-tps-c2
-	direction="write"
+        direction="write"
         support-radius="0.4"
         from="Fluid-Mesh"
         to="Transport-Mesh"

From 9e1e194bcbc64cd0a6ad681f8431e9a0eb46eeba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Simonis?= <simonisfrederic@gmail.com>
Date: Wed, 23 Mar 2022 11:42:58 +0100
Subject: [PATCH 08/22] Add animation of blob

---
 channel-transport/README.md                   |   4 ++++
 ...tutorials-channel-transport-animation.webp | Bin 0 -> 4937696 bytes
 2 files changed, 4 insertions(+)
 create mode 100644 channel-transport/images/tutorials-channel-transport-animation.webp

diff --git a/channel-transport/README.md b/channel-transport/README.md
index ca5ccd934..5db7e6112 100644
--- a/channel-transport/README.md
+++ b/channel-transport/README.md
@@ -15,6 +15,10 @@ We model a two-dimensional incompressible fluid flowing through a channel with a
 
 ![Flap setup](images/tutorials-channel-transport-physics.png)
 
+The behavior of the blob over the full 200 timesteps looks as follows:
+
+![Animation of blob over 200 timesteps](images/tutorials-channel-transport-animation.webp)
+
 ## Available solvers
 
 Fluid participant:
diff --git a/channel-transport/images/tutorials-channel-transport-animation.webp b/channel-transport/images/tutorials-channel-transport-animation.webp
new file mode 100644
index 0000000000000000000000000000000000000000..9639320234fe471c2ae0ddd7b61b9bfe7383533f
GIT binary patch

From b99cc3ea13037a395f642cc6ee782729b3501455 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Simonis?= <simonisfrederic@gmail.com>
Date: Wed, 23 Mar 2022 11:44:45 +0100
Subject: [PATCH 09/22] Add support for parallel cplscheme

---
 channel-transport/transport-nutils/transport.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/channel-transport/transport-nutils/transport.py b/channel-transport/transport-nutils/transport.py
index a8b3f1a8b..3fe816316 100644
--- a/channel-transport/transport-nutils/transport.py
+++ b/channel-transport/transport-nutils/transport.py
@@ -67,6 +67,9 @@ def main():
     sqr = domain.integral("(u - uinit)^2" @ ns, degree=2)
     lhs0 = solver.optimize("lhs", sqr)
 
+    # initialize the velocity values
+    velocity_values = interface.read_block_vector_data(velocity_id, vertex_ids)
+
     while interface.is_coupling_ongoing():
 
         if timestep % 1 == 0:  # visualize

From 6da057fd7e2f227e520649648c962751e10738af Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Simonis?= <simonisfrederic@gmail.com>
Date: Wed, 23 Mar 2022 12:12:17 +0100
Subject: [PATCH 10/22] Reduce animation size

---
 ...tutorials-channel-transport-animation.webp | Bin 4937696 -> 2180122 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/channel-transport/images/tutorials-channel-transport-animation.webp b/channel-transport/images/tutorials-channel-transport-animation.webp
index 9639320234fe471c2ae0ddd7b61b9bfe7383533f..ee9cb31c021a08c03cc9deadd4f657753bc1a8d2 100644
GIT binary patch

From a387a8b457834f1a8c6a3ffa74ea66f4aadfa5bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Simonis?= <simonisfrederic@gmail.com>
Date: Wed, 23 Mar 2022 12:14:23 +0100
Subject: [PATCH 11/22] Fix check-size for animated webp

---
 tools/check-size.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/check-size.sh b/tools/check-size.sh
index e6d071382..af3f76fd1 100755
--- a/tools/check-size.sh
+++ b/tools/check-size.sh
@@ -20,7 +20,7 @@ for tutorial in $tutorials; do
   for img in $images; do
     actualsize=$(du -k "$img" | cut -f 1)
     # Check gifs
-    if [[ "${img}" == *.gif ]]; then
+    if [[ "${img}" == *.gif || "${img}" == *.webp ]]; then
       if [ "${actualsize}" -ge "${MAXIMUMGIFSIZE}" ]; then
           echo -e "$img:$RED $actualsize kb exceeds the limit of $MAXIMUMGIFSIZE kb. $NOCOLOR"
           CODE=1

From a7fd6b17a495596aaf5e73773c508ca384424763 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Simonis?= <simonisfrederic@gmail.com>
Date: Wed, 23 Mar 2022 15:08:59 +0100
Subject: [PATCH 12/22] Initialize velocity_values to 0

Co-authored-by: Benjamin Uekermann <benjamin.uekermann@gmail.com>
---
 channel-transport/transport-nutils/transport.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/channel-transport/transport-nutils/transport.py b/channel-transport/transport-nutils/transport.py
index 3fe816316..813569a5b 100644
--- a/channel-transport/transport-nutils/transport.py
+++ b/channel-transport/transport-nutils/transport.py
@@ -68,7 +68,7 @@ def main():
     lhs0 = solver.optimize("lhs", sqr)
 
     # initialize the velocity values
-    velocity_values = interface.read_block_vector_data(velocity_id, vertex_ids)
+    velocity_values = np.zeros_like(vertices)
 
     while interface.is_coupling_ongoing():
 

From f9fa573457894fd84c1f8fa2fa5124ef78ade1ba Mon Sep 17 00:00:00 2001
From: Benjamin Uekermann <benjamin.uekermann@gmail.com>
Date: Mon, 2 May 2022 16:21:45 +0200
Subject: [PATCH 13/22] Update channel-transport/README.md

Co-authored-by: Gerasimos Chourdakis <chourdak@in.tum.de>
---
 channel-transport/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/channel-transport/README.md b/channel-transport/README.md
index 5db7e6112..9bed0a514 100644
--- a/channel-transport/README.md
+++ b/channel-transport/README.md
@@ -29,7 +29,7 @@ Transport participant:
 
 * Nutils. For more information, have a look at the [Nutils adapter documentation](https://www.precice.org/adapter-nutils.html). This Nutils solver requires at least Nutils v7.0.
 
-## Running the Simulation
+## Running the simulation
 
 Open two separate terminals and start one fluid and one transport participant by calling the respective run scripts `run.sh` located in the participant directory. For example:
 

From 43f32f7c79f58a05092bd1f38354d1938e4b9311 Mon Sep 17 00:00:00 2001
From: Benjamin Uekermann <benjamin.uekermann@gmail.com>
Date: Mon, 2 May 2022 16:22:22 +0200
Subject: [PATCH 14/22] Update channel-transport/README.md

Co-authored-by: Gerasimos Chourdakis <chourdak@in.tum.de>
---
 channel-transport/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/channel-transport/README.md b/channel-transport/README.md
index 9bed0a514..525792ebd 100644
--- a/channel-transport/README.md
+++ b/channel-transport/README.md
@@ -47,4 +47,4 @@ cd transport-nutils
 
 ## Post-processing
 
-All solvers generate `vtk` files which can visualized using, e.g., ParaView.
+All solvers generate `vtk` files which can be visualized using, e.g., ParaView.

From a94ef8d6f482aac00409493298d95eda32a72417 Mon Sep 17 00:00:00 2001
From: Benjamin Uekermann <benjamin.uekermann@ipvs.uni-stuttgart.de>
Date: Mon, 2 May 2022 18:57:43 +0200
Subject: [PATCH 15/22] Fix copy-paste leftovers in README

---
 channel-transport/README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/channel-transport/README.md b/channel-transport/README.md
index 525792ebd..2f7a2e526 100644
--- a/channel-transport/README.md
+++ b/channel-transport/README.md
@@ -1,8 +1,8 @@
 ---
 title: Channel transport
 permalink: tutorials-channel-transport.html
-keywords: fluid-structure interaction, FSI, OpenFOAM, FEniCS, Nutils, deal.II, Calculix, SU2,
-summary: This testcase shows how to use preCICE for simple volume-coupled problems.
+keywords: volume coupling, chemistry, OpenFOAM, Nutils, species, transport
+summary: A CFD problem is coupled to a transport (of, e.g., a chemistry species) in a uni-directional way.
 ---
 
 {% note %}

From d5a73aa27d755b8c6e8782605340c227ffa792bf Mon Sep 17 00:00:00 2001
From: Benjamin Uekermann <benjamin.uekermann@ipvs.uni-stuttgart.de>
Date: Mon, 2 May 2022 19:03:23 +0200
Subject: [PATCH 16/22] Clarify variable T in README

---
 channel-transport/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/channel-transport/README.md b/channel-transport/README.md
index 2f7a2e526..5b649f685 100644
--- a/channel-transport/README.md
+++ b/channel-transport/README.md
@@ -11,7 +11,7 @@ Get the [case files of this tutorial](https://github.com/precice/tutorials/tree/
 
 ## Setup
 
-We model a two-dimensional incompressible fluid flowing through a channel with an obstacle. The fluid problem is coupled to a simple transport problem in a uni-directional fashion. The transport problem is initialized with a circular blob close to the inflow. The convected and diffused blob after 23 timesteps:
+We model a two-dimensional incompressible fluid flowing through a channel with an obstacle. The fluid problem is coupled to a simple transport problem in a uni-directional fashion. The transport problem is initialized with a circular blob close to the inflow. The density of the species is denoted with `T` (like temperature). The convected and diffused blob after 23 timesteps:
 
 ![Flap setup](images/tutorials-channel-transport-physics.png)
 

From 0fd08ab2c3f79cf42d01037da4406b2f38fc1d33 Mon Sep 17 00:00:00 2001
From: Benjamin Uekermann <benjamin.uekermann@ipvs.uni-stuttgart.de>
Date: Mon, 2 May 2022 19:04:30 +0200
Subject: [PATCH 17/22] Fix language issue in README

---
 channel-transport/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/channel-transport/README.md b/channel-transport/README.md
index 5b649f685..0339aa9fd 100644
--- a/channel-transport/README.md
+++ b/channel-transport/README.md
@@ -31,7 +31,7 @@ Transport participant:
 
 ## Running the simulation
 
-Open two separate terminals and start one fluid and one transport participant by calling the respective run scripts `run.sh` located in the participant directory. For example:
+Open two separate terminals and start one fluid and one transport participant by calling the respective run scripts `run.sh` located in each of the participants' directory. For example:
 
 ```bash
 cd fluid-nutils

From bc1c68b94db4e3b47a8de64b77c72030323a4a4f Mon Sep 17 00:00:00 2001
From: Benjamin Uekermann <benjamin.uekermann@ipvs.uni-stuttgart.de>
Date: Mon, 2 May 2022 19:10:02 +0200
Subject: [PATCH 18/22] Add header documentation to both solver scripts

---
 channel-transport/fluid-nutils/fluid.py         | 4 ++++
 channel-transport/transport-nutils/transport.py | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/channel-transport/fluid-nutils/fluid.py b/channel-transport/fluid-nutils/fluid.py
index d2978d736..b6635864a 100644
--- a/channel-transport/fluid-nutils/fluid.py
+++ b/channel-transport/fluid-nutils/fluid.py
@@ -1,5 +1,9 @@
 #! /usr/bin/env python3
 
+#
+# Incompressible NSE solved within a channel geometry with parabolic inflow profile and an obstacle attached to the bottom towards the middle of the domain. The fluid field is initialized with a Stokes solution. The resulting velocity field is written to preCICE on the complete volume.
+#
+
 from nutils import function, mesh, cli, solver, export
 import treelog as log
 import numpy as np
diff --git a/channel-transport/transport-nutils/transport.py b/channel-transport/transport-nutils/transport.py
index 813569a5b..db7ad4427 100644
--- a/channel-transport/transport-nutils/transport.py
+++ b/channel-transport/transport-nutils/transport.py
@@ -1,5 +1,9 @@
 #! /usr/bin/env python3
 
+#
+# Advection-Diffusion equation for a single species with a velocity field read from preCICE on the complete volume. 
+#
+
 from nutils import function, mesh, cli, solver, export
 import treelog as log
 import numpy as np

From 3bd19c04f4dcef2997be062d707ed8b003507450 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Simonis?= <simonisfrederic@gmail.com>
Date: Thu, 5 May 2022 12:51:18 +0200
Subject: [PATCH 19/22] Add tool support for webm

---
 tools/check-size.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/check-size.sh b/tools/check-size.sh
index af3f76fd1..92cec1b54 100755
--- a/tools/check-size.sh
+++ b/tools/check-size.sh
@@ -20,7 +20,7 @@ for tutorial in $tutorials; do
   for img in $images; do
     actualsize=$(du -k "$img" | cut -f 1)
     # Check gifs
-    if [[ "${img}" == *.gif || "${img}" == *.webp ]]; then
+    if [[ "${img}" == *.gif || "${img}" == *.webp || "${img}" == *.webm ]]; then
       if [ "${actualsize}" -ge "${MAXIMUMGIFSIZE}" ]; then
           echo -e "$img:$RED $actualsize kb exceeds the limit of $MAXIMUMGIFSIZE kb. $NOCOLOR"
           CODE=1

From 87ae4f33d0270cd90e5222edf28bebc5237612fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Simonis?= <simonisfrederic@gmail.com>
Date: Thu, 5 May 2022 12:53:57 +0200
Subject: [PATCH 20/22] Replace webp by webm

---
 channel-transport/README.md                   |   2 +-
 ...tutorials-channel-transport-animation.webm | Bin 0 -> 755973 bytes
 ...tutorials-channel-transport-animation.webp | Bin 2180122 -> 0 bytes
 3 files changed, 1 insertion(+), 1 deletion(-)
 create mode 100644 channel-transport/images/tutorials-channel-transport-animation.webm
 delete mode 100644 channel-transport/images/tutorials-channel-transport-animation.webp

diff --git a/channel-transport/README.md b/channel-transport/README.md
index 0339aa9fd..daf56ef94 100644
--- a/channel-transport/README.md
+++ b/channel-transport/README.md
@@ -17,7 +17,7 @@ We model a two-dimensional incompressible fluid flowing through a channel with a
 
 The behavior of the blob over the full 200 timesteps looks as follows:
 
-![Animation of blob over 200 timesteps](images/tutorials-channel-transport-animation.webp)
+![Animation of blob over 200 timesteps](images/tutorials-channel-transport-animation.webm)
 
 ## Available solvers
 
diff --git a/channel-transport/images/tutorials-channel-transport-animation.webm b/channel-transport/images/tutorials-channel-transport-animation.webm
new file mode 100644
index 0000000000000000000000000000000000000000..f5596bc1c6b35dfb6984ea989c0ea428f6613066
GIT binary patch
diff --git a/channel-transport/images/tutorials-channel-transport-animation.webp b/channel-transport/images/tutorials-channel-transport-animation.webp
deleted file mode 100644
index ee9cb31c021a08c03cc9deadd4f657753bc1a8d2..0000000000000000000000000000000000000000
GIT binary patch

From 3ea21373c7efc0876b23f54050d24271c1add2ba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Simonis?= <simonisfrederic@gmail.com>
Date: Thu, 5 May 2022 13:20:08 +0200
Subject: [PATCH 21/22] Fix embedded webm

---
 channel-transport/README.md | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/channel-transport/README.md b/channel-transport/README.md
index daf56ef94..10c150b20 100644
--- a/channel-transport/README.md
+++ b/channel-transport/README.md
@@ -17,8 +17,11 @@ We model a two-dimensional incompressible fluid flowing through a channel with a
 
 The behavior of the blob over the full 200 timesteps looks as follows:
 
-![Animation of blob over 200 timesteps](images/tutorials-channel-transport-animation.webm)
-
+<video autoplay>
+  <source src="images/tutorials-channel-transport-animation.webm" type="video/webm">
+  Animation of blob over 200 timesteps.
+</video>
+  
 ## Available solvers
 
 Fluid participant:

From c471ced3de1b2ba30ae923db8c39392a27b7f8c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Simonis?= <simonisfrederic@gmail.com>
Date: Thu, 5 May 2022 13:39:16 +0200
Subject: [PATCH 22/22] Fix webm

---
 channel-transport/README.md                   |   2 +-
 ...tutorials-channel-transport-animation.webm | Bin 755973 -> 780020 bytes
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/channel-transport/README.md b/channel-transport/README.md
index 10c150b20..7a7d8e5ff 100644
--- a/channel-transport/README.md
+++ b/channel-transport/README.md
@@ -17,7 +17,7 @@ We model a two-dimensional incompressible fluid flowing through a channel with a
 
 The behavior of the blob over the full 200 timesteps looks as follows:
 
-<video autoplay>
+<video autoplay loop>
   <source src="images/tutorials-channel-transport-animation.webm" type="video/webm">
   Animation of blob over 200 timesteps.
 </video>
diff --git a/channel-transport/images/tutorials-channel-transport-animation.webm b/channel-transport/images/tutorials-channel-transport-animation.webm
index f5596bc1c6b35dfb6984ea989c0ea428f6613066..f4c846ece25c1f6c09aff2a9ec9798a86331cc2a 100644
GIT binary patch
