Skip to content

Commit caa5ad0

Browse files
Split partitioned-heat-conduction case into complex and simple one (#148)
* Strip case of everything besides simple heat conduction setup. * Adds precice-config.xml supporting parallel runs. * Add documentation and nutils case. * Add complex case. Based on ae77ae5. * Cleanup and Update to newest version of python bindings. * Use scalar-valued flux. * Apply new structure to complex case. * Remove subcycling. * Simplify heat nutils to one mesh, make config consistent * Fix names. * BCs working. * Minor restructuring * Fix initial condition and output. * Mark nutils case as under construction. See #152. * Add link to Nutils * Update partitioned-heat-conduction-complex/precice-config.xml Co-authored-by: Benjamin Uekermann <[email protected]> * Update partitioned-heat-conduction-complex/precice-config.xml Co-authored-by: Benjamin Uekermann <[email protected]> * Update partitioned-heat-conduction-complex/precice-config.xml Co-authored-by: Benjamin Uekermann <[email protected]> * Update partitioned-heat-conduction-complex/precice-config.xml Co-authored-by: Benjamin Uekermann <[email protected]> * Add note for website. * Provide more details and use x_c = 1 * Cleanup for consistency with non-complex case. * Fix formatting * Cleanup w.r.t visualization * Add summary. * Remove FEniCS from setup. Co-authored-by: uekerman <[email protected]>
1 parent 3ff76bc commit caa5ad0

26 files changed

+618
-134
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
title: Partitioned heat conduction (complex setup)
3+
permalink: tutorials-partitioned-heat-conduction-complex.html
4+
keywords: FEniCS, Heat conduction
5+
summary: This tutorial is the advanced version of the `partitioned-heat-conduction` tutorial. More advanced features and geometries may be used.
6+
---
7+
8+
{% 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)" %}
9+
10+
## Setup
11+
12+
This case is an advanced version of `partitioned-heat-conduction`. Some advanced features offered by this case:
13+
14+
* 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.
15+
* You may combine arbitrary mesh resolutions at the coupling interface.
16+
* Nearest projection mapping is used.
17+
* The Dirichlet and Neumann participants may be swapped arbitrarily.
18+
* The exchanged temperature is still scalar valued, but the heat flux is vector valued.
19+
* You can decide to use a time dependent heat flux and right-hand side to make the problem more challenging.
20+
21+
## Available solvers and dependencies
22+
23+
See `partitioned-heat-conduction`, only `fenics` is provided as a solver.
24+
25+
## Running the simulation
26+
27+
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.

partitioned-heat-conduction/fenics-fenics/heat.py renamed to partitioned-heat-conduction-complex/fenics/heat.py

Lines changed: 26 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
File, solve, lhs, rhs, grad, inner, dot, dx, ds, interpolate, VectorFunctionSpace, MeshFunction, MPI
3030
from fenicsprecice import Adapter
3131
from errorcomputation import compute_errors
32-
from my_enums import ProblemType, Subcycling, DomainPart
32+
from my_enums import ProblemType, DomainPart
3333
import argparse
3434
import numpy as np
3535
from problem_setup import get_geometry, get_problem_setup
@@ -53,9 +53,12 @@ def determine_gradient(V_g, u, flux):
5353
solve(a == L, flux)
5454

5555

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

7073
args = parser.parse_args()
7174

72-
subcycle = Subcycling.NONE
73-
74-
fenics_dt = None
75-
error_tol = None
76-
77-
# for all scenarios, we assume precice_dt == .1
78-
if subcycle is Subcycling.NONE and not args.arbitrary_coupling_interface:
79-
fenics_dt = .1 # time step size
80-
error_tol = 10 ** -6 # Error is bounded by coupling accuracy. In theory we would obtain the analytical solution.
81-
print("Subcycling = NO, Arbitrary coupling interface = NO, error tolerance = {}".format(error_tol))
82-
elif subcycle is Subcycling.NONE and args.arbitrary_coupling_interface:
83-
fenics_dt = .1 # time step size
84-
error_tol = 10 ** -3 # error low, if we do not subcycle. In theory we would obtain the analytical solution.
85-
# TODO For reasons, why we currently still have a relatively high error, see milestone https://github.com/precice/fenics-adapter/milestone/1
86-
print("Subcycling = NO, Arbitrary coupling interface = YES, error tolerance = {}".format(error_tol))
87-
elif subcycle is Subcycling.MATCHING:
88-
fenics_dt = .01 # time step size
89-
error_tol = 10 ** -2 # error increases. If we use subcycling, we cannot assume that we still get the exact solution.
90-
# TODO Using waveform relaxation, we should be able to obtain the exact solution here, as well.
91-
print("Subcycling = YES, Matching. error tolerance = {}".format(error_tol))
92-
elif subcycle is Subcycling.NONMATCHING:
93-
fenics_dt = .03 # time step size
94-
error_tol = 10 ** -1 # error increases. If we use subcycling, we cannot assume that we still get the exact solution.
95-
# TODO Using waveform relaxation, we should be able to obtain the exact solution here, as well.
96-
print("Subcycling = YES, Non-matching. error tolerance = {}".format(error_tol))
97-
75+
fenics_dt = .1
76+
# Error is bounded by coupling accuracy. In theory we can obtain the analytical solution.
77+
error_tol = 10 ** -6
9878
alpha = 3 # parameter alpha
9979
beta = 1.3 # parameter beta
10080
gamma = args.gamma # parameter gamma, dependence of heat flux on time
@@ -112,7 +92,8 @@ def determine_gradient(V_g, u, flux):
11292
beta=beta, gamma=gamma, t=0)
11393
u_D_function = interpolate(u_D, V)
11494
# Define flux in x direction on coupling interface (grad(u_D) in normal direction)
115-
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)
95+
f_N = Expression(("2 * gamma*t*x[0] + 2 * (1-gamma)*x[0]",
96+
"2 * alpha*x[1]"), degree=1, gamma=gamma, alpha=alpha, t=0)
11697
f_N_function = interpolate(f_N, V_g)
11798

