MFEM v4.8.0
Finite element discretization library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
multidomain_nd.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12// This miniapp is a variant of the multidomain miniapp which aims to extend
13// the demonstration given therein to PDEs involving H(curl) finite elements.
14//
15// A 3D domain comprised of an outer box with a cylinder shaped inside is used.
16//
17// A magnetic diffusion equation is described on the outer box domain
18//
19// dH/dt = -∇×(σ∇×H) in outer box
20// n×H = n×H_wall on outside wall
21// n×(σ∇×H) = 0 on inside (cylinder) wall
22//
23// with magnetic field H and coefficient σ (non-physical in this example).
24//
25// A convection-diffusion equation is described inside the cylinder domain
26//
27// dH/dt = -∇×(σ∇×H)+α∇×(v×H) in inner cylinder
28// n×H = n×H_wall on cylinder wall (obtained from
29// diffusion equation)
30// n×(σ∇×H) = 0 else
31//
32// with magnetic field H, coefficients σ, α, and prescribed velocity
33// profile v.
34//
35// To couple the solutions of both equations, a segregated solve with one way
36// coupling approach is used. The diffusion equation of the outer box is solved
37// from the timestep H_box(t) to H_box(t+dt). Then for the convection-diffusion
38// equation H_wall is set to H_box(t+dt) and the equation is solved for H(t+dt)
39// which results in a first-order one way coupling. It is important to note
40// that when using Nedelec basis functions, as in this example, only the
41// tangential portion of H is communicated between the two regions.
42
43#include "mfem.hpp"
44#include <fstream>
45#include <iostream>
46
47using namespace mfem;
48
49// Prescribed velocity profile for the convection-diffusion equation inside the
50// cylinder. The profile is constructed s.t. it approximates a no-slip (v=0)
51// directly at the cylinder wall boundary.
52void velocity_profile(const Vector &c, Vector &q)
53{
54 real_t A = 1.0;
55 real_t x = c(0);
56 real_t y = c(1);
57 real_t r = sqrt(pow(x, 2.0) + pow(y, 2.0));
58
59 q(0) = 0.0;
60 q(1) = 0.0;
61
62 if (std::abs(r) >= 0.25 - 1e-8)
63 {
64 q(2) = 0.0;
65 }
66 else
67 {
68 q(2) = A * exp(-(pow(x, 2.0) / 2.0 + pow(y, 2.0) / 2.0));
69 }
70}
71
72// A simple vector field which is everywhere parallel to the xy plane and
73// wraps around the domain in a counter-clockwise fashion.
74void square_xy(const Vector &p, Vector &v)
75{
76 v.SetSize(3);
77
78 v[0] = -2.0 * p[1];
79 v[1] = 2.0 * p[0];
80 v[2] = 0.0;
81}
82
83/**
84 * @brief Convection-diffusion time dependent operator for a vector field
85 *
86 * dH/dt = -∇ × σ ∇ × H + α ∇ ×(v × H)
87 *
88 * Can also be used to create a diffusion or convection only operator by setting
89 * α or σ to zero.
90 */
91class ConvectionDiffusionTDO : public TimeDependentOperator
92{
93public:
94 /**
95 * @brief Construct a new convection-diffusion time dependent operator.
96 *
97 * @param fes The ParFiniteElementSpace the solution is defined on
98 * @param ess_tdofs All essential true dofs in the Nedelec space
99 * @param alpha The convection coefficient
100 * @param kappa The diffusion coefficient
101 */
102 ConvectionDiffusionTDO(ParFiniteElementSpace &fes,
103 Array<int> ess_tdofs,
104 real_t alpha = 1.0,
105 real_t sigma = 1.0e-1)
106 : TimeDependentOperator(fes.GetTrueVSize()),
107 Mform(&fes),
108 Kform(&fes),
109 bform(&fes),
110 ess_tdofs_(ess_tdofs),
111 M_solver(fes.GetComm())
112 {
113 d = new ConstantCoefficient(-sigma);
116
118
120 Mform.Assemble(0);
121 Mform.Finalize();
122
125 Kform.Assemble(0);
126
127 Array<int> empty;
128 Kform.FormSystemMatrix(empty, K);
129 Mform.FormSystemMatrix(ess_tdofs_, M);
130
131 bform.Assemble();
132 b = bform.ParallelAssemble();
133
134 M_solver.iterative_mode = false;
135 M_solver.SetRelTol(1e-8);
136 M_solver.SetAbsTol(0.0);
137 M_solver.SetMaxIter(100);
138 M_solver.SetPrintLevel(0);
140 M_solver.SetPreconditioner(M_prec);
141 M_solver.SetOperator(*M);
142
143 t1.SetSize(height);
144 t2.SetSize(height);
145 }
146
147 void Mult(const Vector &u, Vector &du_dt) const override
148 {
149 K->Mult(u, t1);
150 t1.Add(1.0, *b);
151 M_solver.Mult(t1, du_dt);
152 du_dt.SetSubVector(ess_tdofs_, 0.0);
153 }
154
155 ~ConvectionDiffusionTDO() override
156 {
157 delete aq;
158 delete q;
159 delete d;
160 delete b;
161 }
162
163 /// Mass form
164 ParBilinearForm Mform;
165
166 /// Stiffness form. Might include diffusion, convection or both.
167 ParBilinearForm Kform;
168
169 /// Mass opeperator
171
172 /// Stiffness opeperator. Might include diffusion, convection or both.
174
175 /// RHS form
176 ParLinearForm bform;
177
178 /// RHS vector
179 Vector *b = nullptr;
180
181 /// Velocity coefficient
182 VectorCoefficient *q = nullptr;
183
184 /// alpha * Velocity coefficient
185 VectorCoefficient *aq = nullptr;
186
187 /// Diffusion coefficient
188 Coefficient *d = nullptr;
189
190 /// Essential true dof array. Relevant for eliminating boundary conditions
191 /// when using a Nedelec space.
192 Array<int> ess_tdofs_;
193
194 real_t current_dt = -1.0;
195
196 /// Mass matrix solver
197 CGSolver M_solver;
198
199 /// Mass matrix preconditioner
200 HypreSmoother M_prec;
201
202 /// Auxiliary vectors
203 mutable Vector t1, t2;
204};
205
206int main(int argc, char *argv[])
207{
208 Mpi::Init();
209 Hypre::Init();
210 int num_procs = Mpi::WorldSize();
211 int myid = Mpi::WorldRank();
212
213 int order = 2;
214 real_t t_final = 5.0;
215 real_t dt = 1.0e-5;
216 bool visualization = true;
217 int visport = 19916;
218 int vis_steps = 10;
219
220 OptionsParser args(argc, argv);
221 args.AddOption(&order, "-o", "--order",
222 "Finite element order (polynomial degree).");
223 args.AddOption(&t_final, "-tf", "--t-final",
224 "Final time; start time is 0.");
225 args.AddOption(&dt, "-dt", "--time-step",
226 "Time step.");
227 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
228 "--no-visualization",
229 "Enable or disable GLVis visualization.");
230 args.AddOption(&vis_steps, "-vs", "--visualization-steps",
231 "Visualize every n-th timestep.");
232 args.ParseCheck();
233
234 Mesh *serial_mesh = new Mesh("multidomain-hex.mesh");
235 ParMesh parent_mesh = ParMesh(MPI_COMM_WORLD, *serial_mesh);
236 delete serial_mesh;
237
238 parent_mesh.UniformRefinement();
239
240 ND_FECollection fec(order, parent_mesh.Dimension());
241
242 // Create the sub-domains and accompanying Finite Element spaces from
243 // corresponding attributes. This specific mesh has two domain attributes and
244 // 9 boundary attributes.
245 Array<int> cylinder_domain_attributes(1);
246 cylinder_domain_attributes[0] = 1;
247
248 auto cylinder_submesh =
249 ParSubMesh::CreateFromDomain(parent_mesh, cylinder_domain_attributes);
250
251 ParFiniteElementSpace fes_cylinder(&cylinder_submesh, &fec);
252
253 Array<int> inflow_attributes(cylinder_submesh.bdr_attributes.Max());
254 inflow_attributes = 0;
255 inflow_attributes[7] = 1;
256
257 Array<int> inner_cylinder_wall_attributes(
258 cylinder_submesh.bdr_attributes.Max());
259 inner_cylinder_wall_attributes = 0;
260 inner_cylinder_wall_attributes[8] = 1;
261
262 // For the convection-diffusion equation inside the cylinder domain, the
263 // inflow surface and outer wall are treated as Dirichlet boundary
264 // conditions.
265 Array<int> inflow_tdofs, interface_tdofs, ess_tdofs;
266 fes_cylinder.GetEssentialTrueDofs(inflow_attributes, inflow_tdofs);
267 fes_cylinder.GetEssentialTrueDofs(inner_cylinder_wall_attributes,
268 interface_tdofs);
269 ess_tdofs.Append(inflow_tdofs);
270 ess_tdofs.Append(interface_tdofs);
271 ess_tdofs.Sort();
272 ess_tdofs.Unique();
273 ConvectionDiffusionTDO cd_tdo(fes_cylinder, ess_tdofs);
274
275 ParGridFunction magnetic_field_cylinder_gf(&fes_cylinder);
276 magnetic_field_cylinder_gf = 0.0;
277
278 Vector magnetic_field_cylinder;
279 magnetic_field_cylinder_gf.GetTrueDofs(magnetic_field_cylinder);
280
281 RK3SSPSolver cd_ode_solver;
282 cd_ode_solver.Init(cd_tdo);
283
284 Array<int> outer_domain_attributes(1);
285 outer_domain_attributes[0] = 2;
286
287 auto block_submesh = ParSubMesh::CreateFromDomain(parent_mesh,
288 outer_domain_attributes);
289
290 ParFiniteElementSpace fes_block(&block_submesh, &fec);
291
292 Array<int> block_wall_attributes(block_submesh.bdr_attributes.Max());
293 block_wall_attributes = 0;
294 block_wall_attributes[0] = 1;
295 block_wall_attributes[1] = 1;
296 block_wall_attributes[2] = 1;
297 block_wall_attributes[3] = 1;
298
299 Array<int> outer_cylinder_wall_attributes(
300 block_submesh.bdr_attributes.Max());
301 outer_cylinder_wall_attributes = 0;
302 outer_cylinder_wall_attributes[8] = 1;
303
304 fes_block.GetEssentialTrueDofs(block_wall_attributes, ess_tdofs);
305
306 ConvectionDiffusionTDO d_tdo(fes_block, ess_tdofs, 0.0, 1.0);
307
308 ParGridFunction magnetic_field_block_gf(&fes_block);
309 magnetic_field_block_gf = 0.0;
310
312 magnetic_field_block_gf.ProjectBdrCoefficientTangent(one,
313 block_wall_attributes);
314
315 Vector magnetic_field_block;
316 magnetic_field_block_gf.GetTrueDofs(magnetic_field_block);
317
318 RK3SSPSolver d_ode_solver;
319 d_ode_solver.Init(d_tdo);
320
321 Array<int> cylinder_surface_attributes(1);
322 cylinder_surface_attributes[0] = 9;
323
324 auto cylinder_surface_submesh =
325 ParSubMesh::CreateFromBoundary(parent_mesh, cylinder_surface_attributes);
326
327 char vishost[] = "localhost";
328 socketstream cyl_sol_sock;
329 if (visualization)
330 {
331 cyl_sol_sock.open(vishost, visport);
332 cyl_sol_sock << "parallel " << num_procs << " " << myid << "\n";
333 cyl_sol_sock.precision(8);
334 cyl_sol_sock << "solution\n" << cylinder_submesh
335 << magnetic_field_cylinder_gf
336 << "window_title \"Time step: " << 0 << "\""
337 << "keys cvv\n autoscale off\n valuerange 0 1.414\n"
338 << "pause\n" << std::flush;
339 }
340 socketstream block_sol_sock;
341 if (visualization)
342 {
343 block_sol_sock.open(vishost, visport);
344 block_sol_sock << "parallel " << num_procs << " " << myid << "\n";
345 block_sol_sock.precision(8);
346 block_sol_sock << "solution\n" << block_submesh
347 << magnetic_field_block_gf
348 << "window_title \"Time step: " << 0 << "\""
349 << "window_geometry 400 0 400 350\n"
350 << "keys cvv\n autoscale off\n valuerange 0 1.414\n"
351 << "pause\n" << std::flush;
352 }
353
354 // Create the transfer map needed in the time integration loop
355 auto magnetic_field_block_to_cylinder_map = ParSubMesh::CreateTransferMap(
356 magnetic_field_block_gf,
357 magnetic_field_cylinder_gf);
358
359 real_t t = 0.0;
360 bool last_step = false;
361 for (int ti = 1; !last_step; ti++)
362 {
363 if (t + dt >= t_final - dt/2)
364 {
365 last_step = true;
366 }
367
368 // Advance the diffusion equation on the outer block to the next time step
369 d_ode_solver.Step(magnetic_field_block, t, dt);
370 {
371 // Transfer the solution from the inner surface of the outer block to
372 // the cylinder outer surface to act as a boundary condition.
373 magnetic_field_block_gf.SetFromTrueDofs(magnetic_field_block);
374
375 magnetic_field_block_to_cylinder_map.Transfer(magnetic_field_block_gf,
376 magnetic_field_cylinder_gf);
377
378 magnetic_field_cylinder_gf.GetTrueDofs(magnetic_field_cylinder);
379 }
380 // Advance the convection-diffusion equation on the outer block to the
381 // next time step
382 cd_ode_solver.Step(magnetic_field_cylinder, t, dt);
383
384 if (last_step || (ti % vis_steps) == 0)
385 {
386 if (myid == 0)
387 {
388 out << "step " << ti << ", t = " << t << std::endl;
389 }
390
391 magnetic_field_cylinder_gf.SetFromTrueDofs(magnetic_field_cylinder);
392 magnetic_field_block_gf.SetFromTrueDofs(magnetic_field_block);
393
394 if (visualization)
395 {
396 cyl_sol_sock << "parallel " << num_procs << " " << myid << "\n";
397 cyl_sol_sock << "solution\n" << cylinder_submesh
398 << magnetic_field_cylinder_gf
399 << "window_title \"Time step: " << ti << "\""
400 << std::flush;
401 block_sol_sock << "parallel " << num_procs << " " << myid << "\n";
402 block_sol_sock << "solution\n" << block_submesh
403 << magnetic_field_block_gf
404 << "window_title \"Time step: " << ti << "\""
405 << std::flush;
406 }
407 }
408 }
409
410 return 0;
411}
void Sort()
Sorts the array in ascending order. This requires operator< to be defined for T.
Definition array.hpp:278
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:830
void Unique()
Removes duplicities from a sorted array. This requires operator== to be defined for T.
Definition array.hpp:286
void AddDomainIntegrator(BilinearFormIntegrator *bfi)
Adds new Domain Integrator. Assumes ownership of bfi.
void Finalize(int skip_zeros=1) override
Finalizes the matrix initialization if the AssemblyLevel is AssemblyLevel::LEGACY....
Conjugate gradient method.
Definition solvers.hpp:538
void Mult(const Vector &b, Vector &x) const override
Iterative solution of the linear system using the Conjugate Gradient method.
Definition solvers.cpp:751
void SetOperator(const Operator &op) override
Set/update the solver for the given operator.
Definition solvers.hpp:551
Base class Coefficients that optionally depend on space and time. These are used by the BilinearFormI...
A coefficient that is constant across space and time.
Integrator for for Nedelec elements.
Parallel smoothers in hypre.
Definition hypre.hpp:1046
void SetType(HypreSmoother::Type type, int relax_times=1)
Set the relaxation type and number of sweeps.
Definition hypre.cpp:3608
static void Init()
Initialize hypre by calling HYPRE_Init() and set default options. After calling Hypre::Init(),...
Definition hypre.cpp:33
void SetRelTol(real_t rtol)
Definition solvers.hpp:229
virtual void SetPreconditioner(Solver &pr)
This should be called before SetOperator.
Definition solvers.cpp:174
virtual void SetPrintLevel(int print_lvl)
Legacy method to set the level of verbosity of the solver output.
Definition solvers.cpp:72
void SetMaxIter(int max_it)
Definition solvers.hpp:231
void SetAbsTol(real_t atol)
Definition solvers.hpp:230
Mesh data type.
Definition mesh.hpp:64
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1216
void UniformRefinement(int i, const DSTable &, int *, int *, int *)
Definition mesh.cpp:11295
static int WorldRank()
Return the MPI rank in MPI_COMM_WORLD.
static int WorldSize()
Return the size of MPI_COMM_WORLD.
static void Init(int &argc, char **&argv, int required=default_thread_required, int *provided=nullptr)
Singleton creation with Mpi::Init(argc, argv).
Arbitrary order H(curl)-conforming Nedelec finite elements.
Definition fe_coll.hpp:482
Pointer to an Operator of a specified type.
Definition handle.hpp:34
int height
Dimension of the output / number of rows in the matrix.
Definition operator.hpp:27
virtual void Mult(const Vector &x, Vector &y) const =0
Operator application: y=A(x).
void ParseCheck(std::ostream &out=mfem::out)
void AddOption(bool *var, const char *enable_short_name, const char *enable_long_name, const char *disable_short_name, const char *disable_long_name, const char *description, bool required=false)
Add a boolean option and set 'var' to receive the value. Enable/disable tags are used to set the bool...
Definition optparser.hpp:82
Class for parallel bilinear form.
void Assemble(int skip_zeros=1)
Assemble the local matrix.
void FormSystemMatrix(const Array< int > &ess_tdof_list, OperatorHandle &A) override
Form the linear system matrix A, see FormLinearSystem() for details.
Abstract parallel finite element space.
Definition pfespace.hpp:29
void GetEssentialTrueDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_tdof_list, int component=-1) const override
ParMesh * GetParMesh() const
Definition pfespace.hpp:338
Class for parallel grid function.
Definition pgridfunc.hpp:50
HypreParVector * GetTrueDofs() const
Returns the true dofs in a new HypreParVector.
void SetFromTrueDofs(const Vector &tv) override
Set the GridFunction from the given true-dof vector.
void ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff, const Array< int > &bdr_attr) override
Project the tangential components of the given VectorCoefficient on the boundary. Only boundary attri...
Class for parallel linear form.
void ParallelAssemble(Vector &tv)
Assemble the vector on the true dofs, i.e. P^t v.
void Assemble()
Assembles the ParLinearForm i.e. sums over all domain/bdr integrators.
Class for parallel meshes.
Definition pmesh.hpp:34
static ParSubMesh CreateFromDomain(const ParMesh &parent, const Array< int > &domain_attributes)
Create a domain ParSubMesh from its parent.
Definition psubmesh.cpp:27
static ParTransferMap CreateTransferMap(const ParGridFunction &src, const ParGridFunction &dst)
Create a Transfer Map object.
static ParSubMesh CreateFromBoundary(const ParMesh &parent, const Array< int > &boundary_attributes)
Create a surface ParSubMesh from its parent.
Definition psubmesh.cpp:33
Third-order, strong stability preserving (SSP) Runge-Kutta method.
Definition ode.hpp:259
void Init(TimeDependentOperator &f_) override
Associate a TimeDependentOperator with the ODE solver.
Definition ode.cpp:211
void Step(Vector &x, real_t &t, real_t &dt) override
Perform a time step from time t [in] to time t [out] based on the requested step size dt [in].
Definition ode.cpp:219
Vector coefficient defined as a product of scalar and vector coefficients.
bool iterative_mode
If true, use the second argument of Mult() as an initial guess.
Definition operator.hpp:783
Base abstract class for first order time dependent operators.
Definition operator.hpp:332
Base class for vector Coefficients that optionally depend on time and space.
A general vector function coefficient.
Vector data type.
Definition vector.hpp:82
void SetSubVector(const Array< int > &dofs, const real_t value)
Set the entries listed in dofs to the given value.
Definition vector.cpp:679
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:558
Vector & Add(const real_t a, const Vector &Va)
(*this) += a * Va
Definition vector.cpp:322
int open(const char hostname[], int port)
Open the socket stream on 'port' at 'hostname'.
real_t sigma(const Vector &x)
Definition maxwell.cpp:102
const real_t alpha
Definition ex15.cpp:369
int main()
void square_xy(const Vector &p, Vector &v)
void velocity_profile(const Vector &c, Vector &q)
void velocity_profile(const Vector &c, Vector &q)
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition globals.hpp:66
float real_t
Definition config.hpp:43
const char vishost[]
real_t p(const Vector &x, real_t t)