Skip to content

Split partitioned-heat-conduction case into complex and simple one #148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Mar 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c05a2e8
Strip case of everything besides simple heat conduction setup.
BenjaminRodenberg Jan 20, 2021
c41013e
Adds precice-config.xml supporting parallel runs.
BenjaminRodenberg Jan 20, 2021
71fcc28
Merge branch 'restructure' into i_137
BenjaminRodenberg Feb 3, 2021
51eb727
Add documentation and nutils case.
BenjaminRodenberg Feb 3, 2021
91510da
Add complex case. Based on ae77ae5d012e6652a4249bbecc820fbb2211ce23.
BenjaminRodenberg Feb 3, 2021
1ca266d
Cleanup and Update to newest version of python bindings.
BenjaminRodenberg Feb 3, 2021
0551635
Use scalar-valued flux.
BenjaminRodenberg Feb 3, 2021
f410eec
Apply new structure to complex case.
BenjaminRodenberg Feb 3, 2021
6aacd46
Remove subcycling.
BenjaminRodenberg Feb 3, 2021
77bf103
Simplify heat nutils to one mesh, make config consistent
uekerman Feb 3, 2021
8dd4258
Fix names.
BenjaminRodenberg Feb 3, 2021
d2e726c
BCs working.
BenjaminRodenberg Feb 3, 2021
0bcf866
Minor restructuring
BenjaminRodenberg Feb 3, 2021
679fe8d
Fix initial condition and output.
BenjaminRodenberg Feb 3, 2021
f80f8fd
Mark nutils case as under construction. See #152.
BenjaminRodenberg Feb 5, 2021
35dc013
Add link to Nutils
BenjaminRodenberg Feb 12, 2021
1d01384
Update partitioned-heat-conduction-complex/precice-config.xml
BenjaminRodenberg Feb 19, 2021
4acdad4
Update partitioned-heat-conduction-complex/precice-config.xml
BenjaminRodenberg Feb 19, 2021
4afe280
Update partitioned-heat-conduction-complex/precice-config.xml
BenjaminRodenberg Feb 19, 2021
97cf386
Update partitioned-heat-conduction-complex/precice-config.xml
BenjaminRodenberg Feb 19, 2021
e1d1411
Add note for website.
BenjaminRodenberg Feb 19, 2021
75e68af
Provide more details and use x_c = 1
BenjaminRodenberg Feb 19, 2021
2ad8b53
Cleanup for consistency with non-complex case.
BenjaminRodenberg Feb 19, 2021
9db75e5
Fix formatting
BenjaminRodenberg Feb 19, 2021
69c0c46
Cleanup w.r.t visualization
BenjaminRodenberg Feb 19, 2021
cf11057
Merge branch 'i_137' of github.com:precice/tutorials into i_137
BenjaminRodenberg Feb 19, 2021
bf765be
Add summary.
BenjaminRodenberg Feb 19, 2021
9614c02
Remove FEniCS from setup.
BenjaminRodenberg Feb 19, 2021
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
27 changes: 27 additions & 0 deletions partitioned-heat-conduction-complex/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
title: Partitioned heat conduction (complex setup)
permalink: tutorials-partitioned-heat-conduction-complex.html
keywords: FEniCS, Heat conduction
summary: This tutorial is the advanced version of the `partitioned-heat-conduction` tutorial. More advanced features and geometries may be used.
---

{% include important.html content="We have not yet ported the documentation of the preCICE tutorials from the preCICE wiki to here. Please go to the [preCICE wiki](https://github.com/precice/precice/wiki#2-getting-started---tutorials)" %}

## Setup

This case is an advanced version of `partitioned-heat-conduction`. Some advanced features offered by this case:

* Geometries may be chosen arbitrarily. One possibility is to use a circle and a rectangular plate with a hole, but you can also provide your own geometry, if you want.
* You may combine arbitrary mesh resolutions at the coupling interface.
* Nearest projection mapping is used.
* The Dirichlet and Neumann participants may be swapped arbitrarily.
* The exchanged temperature is still scalar valued, but the heat flux is vector valued.
* You can decide to use a time dependent heat flux and right-hand side to make the problem more challenging.

## Available solvers and dependencies

See `partitioned-heat-conduction`, only `fenics` is provided as a solver.

## Running the simulation

