Skip to content

Commit cbbc826

Browse files
committed
Add perpendicular flap tutorial
as example on how to use this version of the adapter
1 parent 9a15064 commit cbbc826

File tree

1 file changed

+258
-0
lines changed
  • tutorial/perpendicular-flap/solid-fenicsx

1 file changed

+258
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
import os
2+
3+
import dolfinx as dfx
4+
import numpy as np
5+
import ufl
6+
from dolfinx.fem.petsc import (
7+
LinearProblem,
8+
apply_lifting,
9+
assemble_matrix_mat,
10+
assemble_vector,
11+
set_bc,
12+
)
13+
from dolfinx.mesh import CellType, create_rectangle
14+
from fenicsxprecice import Adapter
15+
from mpi4py import MPI
16+
from petsc4py import PETSc
17+
18+
# ------ #
19+
# SOLVER #
20+
# ------ #
21+
22+
# EXTEND the linear petsc.LinearProblem to add values into the bilinear form matrix on
23+
# desired DOFs. Wee need this due to the incoming Force are discrete vector, so wee need
24+
# to avoid domain integration
25+
class DiscreteLinearProblem(LinearProblem):
26+
def __init__(
27+
self,
28+
a,
29+
L,
30+
bcs,
31+
u,
32+
point_dofs,
33+
petsc_options=None,
34+
form_compiler_options=None,
35+
jit_options=None,
36+
):
37+
if point_dofs is not None:
38+
if not isinstance(type(point_dofs), np.ndarray):
39+
point_dofs = np.array([point_dofs], dtype="int32")
40+
self._point_dofs = point_dofs
41+
42+
super().__init__(a, L, bcs, u, petsc_options, form_compiler_options, jit_options)
43+
44+
def solve(self, values):
45+
"""Solve the problem."""
46+
# Assemble lhs
47+
self._A.zeroEntries()
48+
assemble_matrix_mat(self._A, self._a, bcs=self.bcs)
49+
self._A.assemble()
50+
51+
# Assemble rhs
52+
with self._b.localForm() as b_loc:
53+
b_loc.set(0)
54+
55+
if self._point_dofs is not None and values is not None:
56+
# apply load in dofs
57+
self._b[self._point_dofs] = values
58+
self._b.assemble()
59+
60+
assemble_vector(self._b, self._L)
61+
62+
# Apply boundary conditions to the rhs
63+
apply_lifting(self._b, [self._a], bcs=[self.bcs])
64+
self._b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE)
65+
set_bc(self._b, self.bcs)
66+
67+
# Solve linear system and update ghost values in the solution
68+
self._solver.solve(self._b, self._x)
69+
self.u.x.scatter_forward()
70+
71+
return self.u
72+
73+
74+
# --------- #
75+
# CONSTANTS #
76+
# --------- #
77+
78+
MPI_COMM = MPI.COMM_WORLD
79+
CURRENT_FOLDER = os.path.dirname(__file__)
80+
PARTICIPANT_CONFIG = os.path.join(CURRENT_FOLDER, "precice-config.json")
81+
RESULTS_DIR = os.path.join(CURRENT_FOLDER, "results")
82+
os.makedirs(RESULTS_DIR, exist_ok=True)
83+
os.chdir(CURRENT_FOLDER)
84+
85+
WRITER = dfx.io.VTKFile(MPI_COMM, f"{RESULTS_DIR}/result.pvd", "w")
86+
87+
WIDTH, HEIGHT = 0.1, 1
88+
NX, NY = 2, 15 * 8
89+
90+
E = 4000000.0
91+
NU = 0.3
92+
RHO = 3000.0
93+
94+
BETA_ = 0.25
95+
GAMMA_ = 0.5
96+
97+
# ------- #
98+
# MESHING #
99+
# ------- #
100+
101+
domain = create_rectangle(
102+
MPI_COMM,
103+
[np.array([-WIDTH / 2, 0]), np.array([WIDTH / 2, HEIGHT])],
104+
[NX, NY],
105+
cell_type=CellType.quadrilateral,
106+
)
107+
dim = domain.topology.dim
108+
109+
# -------------- #
110+
# Function Space #
111+
# -------------- #
112+
degree = 2
113+
shape = (dim,)
114+
V = dfx.fem.functionspace(domain, ("P", degree, shape))
115+
u = dfx.fem.Function(V, name="Displacement")
116+
f = dfx.fem.Function(V, name="Force")
117+
118+
# ------------------- #
119+
# Boundary conditions #
120+
# ------------------- #
121+
tol = 1e-14
122+
123+
124+
def clamped_boundary(x):
125+
return abs(x[1]) < tol
126+
127+
128+
def neumann_boundary(x):
129+
"""Determines whether a node is on the coupling boundary."""
130+
return np.logical_or((np.abs(x[1] - HEIGHT) < tol), np.abs(np.abs(x[0]) - WIDTH / 2) < tol)
131+
132+
133+
fixed_boundary = dfx.fem.locate_dofs_geometrical(V, clamped_boundary)
134+
coupling_boundary = dfx.mesh.locate_entities_boundary(domain, dim - 1, neumann_boundary)
135+
136+
bcs = [dfx.fem.dirichletbc(np.zeros((dim,)), fixed_boundary, V)]
137+
138+
# ------------ #
139+
# PRECICE INIT #
140+
# ------------ #
141+
participant = Adapter(MPI_COMM, PARTICIPANT_CONFIG)
142+
participant.initialize(coupling_boundary, V, u)
143+
dt = participant.dt
144+
145+
# ------------------------ #
146+
# linear elastic equations #
147+
# ------------------------ #
148+
E = dfx.fem.Constant(domain, E)
149+
nu = dfx.fem.Constant(domain, NU)
150+
rho = dfx.fem.Constant(domain, RHO)
151+
152+
lmbda = E * nu / (1 + nu) / (1 - 2 * nu)
153+
mu = E / 2 / (1 + nu)
154+
155+
156+
def epsilon(v):
157+
return ufl.sym(ufl.grad(v))
158+
159+
160+
def sigma(v):
161+
return lmbda * ufl.tr(epsilon(v)) * ufl.Identity(dim) + 2 * mu * epsilon(v)
162+
163+
164+
# ------------------- #
165+
# Time discretization #
166+
# ------------------- #
167+
# prev time step
168+
u_old = dfx.fem.Function(V)
169+
v_old = dfx.fem.Function(V)
170+
a_old = dfx.fem.Function(V)
171+
172+
# current time step
173+
a_new = dfx.fem.Function(V)
174+
v_new = dfx.fem.Function(V)
175+
176+
beta = dfx.fem.Constant(domain, BETA_)
177+
gamma = dfx.fem.Constant(domain, GAMMA_)
178+
179+
dx = ufl.Measure("dx", domain=domain)
180+
181+
a = (1 / (beta * dt**2)) * (u - u_old - dt * v_old) - ((1 - 2 * beta) / (2 * beta)) * a_old
182+
a_expr = dfx.fem.Expression(a, V.element.interpolation_points())
183+
184+
v = v_old + dt * ((1 - gamma) * a_old + gamma * a)
185+
v_expr = dfx.fem.Expression(v, V.element.interpolation_points())
186+
187+
# ------------------ #
188+
# mass, a stiffness #
189+
# ------------------ #
190+
u_ = ufl.TestFunction(V)
191+
du = ufl.TrialFunction(V)
192+
193+
194+
def mass(u, u_):
195+
return rho * ufl.dot(u, u_) * dx
196+
197+
198+
def stiffness(u, u_):
199+
return ufl.inner(sigma(u), epsilon(u_)) * dx
200+
201+
202+
Residual = mass(a, u_) + stiffness(u, u_)
203+
Residual_du = ufl.replace(Residual, {u: du})
204+
a_form = ufl.lhs(Residual_du)
205+
L_form = ufl.rhs(Residual_du)
206+
207+
208+
problem = DiscreteLinearProblem(
209+
a=a_form, L=L_form, u=u, bcs=bcs, point_dofs=participant.interface_dof
210+
)
211+
212+
213+
# parameters for Time-Stepping
214+
t = 0.0
215+
216+
while participant.is_coupling_ongoing():
217+
if participant.requires_writing_checkpoint(): # write checkpoint
218+
participant.store_checkpoint((u_old, v_old, a_old, t))
219+
220+
read_data = participant.read_data()
221+
222+
u = problem.solve(read_data)
223+
224+
# Write new displacements to preCICE
225+
participant.write_data(u)
226+
227+
# Call to advance coupling, also returns the optimum time step value
228+
participant.advance(dt)
229+
230+
# Either revert to old step if timestep has not converged or move to next timestep
231+
if participant.requires_reading_checkpoint():
232+
(
233+
u_cp,
234+
v_cp,
235+
a_cp,
236+
t_cp,
237+
) = participant.retrieve_checkpoint()
238+
u_old.vector.copy(u_cp.vector)
239+
v_old.vector.copy(v_cp.vector)
240+
a_old.vector.copy(a_cp.vector)
241+
t = t_cp
242+
else:
243+
v_new.interpolate(v_expr)
244+
a_new.interpolate(a_expr)
245+
u.vector.copy(u_old.vector)
246+
v_new.vector.copy(v_old.vector)
247+
a_new.vector.copy(a_old.vector)
248+
t += dt
249+
250+
if participant.is_time_window_complete():
251+
f.vector[participant.interface_dof] = read_data
252+
f.vector.assemble()
253+
WRITER.write_function(u, t)
254+
WRITER.write_function(f, t)
255+
WRITER.close()
256+
257+
WRITER.close()
258+
participant.finalize()

0 commit comments

Comments
 (0)