MFEM v4.7.0
Finite element discretization library
Loading...
Searching...
No Matches
multidomain_nd.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2024, 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()
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 vis_steps = 10;
218
219 OptionsParser args(argc, argv);
220 args.AddOption(&order, "-o", "--order",
221 "Finite element order (polynomial degree).");
222 args.AddOption(&t_final, "-tf", "--t-final",
223 "Final time; start time is 0.");
224 args.AddOption(&dt, "-dt", "--time-step",
225 "Time step.");
226 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
227 "--no-visualization",
228 "Enable or disable GLVis visualization.");
229 args.AddOption(&vis_steps, "-vs", "--visualization-steps",
230 "Visualize every n-th timestep.");
231 args.ParseCheck();
232
233 Mesh *serial_mesh = new Mesh("multidomain-hex.mesh");
234 ParMesh parent_mesh = ParMesh(MPI_COMM_WORLD, *serial_mesh);
235 delete serial_mesh;
236
237 parent_mesh.UniformRefinement();
238
239 ND_FECollection fec(order, parent_mesh.Dimension());
240
241 // Create the sub-domains and accompanying Finite Element spaces from
242 // corresponding attributes. This specific mesh has two domain attributes and
243 // 9 boundary attributes.
244 Array<int> cylinder_domain_attributes(1);
245 cylinder_domain_attributes[0] = 1;
246
247 auto cylinder_submesh =
248 ParSubMesh::CreateFromDomain(parent_mesh, cylinder_domain_attributes);
249
250 ParFiniteElementSpace fes_cylinder(&cylinder_submesh, &fec);
251
252 Array<int> inflow_attributes(cylinder_submesh.bdr_attributes.Max());
253 inflow_attributes = 0;
254 inflow_attributes[7] = 1;
255
256 Array<int> inner_cylinder_wall_attributes(
257 cylinder_submesh.bdr_attributes.Max());
258 inner_cylinder_wall_attributes = 0;
259 inner_cylinder_wall_attributes[8] = 1;
260
261 // For the convection-diffusion equation inside the cylinder domain, the
262 // inflow surface and outer wall are treated as Dirichlet boundary
263 // conditions.
264 Array<int> inflow_tdofs, interface_tdofs, ess_tdofs;
265 fes_cylinder.GetEssentialTrueDofs(inflow_attributes, inflow_tdofs);
266 fes_cylinder.GetEssentialTrueDofs(inner_cylinder_wall_attributes,
267 interface_tdofs);
268 ess_tdofs.Append(inflow_tdofs);
269 ess_tdofs.Append(interface_tdofs);
270 ess_tdofs.Sort();
271 ess_tdofs.Unique();
272 ConvectionDiffusionTDO cd_tdo(fes_cylinder, ess_tdofs);
273
274 ParGridFunction magnetic_field_cylinder_gf(&fes_cylinder);
275 magnetic_field_cylinder_gf = 0.0;
276
277 Vector magnetic_field_cylinder;
278 magnetic_field_cylinder_gf.GetTrueDofs(magnetic_field_cylinder);
279
280 RK3SSPSolver cd_ode_solver;
281 cd_ode_solver.Init(cd_tdo);
282
283 Array<int> outer_domain_attributes(1);
284 outer_domain_attributes[0] = 2;
285
286 auto block_submesh = ParSubMesh::CreateFromDomain(parent_mesh,
287 outer_domain_attributes);
288
289 ParFiniteElementSpace fes_block(&block_submesh, &fec);
290
291 Array<int> block_wall_attributes(block_submesh.bdr_attributes.Max());
292 block_wall_attributes = 0;
293 block_wall_attributes[0] = 1;
294 block_wall_attributes[1] = 1;
295 block_wall_attributes[2] = 1;
296 block_wall_attributes[3] = 1;
297
298 Array<int> outer_cylinder_wall_attributes(
299 block_submesh.bdr_attributes.Max());
300 outer_cylinder_wall_attributes = 0;
301 outer_cylinder_wall_attributes[8] = 1;
302
303 fes_block.GetEssentialTrueDofs(block_wall_attributes, ess_tdofs);
304
305 ConvectionDiffusionTDO d_tdo(fes_block, ess_tdofs, 0.0, 1.0);
306
307 ParGridFunction magnetic_field_block_gf(&fes_block);
308 magnetic_field_block_gf = 0.0;
309
311 magnetic_field_block_gf.ProjectBdrCoefficientTangent(one,
312 block_wall_attributes);
313
314 Vector magnetic_field_block;
315 magnetic_field_block_gf.GetTrueDofs(magnetic_field_block);
316
317 RK3SSPSolver d_ode_solver;
318 d_ode_solver.Init(d_tdo);
319
320 Array<int> cylinder_surface_attributes(1);
321 cylinder_surface_attributes[0] = 9;
322
323 auto cylinder_surface_submesh =
324 ParSubMesh::CreateFromBoundary(parent_mesh, cylinder_surface_attributes);
325
326 char vishost[] = "localhost";
327 int visport = 19916;
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:261
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:769
void Unique()
Removes duplicities from a sorted array. This requires operator== to be defined for T.
Definition array.hpp:269
virtual void Finalize(int skip_zeros=1)
Finalizes the matrix initialization if the AssemblyLevel is AssemblyLevel::LEGACY....
void AddDomainIntegrator(BilinearFormIntegrator *bfi)
Adds new Domain Integrator. Assumes ownership of bfi.
Conjugate gradient method.
Definition solvers.hpp:513
virtual void SetOperator(const Operator &op)
Also calls SetOperator for the preconditioner if there is one.
Definition solvers.hpp:526
virtual void Mult(const Vector &b, Vector &x) const
Iterative solution of the linear system using the Conjugate Gradient method.
Definition solvers.cpp:718
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:1020
void SetType(HypreSmoother::Type type, int relax_times=1)
Set the relaxation type and number of sweeps.
Definition hypre.cpp:3550
static void Init()
Initialize hypre by calling HYPRE_Init() and set default options. After calling Hypre::Init(),...
Definition hypre.hpp:74
void SetRelTol(real_t rtol)
Definition solvers.hpp:209
virtual void SetPreconditioner(Solver &pr)
This should be called before SetOperator.
Definition solvers.cpp:173
virtual void SetPrintLevel(int print_lvl)
Legacy method to set the level of verbosity of the solver output.
Definition solvers.cpp:71
void SetMaxIter(int max_it)
Definition solvers.hpp:211
void SetAbsTol(real_t atol)
Definition solvers.hpp:210
Mesh data type.
Definition mesh.hpp:56
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1160
void UniformRefinement(int i, const DSTable &, int *, int *, int *)
Definition mesh.cpp:10970
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:465
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.
virtual void FormSystemMatrix(const Array< int > &ess_tdof_list, OperatorHandle &A)
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:277
Class for parallel grid function.
Definition pgridfunc.hpp:33
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 ParTransferMap CreateTransferMap(const ParGridFunction &src, const ParGridFunction &dst)
Create a Transfer Map object.
static ParSubMesh CreateFromBoundary(const ParMesh &parent, Array< int > &boundary_attributes)
Create a surface ParSubMesh from it's parent.
Definition psubmesh.cpp:32
static ParSubMesh CreateFromDomain(const ParMesh &parent, Array< int > &domain_attributes)
Create a domain ParSubMesh from it's parent.
Definition psubmesh.cpp:26
Third-order, strong stability preserving (SSP) Runge-Kutta method.
Definition ode.hpp:151
void Init(TimeDependentOperator &f_) override
Associate a TimeDependentOperator with the ODE solver.
Definition ode.cpp:68
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:76
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:686
Base abstract class for first order time dependent operators.
Definition operator.hpp:317
Base class for vector Coefficients that optionally depend on time and space.
A general vector function coefficient.
Vector data type.
Definition vector.hpp:80
void SetSubVector(const Array< int > &dofs, const real_t value)
Set the entries listed in dofs to the given value.
Definition vector.cpp:604
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:538
Vector & Add(const real_t a, const Vector &Va)
(*this) += a * Va
Definition vector.cpp:247
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)
const int visport
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)
RefCoord t[3]