See `partitioned-heat-conduction`. The additional featured mentioned above can be activated via command line arguments. Please run `python3 fenics/heat.py --help` for a full list of provided arguments.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
File, solve, lhs, rhs, grad, inner, dot, dx, ds, interpolate, VectorFunctionSpace, MeshFunction, MPI
from fenicsprecice import Adapter
from errorcomputation import compute_errors
from my_enums import ProblemType, Subcycling, DomainPart
from my_enums import ProblemType, DomainPart
import argparse
import numpy as np
from problem_setup import get_geometry, get_problem_setup
Expand All @@ -53,9 +53,12 @@ def determine_gradient(V_g, u, flux):
solve(a == L, flux)


parser = argparse.ArgumentParser(description='Solving heat equation for simple or complex interface case')
parser.add_argument("-d", "--dirichlet", help="create a dirichlet problem", dest='dirichlet', action='store_true')
parser.add_argument("-n", "--neumann", help="create a neumann problem", dest='neumann', action='store_true')
parser = argparse.ArgumentParser(
description='Solving heat equation for simple or complex interface case')
parser.add_argument("-d", "--dirichlet", help="create a dirichlet problem",
dest='dirichlet', action='store_true')
parser.add_argument("-n", "--neumann", help="create a neumann problem",
dest='neumann', action='store_true')
parser.add_argument("-g", "--gamma", help="parameter gamma to set temporal dependence of heat flux", default=0.0,
type=float)
parser.add_argument("-a", "--arbitrary-coupling-interface",
Expand All @@ -69,32 +72,9 @@ def determine_gradient(V_g, u, flux):

args = parser.parse_args()

subcycle = Subcycling.NONE

fenics_dt = None
error_tol = None

# for all scenarios, we assume precice_dt == .1
if subcycle is Subcycling.NONE and not args.arbitrary_coupling_interface:
fenics_dt = .1 # time step size
error_tol = 10 ** -6 # Error is bounded by coupling accuracy. In theory we would obtain the analytical solution.
print("Subcycling = NO, Arbitrary coupling interface = NO, error tolerance = {}".format(error_tol))
elif subcycle is Subcycling.NONE and args.arbitrary_coupling_interface:
fenics_dt = .1 # time step size
error_tol = 10 ** -3 # error low, if we do not subcycle. In theory we would obtain the analytical solution.
# TODO For reasons, why we currently still have a relatively high error, see milestone https://github.com/precice/fenics-adapter/milestone/1
print("Subcycling = NO, Arbitrary coupling interface = YES, error tolerance = {}".format(error_tol))
elif subcycle is Subcycling.MATCHING:
fenics_dt = .01 # time step size
error_tol = 10 ** -2 # error increases. If we use subcycling, we cannot assume that we still get the exact solution.
# TODO Using waveform relaxation, we should be able to obtain the exact solution here, as well.
print("Subcycling = YES, Matching. error tolerance = {}".format(error_tol))
elif subcycle is Subcycling.NONMATCHING:
fenics_dt = .03 # time step size
error_tol = 10 ** -1 # error increases. If we use subcycling, we cannot assume that we still get the exact solution.
# TODO Using waveform relaxation, we should be able to obtain the exact solution here, as well.
print("Subcycling = YES, Non-matching. error tolerance = {}".format(error_tol))

fenics_dt = .1
# Error is bounded by coupling accuracy. In theory we can obtain the analytical solution.
error_tol = 10 ** -6
alpha = 3 # parameter alpha
beta = 1.3 # parameter beta
gamma = args.gamma # parameter gamma, dependence of heat flux on time
Expand All @@ -112,7 +92,8 @@ def determine_gradient(V_g, u, flux):
beta=beta, gamma=gamma, t=0)
u_D_function = interpolate(u_D, V)
# Define flux in x direction on coupling interface (grad(u_D) in normal direction)
f_N = Expression(("2 * gamma*t*x[0] + 2 * (1-gamma)*x[0]", "2 * alpha*x[1]"), degree=1, gamma=gamma, alpha=alpha, t=0)
f_N = Expression(("2 * gamma*t*x[0] + 2 * (1-gamma)*x[0]",
"2 * alpha*x[1]"), degree=1, gamma=gamma, alpha=alpha, t=0)
f_N_function = interpolate(f_N, V_g)