11899
# Define initial value
@@ -124,10 +105,12 @@ def determine_gradient(V_g, u, flux):
124105
# Initialize the adapter according to the specific participant
125106
if problem is ProblemType.DIRICHLET:
126107
precice = Adapter(adapter_config_filename="precice-adapter-config-D.json")
127-
precice_dt = precice.initialize(coupling_boundary, read_function_space=V, write_object=f_N_function)
108+
precice_dt = precice.initialize(
109+
coupling_boundary, read_function_space=V, write_object=f_N_function)
128110
elif problem is ProblemType.NEUMANN:
129111
precice = Adapter(adapter_config_filename="precice-adapter-config-N.json")
130-
precice_dt = precice.initialize(coupling_boundary, read_function_space=V_g, write_object=u_D_function)
112+
precice_dt = precice.initialize(
113+
coupling_boundary, read_function_space=V_g, write_object=u_D_function)
131114

132115
boundary_marker = False
133116

@@ -152,7 +135,8 @@ def determine_gradient(V_g, u, flux):
152135
# modify Neumann boundary condition on coupling interface, modify weak form correspondingly
153136
if not boundary_marker: # there is only 1 Neumann-BC which is at the coupling boundary -> integration over whole boundary
154137
if coupling_expression.is_scalar_valued():
155-
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).)
138+
# 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).)
139+
F += v * coupling_expression * dolfin.ds
156140
elif coupling_expression.is_vector_valued():
157141
normal = FacetNormal(mesh)
158142
F += -v * dot(normal, coupling_expression) * dolfin.ds
@@ -199,15 +183,17 @@ def determine_gradient(V_g, u, flux):
199183
error_out << error_pointwise
200184

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

205190
flux = Function(V_g)
206191
flux.rename("Flux", "")
207192

208193
while precice.is_coupling_ongoing():
209194

210-
if precice.is_action_required(precice.action_write_iteration_checkpoint()): # write checkpoint
195+
# write checkpoint
196+
if precice.is_action_required(precice.action_write_iteration_checkpoint()):
211197
precice.store_checkpoint(u_n, t, n)
212198

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

236222
precice_dt = precice.advance(dt(0))
237223

238-
if precice.is_action_required(precice.action_read_iteration_checkpoint()): # roll back to checkpoint
224+
# roll back to checkpoint
225+
if precice.is_action_required(precice.action_read_iteration_checkpoint()):
239226
u_cp, t_cp, n_cp = precice.retrieve_checkpoint()
240227
u_n.assign(u_cp)
241228
t = t_cp
@@ -248,7 +235,8 @@ def determine_gradient(V_g, u, flux):
248235
if precice.is_time_window_complete():
249236
u_ref = interpolate(u_D, V)
250237
u_ref.rename("reference", " ")
251-
error, error_pointwise = compute_errors(u_n, u_ref, V, total_error_tol=error_tol)
238+
error, error_pointwise = compute_errors(
239+
u_n, u_ref, V, total_error_tol=error_tol)
252240
print('n = %d, t = %.2f: L2 error on domain = %.3g' % (n, t, error))
253241
# output solution and reference solution at t_n+1
254242
print('output u^%d and u_ref^%d' % (n, n))
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from enum import Enum
22