# Define initial value
Expand All @@ -124,10 +105,12 @@ def determine_gradient(V_g, u, flux):
# Initialize the adapter according to the specific participant
if problem is ProblemType.DIRICHLET:
precice = Adapter(adapter_config_filename="precice-adapter-config-D.json")
precice_dt = precice.initialize(coupling_boundary, read_function_space=V, write_object=f_N_function)
precice_dt = precice.initialize(
coupling_boundary, read_function_space=V, write_object=f_N_function)
elif problem is ProblemType.NEUMANN:
precice = Adapter(adapter_config_filename="precice-adapter-config-N.json")
precice_dt = precice.initialize(coupling_boundary, read_function_space=V_g, write_object=u_D_function)
precice_dt = precice.initialize(
coupling_boundary, read_function_space=V_g, write_object=u_D_function)

boundary_marker = False

Expand All @@ -152,7 +135,8 @@ def determine_gradient(V_g, u, flux):
# modify Neumann boundary condition on coupling interface, modify weak form correspondingly
if not boundary_marker: # there is only 1 Neumann-BC which is at the coupling boundary -> integration over whole boundary
if coupling_expression.is_scalar_valued():
F += v * coupling_expression * dolfin.ds # this term has to be added to weak form to add a Neumann BC (see e.g. p. 83ff Langtangen, Hans Petter, and Anders Logg. "Solving PDEs in Python The FEniCS Tutorial Volume I." (2016).)
# this term has to be added to weak form to add a Neumann BC (see e.g. p. 83ff Langtangen, Hans Petter, and Anders Logg. "Solving PDEs in Python The FEniCS Tutorial Volume I." (2016).)
F += v * coupling_expression * dolfin.ds
elif coupling_expression.is_vector_valued():
normal = FacetNormal(mesh)
F += -v * dot(normal, coupling_expression) * dolfin.ds
Expand Down Expand Up @@ -199,15 +183,17 @@ def determine_gradient(V_g, u, flux):
error_out << error_pointwise

# set t_1 = t_0 + dt, this gives u_D^1
u_D.t = t + dt(0) # call dt(0) to evaluate FEniCS Constant. Todo: is there a better way?
# call dt(0) to evaluate FEniCS Constant. Todo: is there a better way?
u_D.t = t + dt(0)
f.t = t + dt(0)

flux = Function(V_g)
flux.rename("Flux", "")

while precice.is_coupling_ongoing():

if precice.is_action_required(precice.action_write_iteration_checkpoint()): # write checkpoint
# write checkpoint
if precice.is_action_required(precice.action_write_iteration_checkpoint()):
precice.store_checkpoint(u_n, t, n)

read_data = precice.read_data()
Expand Down Expand Up @@ -235,7 +221,8 @@ def determine_gradient(V_g, u, flux):

precice_dt = precice.advance(dt(0))

if precice.is_action_required(precice.action_read_iteration_checkpoint()): # roll back to checkpoint
# roll back to checkpoint
if precice.is_action_required(precice.action_read_iteration_checkpoint()):
u_cp, t_cp, n_cp = precice.retrieve_checkpoint()
u_n.assign(u_cp)
t = t_cp
Expand All @@ -248,7 +235,8 @@ def determine_gradient(V_g, u, flux):
if precice.is_time_window_complete():
u_ref = interpolate(u_D, V)
u_ref.rename("reference", " ")
error, error_pointwise = compute_errors(u_n, u_ref, V, total_error_tol=error_tol)
error, error_pointwise = compute_errors(
u_n, u_ref, V, total_error_tol=error_tol)
print('n = %d, t = %.2f: L2 error on domain = %.3g' % (n, t, error))
# output solution and reference solution at t_n+1
print('output u^%d and u_ref^%d' % (n, n))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from enum import Enum


class ProblemType(Enum):
"""
Enum defines problem type. Details see above.
Expand All @@ -16,15 +17,3 @@ class DomainPart(Enum):
RIGHT = 2 # right part of domain in simple interface case
CIRCULAR = 3 # circular part of domain in complex interface case
RECTANGLE = 4 # domain excluding circular part of complex interface case


class Subcycling(Enum):
"""
Enum defines which kind of subcycling is used
"""
NONE = 0 # no subcycling, precice_dt == fenics_dt
MATCHING = 1 # subcycling, where fenics_dt fits into precice_dt, mod(precice_dt, fenics_dt) == 0
NONMATCHING = 2 # subcycling, where fenics_dt does not fit into precice_dt, mod(precice_dt, fenics_dt) != 0

# note: the modulo expressions above should be understood in an exact way (no floating point round off problems. For
# details, see https://stackoverflow.com/questions/14763722/python-modulo-on-floats)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"participant_name": "Dirichlet",
"config_file_name": "../precice-config.xml",
"interface": {
"coupling_mesh_name": "Dirichlet-Mesh",
"write_data_name": "Flux",
"read_data_name": "Temperature"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"participant_name": "Neumann",
"config_file_name": "../precice-config.xml",
"interface": {
"coupling_mesh_name": "Neumann-Mesh",
"write_data_name": "Temperature",
"read_data_name": "Flux"
}
}
61 changes: 61 additions & 0 deletions partitioned-heat-conduction-complex/precice-config.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?xml version="1.0"?>

<precice-configuration>

<log>
<sink filter="%Severity% > debug and %Rank% = 0" format="---[precice] %ColorizedSeverity% %Message%" enabled="true"/>
</log>

<solver-interface dimensions="2">

<data:scalar name="Temperature"/>
<data:vector name="Flux"/>

<mesh name="Dirichlet-Mesh">
<use-data name="Temperature"/>
<use-data name="Flux"/>
</mesh>

<mesh name="Neumann-Mesh">
<use-data name="Temperature"/>
<use-data name="Flux"/>
</mesh>

<participant name="Dirichlet">
<use-mesh name="Dirichlet-Mesh" provide="yes"/>
<use-mesh name="Neumann-Mesh" from="Neumann"/>
<write-data name="Flux" mesh="Dirichlet-Mesh"/>
<read-data name="Temperature" mesh="Dirichlet-Mesh"/>
<mapping:nearest-projection direction="read" from="Neumann-Mesh" to="Dirichlet-Mesh" constraint="consistent" />
</participant>

<participant name="Neumann">
<use-mesh name="Neumann-Mesh" provide="yes"/>
<use-mesh name="Dirichlet-Mesh" from="Dirichlet"/>
<write-data name="Temperature" mesh="Neumann-Mesh"/>
<read-data name="Flux" mesh="Neumann-Mesh"/>
<mapping:nearest-projection direction="read" from="Dirichlet-Mesh" to="Neumann-Mesh" constraint="consistent" />
</participant>

<m2n:sockets from="Dirichlet" to="Neumann" exchange-directory=".."/>

<coupling-scheme:serial-implicit>
<participants first="Dirichlet" second="Neumann"/>
<max-time value="1.0"/>
<time-window-size value="0.1"/>
<max-iterations value="100"/>
<exchange data="Flux" mesh="Dirichlet-Mesh" from="Dirichlet" to="Neumann" />
<exchange data="Temperature" mesh="Neumann-Mesh" from="Neumann" to="Dirichlet" initialize="true"/>
<relative-convergence-measure data="Flux" mesh="Dirichlet-Mesh" limit="1e-5"/>
<relative-convergence-measure data="Temperature" mesh="Neumann-Mesh" limit="1e-5"/>
<acceleration:IQN-ILS>
<data name="Temperature" mesh="Neumann-Mesh"/>
<initial-relaxation value="0.1"/>
<max-used-iterations value="10"/>
<time-windows-reused value="5"/>
<filter type="QR2" limit="1e-3"/>
</acceleration:IQN-ILS>
</coupling-scheme:serial-implicit>

</solver-interface>
</precice-configuration>
66 changes: 64 additions & 2 deletions partitioned-heat-conduction/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,70 @@
---
title: Partitioned heat conduction
permalink: tutorials-partitioned-heat-conduction.html
keywords:
summary:
keywords: FEniCS, Nutils, Heat conduction
summary: In this tutorial we solve a simple heat equation. The domain is partitioned and the coupling is established in a Dirichlet-Neumann fashion.
---

{% include important.html content="We have not yet ported the documentation of the preCICE tutorials from the preCICE wiki to here. Please go to the [preCICE wiki](https://github.com/precice/precice/wiki#2-getting-started---tutorials)" %}

## Setup

In this tutorial we solve a partitioned heat equation. For information on the non-partitioned case, please refer to [1, p.37ff]. In this tutorial the computational domain is partitioned and coupled via preCICE. The coupling roughly follows the approach described in [2].

![Case setup of partitioned-heat-conduction case](images/setup.png)

Case setup from [3]. `D` denotes the Dirichlet participant and `N` denotes the Neumann participant.

The heat equation is solved on a rectangular domain `Omega = [0,2] x [0,1]` with given Dirichlet boundary conditions. We split the domain at `x_c = 1` using a straight vertical line, the coupling interface. The left part of the domain will be referred to as the Dirichlet partition and the right part as the Neumann partition. To couple the two participant we use Dirichlet-Neumann coupling. Here, the Dirichlet participant receives Dirichlet boundary conditions (`Temperature`) at the coupling interface and solves the heat equation using these boundary conditions on the left part of the domain. Then the Dirichlet participant computes the resulting heat flux (`Flux`) from the solution and sends it to the Neumann participant. The Neumann participant uses the flux as a Neumann boundary condition to solve the heat equation on the right part of the domain. We then extract the temperature from the solution and send it back to the Dirichlet participant. This establishes the coupling between the two participants.

This simple case allows us to compare the solution for the partitioned case to a known analytical solution (method of manufactures solutions, see [1, p.37ff]). For more usage examples and details, please refer to [3, sect. 4.1].

## Available solvers and dependencies

You can either couple a solver with itself or different solvers with each other. In any case you will need to have preCICE and the python bindings installed on your system.

* `fenics`, requires you to install [FEniCS](https://fenicsproject.org/download/) and the [FEniCS-adapter](https://github.com/precice/fenics-adapter). The code is largely based on this [fenics-tutorial](https://github.com/hplgit/fenics-tutorial/blob/master/pub/python/vol1/ft03_heat.py) from [1].


* :construction: This case is still under construction. See https://github.com/precice/tutorials/issues/152. :construction: `nutils`, requires you to install [Nutils](http://www.nutils.org/en/latest/).

## Running the simulation

For choosing whether you want to run the Dirichlet-kind and a Neumann-kind participant, please provide the following commandline input:

* `-d` flag will enforce Dirichlet boundary conditions on the coupling interface.
* `-n` flag will enforce Neumann boundary conditions on the coupling interface.

For running the case, open two terminals and:

```
cd fenics
python3 heat.py -d
```

and

```
cd fenics
python3 heat.py -n
```

If you want to use Nutils for one or both sides of the setup, just `cd nutils`. The FEniCS case also supports parallel runs. Here, simply execute

```
mpirun -n <N_PROC> python3 heat.py -d
```

## Visualization

Output is written into the folder `fenics/out`. You can visualize the content with paraview by opening the `*.pvd` files. The files `Dirichlet.pvd` and `Neumann.pvd` correspond to the numerical solution of the Dirichlet, respectively Neumann, problem, while the files with the prefix `ref` correspond to the analytical reference solution, the files with `error` show the error and the files with `ranks` the ranks of the solvers (if executed in parallel).

![Animation of the partitioned heat equation](images/HT_FEniCS_movie.gif)

Visualization in paraview for `x_c = 1.5`.

## References

[1] Hans Petter Langtangen and Anders Logg. "Solving PDEs in Minutes-The FEniCS Tutorial Volume I." (2016). [pdf](https://fenicsproject.org/pub/tutorial/pdf/fenics-tutorial-vol1.pdf)
[2] Azahar Monge and Philipp Birken. "Convergence Analysis of the Dirichlet-Neumann Iteration for Finite Element Discretizations." (2016). Proceedings in Applied Mathematics and Mechanics. [doi](https://doi.org/10.1002/pamm.201610355)
[3] Benjamin Rüth, Benjamin Uekermann, Miriam Mehl, Philipp Birken, Azahar Monge, and Hans Joachim Bungartz. "Quasi-Newton waveform iteration for partitioned surface-coupled multiphysics applications." (2020). International Journal for Numerical Methods in Engineering. [doi](https://doi.org/10.1002/nme.6443)
3 changes: 0 additions & 3 deletions partitioned-heat-conduction/fenics-fenics/README.md

This file was deleted.

This file was deleted.

This file was deleted.

Loading