3+
34
class ProblemType(Enum):
45
"""
56
Enum defines problem type. Details see above.
@@ -16,15 +17,3 @@ class DomainPart(Enum):
1617
RIGHT = 2 # right part of domain in simple interface case
1718
CIRCULAR = 3 # circular part of domain in complex interface case
1819
RECTANGLE = 4 # domain excluding circular part of complex interface case
19-
20-
21-
class Subcycling(Enum):
22-
"""
23-
Enum defines which kind of subcycling is used
24-
"""
25-
NONE = 0 # no subcycling, precice_dt == fenics_dt
26-
MATCHING = 1 # subcycling, where fenics_dt fits into precice_dt, mod(precice_dt, fenics_dt) == 0
27-
NONMATCHING = 2 # subcycling, where fenics_dt does not fit into precice_dt, mod(precice_dt, fenics_dt) != 0
28-
29-
# note: the modulo expressions above should be understood in an exact way (no floating point round off problems. For
30-
# details, see https://stackoverflow.com/questions/14763722/python-modulo-on-floats)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"participant_name": "Dirichlet",
3+
"config_file_name": "../precice-config.xml",
4+
"interface": {
5+
"coupling_mesh_name": "Dirichlet-Mesh",
6+
"write_data_name": "Flux",
7+
"read_data_name": "Temperature"
8+
}
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"participant_name": "Neumann",
3+
"config_file_name": "../precice-config.xml",
4+
"interface": {
5+
"coupling_mesh_name": "Neumann-Mesh",
6+
"write_data_name": "Temperature",
7+
"read_data_name": "Flux"
8+
}
9+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?xml version="1.0"?>
2+
3+
<precice-configuration>
4+
5+
<log>
6+
<sink filter="%Severity% > debug and %Rank% = 0" format="---[precice] %ColorizedSeverity% %Message%" enabled="true"/>
7+
</log>
8+
9+
<solver-interface dimensions="2">
10+
11+
<data:scalar name="Temperature"/>
12+
<data:vector name="Flux"/>
13+
14+
<mesh name="Dirichlet-Mesh">
15+
<use-data name="Temperature"/>
16+
<use-data name="Flux"/>
17+
</mesh>
18+
19+
<mesh name="Neumann-Mesh">
20+
<use-data name="Temperature"/>
21+
<use-data name="Flux"/>
22+
</mesh>
23+
24+
<participant name="Dirichlet">
25+
<use-mesh name="Dirichlet-Mesh" provide="yes"/>
26+
<use-mesh name="Neumann-Mesh" from="Neumann"/>
27+
<write-data name="Flux" mesh="Dirichlet-Mesh"/>
28+
<read-data name="Temperature" mesh="Dirichlet-Mesh"/>
29+
<mapping:nearest-projection direction="read" from="Neumann-Mesh" to="Dirichlet-Mesh" constraint="consistent" />
30+
</participant>
31+
32+
<participant name="Neumann">
33+
<use-mesh name="Neumann-Mesh" provide="yes"/>
34+
<use-mesh name="Dirichlet-Mesh" from="Dirichlet"/>
35+
<write-data name="Temperature" mesh="Neumann-Mesh"/>
36+
<read-data name="Flux" mesh="Neumann-Mesh"/>
37+
<mapping:nearest-projection direction="read" from="Dirichlet-Mesh" to="Neumann-Mesh" constraint="consistent" />
38+
</participant>
39+
40+
<m2n:sockets from="Dirichlet" to="Neumann" exchange-directory=".."/>
41+
42+
<coupling-scheme:serial-implicit>
43+
<participants first="Dirichlet" second="Neumann"/>
44+
<max-time value="1.0"/>
45+
<time-window-size value="0.1"/>
46+
<max-iterations value="100"/>
47+
<exchange data="Flux" mesh="Dirichlet-Mesh" from="Dirichlet" to="Neumann" />
48+
<exchange data="Temperature" mesh="Neumann-Mesh" from="Neumann" to="Dirichlet" initialize="true"/>
49+
<relative-convergence-measure data="Flux" mesh="Dirichlet-Mesh" limit="1e-5"/>
50+
<relative-convergence-measure data="Temperature" mesh="Neumann-Mesh" limit="1e-5"/>
51+
<acceleration:IQN-ILS>
52+
<data name="Temperature" mesh="Neumann-Mesh"/>
53+
<initial-relaxation value="0.1"/>
54+
<max-used-iterations value="10"/>
55+
<time-windows-reused value="5"/>
56+
<filter type="QR2" limit="1e-3"/>
57+
</acceleration:IQN-ILS>
58+
</coupling-scheme:serial-implicit>
59+
60+
</solver-interface>
61+
</precice-configuration>

0 commit comments

Comments
 (0)