MFEM v4.8.0
Finite element discretization library
Loading...
Searching...
No Matches
plor-transfer.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// -----------------------------------------------------------------------
13// Parallel LOR Transfer Miniapp: Map functions between HO and LOR spaces
14// -----------------------------------------------------------------------
15//
16// This miniapp visualizes the maps between a high-order (HO) finite element
17// space, typically using high-order functions on a high-order mesh, and a
18// low-order refined (LOR) finite element space, typically defined by 0th or 1st
19// order functions on a low-order refinement of the HO mesh.
20//
21// The grid transfer operators are represented using either
22// InterpolationGridTransfer or L2ProjectionGridTransfer (depending on the
23// options requested by the user). The two transfer operators are then:
24//
25// 1. R: HO -> LOR, defined by GridTransfer::ForwardOperator
26// 2. P: LOR -> HO, defined by GridTransfer::BackwardOperator
27//
28// While defined generally, these operators have some nice properties for
29// particular finite element spaces. For example they satisfy PR=I, plus mass
30// conservation in both directions for L2 fields.
31//
32// Compile with: make plor-transfer
33//
34// Sample runs: plor-transfer
35// plor-transfer -h1
36// plor-transfer -t
37// plor-transfer -m ../../data/star-q2.mesh -lref 5 -p 4
38// plor-transfer -m ../../data/star-mixed.mesh -lref 3 -p 2
39// plor-transfer -lref 4 -o 4 -lo 0 -p 1
40// plor-transfer -lref 5 -o 4 -lo 0 -p 1
41// plor-transfer -lref 5 -o 4 -lo 3 -p 2
42// plor-transfer -lref 5 -o 4 -lo 0 -p 3
43
44#include "mfem.hpp"
45#include <fstream>
46#include <iostream>
47
48using namespace std;
49using namespace mfem;
50
51int problem = 1; // problem type
52
53int Wx = 0, Wy = 0; // window position
54int Ww = 350, Wh = 350; // window size
55int offx = Ww+5, offy = Wh+25; // window offsets
56
57string space;
58string direction;
59
60// Exact functions to project
61real_t RHO_exact(const Vector &x);
62
63// Helper functions
64void visualize(VisItDataCollection &, string, int, int, int /* visport */);
66 string);
67
68int main(int argc, char *argv[])
69{
70 // Initialize MPI and HYPRE.
71 Mpi::Init(argc, argv);
73
74 // Parse command-line options.
75 const char *mesh_file = "../../data/star.mesh";
76 int order = 2;
77 int lref = order+1;
78 int lorder = 0;
79 bool vis = true;
80 bool useH1 = false;
81 int visport = 19916;
82 bool use_pointwise_transfer = false;
83 const char *device_config = "cpu";
84 bool use_ea = false;
85
86 OptionsParser args(argc, argv);
87 args.AddOption(&mesh_file, "-m", "--mesh",
88 "Mesh file to use.");
89 args.AddOption(&problem, "-p", "--problem",
90 "Problem type (see the RHO_exact function).");
91 args.AddOption(&order, "-o", "--order",
92 "Finite element order (polynomial degree) or -1 for"
93 " isoparametric space.");
94 args.AddOption(&lref, "-lref", "--lor-ref-level", "LOR refinement level.");
95 args.AddOption(&lorder, "-lo", "--lor-order",
96 "LOR space order (polynomial degree, zero by default).");
97 args.AddOption(&vis, "-vis", "--visualization", "-no-vis",
98 "--no-visualization",
99 "Enable or disable GLVis visualization.");
100 args.AddOption(&useH1, "-h1", "--use-h1", "-l2", "--use-l2",
101 "Use H1 spaces instead of L2.");
102 args.AddOption(&use_pointwise_transfer, "-t", "--use-pointwise-transfer",
103 "-no-t", "--dont-use-pointwise-transfer",
104 "Use pointwise transfer operators instead of L2 projection.");
105 args.AddOption(&device_config, "-d", "--device",
106 "Device configuration string, see Device::Configure().");
107 args.AddOption(&use_ea, "-ea", "--ea-version", "-no-ea",
108 "--no-ea-version", "Use element assembly version.");
109 args.ParseCheck();
110
111 // Configure device
112 Device device(device_config);
113 if (Mpi::Root()) { device.Print(); }
114
115 // Read the mesh from the given mesh file.
116 Mesh serial_mesh(mesh_file, 1, 1);
117 ParMesh mesh(MPI_COMM_WORLD, serial_mesh);
118 serial_mesh.Clear();
119 int dim = mesh.Dimension();
120
121 // Make initial refinement on serial mesh.
122 for (int l = 0; l < 4; l++)
123 {
124 mesh.UniformRefinement();
125 }
126
127 // Create the low-order refined mesh
128 int basis_lor = BasisType::GaussLobatto; // BasisType::ClosedUniform;
129 ParMesh mesh_lor = ParMesh::MakeRefined(mesh, lref, basis_lor);
130
131 // Create spaces
132 FiniteElementCollection *fec, *fec_lor;
133 if (useH1)
134 {
135 space = "H1";
136 if (lorder == 0)
137 {
138 lorder = 1;
139 if (Mpi::Root())
140 {
141 cerr << "Switching the H1 LOR space order from 0 to 1\n";
142 }
143 }
144 fec = new H1_FECollection(order, dim);
145 fec_lor = new H1_FECollection(lorder, dim);
146 }
147 else
148 {
149 space = "L2";
150 fec = new L2_FECollection(order, dim);
151 fec_lor = new L2_FECollection(lorder, dim);
152 }
153
154 ParFiniteElementSpace fespace(&mesh, fec);
155 ParFiniteElementSpace fespace_lor(&mesh_lor, fec_lor);
156
157 ParGridFunction rho(&fespace);
158 ParGridFunction rho_lor(&fespace_lor);
159
160 // Data collections for vis/analysis
161 VisItDataCollection HO_dc(MPI_COMM_WORLD, "HO", &mesh);
162 HO_dc.RegisterField("density", &rho);
163 VisItDataCollection LOR_dc(MPI_COMM_WORLD, "LOR", &mesh_lor);
164 LOR_dc.RegisterField("density", &rho_lor);
165
166 ParBilinearForm M_ho(&fespace);
168 M_ho.Assemble();
169 M_ho.Finalize();
170 HypreParMatrix* M_ho_tdof = M_ho.ParallelAssemble();
171
172 ParBilinearForm M_lor(&fespace_lor);
174 M_lor.Assemble();
175 M_lor.Finalize();
176 HypreParMatrix* M_lor_tdof = M_lor.ParallelAssemble();
177
178 // HO projections
179 direction = "HO -> LOR @ HO";
181 rho.ProjectCoefficient(RHO);
182 // Make sure AMR constraints are satisfied
183 rho.SetTrueVector();
184 rho.SetFromTrueVector();
185
186 real_t ho_mass = compute_mass(&fespace, -1.0, HO_dc, "HO ");
187 if (vis) { visualize(HO_dc, "HO", Wx, Wy, visport); Wx += offx; }
188
189 GridTransfer *gt;
190 if (use_pointwise_transfer)
191 {
192 gt = new InterpolationGridTransfer(fespace, fespace_lor);
193 }
194 else
195 {
196 gt = new L2ProjectionGridTransfer(fespace, fespace_lor);
197 }
198
199 // Configure element assembly for device acceleration
200 gt->UseEA(use_ea);
201
202 const Operator &R = gt->ForwardOperator();
203
204 // HO->LOR restriction
205 direction = "HO -> LOR @ LOR";
206 R.Mult(rho, rho_lor);
207 compute_mass(&fespace_lor, ho_mass, LOR_dc, "R(HO) ");
208 if (vis) { visualize(LOR_dc, "R(HO)", Wx, Wy, visport); Wx += offx; }
209 auto global_max = [](const Vector& v)
210 {
211 real_t max = v.Normlinf();
212 MPI_Allreduce(MPI_IN_PLACE, &max, 1, MPITypeMap<real_t>::mpi_type,
213 MPI_MAX, MPI_COMM_WORLD);
214 return max;
215 };
216
218 {
219 const Operator &P = gt->BackwardOperator();
220 // LOR->HO prolongation
221 direction = "HO -> LOR @ HO";
222 ParGridFunction rho_prev = rho;
223 P.Mult(rho_lor, rho);
224 compute_mass(&fespace, ho_mass, HO_dc, "P(R(HO)) ");
225 if (vis) { visualize(HO_dc, "P(R(HO))", Wx, Wy, visport); Wx = 0; Wy += offy; }
226
227 rho_prev -= rho;
228 Vector rho_prev_true(fespace.GetTrueVSize());
229 rho_prev.GetTrueDofs(rho_prev_true);
230 real_t l_inf = global_max(rho_prev_true);
231 if (Mpi::Root())
232 {
233 cout.precision(12);
234 cout << "|HO - P(R(HO))|_∞ = " << l_inf << endl;
235 }
236 }
237
238 // HO* to LOR* dual fields
239 ParLinearForm M_rho(&fespace), M_rho_lor(&fespace_lor);
240 auto global_sum = [](const Vector& v)
241 {
242 real_t sum = v.Sum();
243 MPI_Allreduce(MPI_IN_PLACE, &sum, 1, MPITypeMap<real_t>::mpi_type,
244 MPI_SUM, MPI_COMM_WORLD);
245 return sum;
246 };
247 if (!use_pointwise_transfer && gt->SupportsBackwardsOperator())
248 {
249 Vector M_rho_true(fespace.GetTrueVSize());
250 M_ho_tdof->Mult(rho.GetTrueVector(), M_rho_true);
251 fespace.GetRestrictionOperator()->MultTranspose(M_rho_true, M_rho);
252 const Operator &P = gt->BackwardOperator();
253 P.MultTranspose(M_rho, M_rho_lor);
254 real_t ho_dual_mass = global_sum(M_rho);
255 real_t lor_dual_mass = global_sum(M_rho_lor);
256 if (Mpi::Root())
257 {
258 cout << "HO -> LOR dual field: " << abs(ho_dual_mass - lor_dual_mass) << "\n\n";
259 }
260 }
261
262 // LOR projections
263 direction = "LOR -> HO @ LOR";
264 rho_lor.ProjectCoefficient(RHO);
265 ParGridFunction rho_lor_prev = rho_lor;
266 real_t lor_mass = compute_mass(&fespace_lor, -1.0, LOR_dc, "LOR ");
267 if (vis) { visualize(LOR_dc, "LOR", Wx, Wy, visport); Wx += offx; }
268
270 {
271 const Operator &P = gt->BackwardOperator();
272 // Prolongate to HO space
273 direction = "LOR -> HO @ HO";
274 P.Mult(rho_lor, rho);
275 compute_mass(&fespace, lor_mass, HO_dc, "P(LOR) ");
276 if (vis) { visualize(HO_dc, "P(LOR)", Wx, Wy, visport); Wx += offx; }
277
278 // Restrict back to LOR space. This won't give the original function because
279 // the rho_lor doesn't necessarily live in the range of R.
280 direction = "LOR -> HO @ LOR";
281 R.Mult(rho, rho_lor);
282 compute_mass(&fespace_lor, lor_mass, LOR_dc, "R(P(LOR))");
283 if (vis) { visualize(LOR_dc, "R(P(LOR))", Wx, Wy, visport); }
284
285 rho_lor_prev -= rho_lor;
286 Vector rho_lor_prev_true(fespace_lor.GetTrueVSize());
287 rho_lor_prev.GetTrueDofs(rho_lor_prev_true);
288 real_t l_inf = global_max(rho_lor_prev_true);
289 if (Mpi::Root())
290 {
291 cout.precision(12);
292 cout << "|LOR - R(P(LOR))|_∞ = " << l_inf << endl;
293 }
294 }
295
296 // LOR* to HO* dual fields
297 if (!use_pointwise_transfer)
298 {
299 Vector M_rho_lor_true(fespace_lor.GetTrueVSize());
300 M_lor_tdof->Mult(rho_lor.GetTrueVector(), M_rho_lor_true);
301 fespace_lor.GetRestrictionOperator()->MultTranspose(M_rho_lor_true,
302 M_rho_lor);
303 R.MultTranspose(M_rho_lor, M_rho);
304 real_t ho_dual_mass = global_sum(M_rho);
305 real_t lor_dual_mass = global_sum(M_rho_lor);
306
307 if (Mpi::Root())
308 {
309 cout << "lor dual mass = " << lor_dual_mass << '\n';
310 cout << "ho dual mass = " << ho_dual_mass << '\n';
311 cout << "LOR -> HO dual field: " << abs(ho_dual_mass - lor_dual_mass) << '\n';
312 }
313 }
314
315 delete fec;
316 delete fec_lor;
317 delete M_ho_tdof;
318 delete M_lor_tdof;
319 delete gt;
320
321 return 0;
322}
323
324
326{
327 switch (problem)
328 {
329 case 1: // smooth field
330 return x(1)+0.25*cos(2*M_PI*x.Norml2());
331 case 2: // cubic function
332 return x(1)*x(1)*x(1) + 2*x(0)*x(1) + x(0);
333 case 3: // sharp gradient
334 return M_PI/2-atan(5*(2*x.Norml2()-1));
335 case 4: // basis function
336 return (x.Norml2() < 0.1) ? 1 : 0;
337 default:
338 return 1.0;
339 }
340}
341
342
343void visualize(VisItDataCollection &dc, string prefix, int x, int y,
344 int visport)
345{
346 int w = Ww, h = Wh;
347
348 char vishost[] = "localhost";
349
350 socketstream sol_sockL2(vishost, visport);
351 sol_sockL2 << "parallel " << Mpi::WorldSize() << " " << Mpi::WorldRank() <<
352 "\n";
353 sol_sockL2.precision(8);
354 sol_sockL2 << "solution\n" << *dc.GetMesh() << *dc.GetField("density")
355 << "window_geometry " << x << " " << y << " " << w << " " << h
356 << "plot_caption '" << space << " " << prefix << " Density'"
357 << "window_title '" << direction << "'" << flush;
358}
359
360
362 VisItDataCollection &dc, string prefix)
363{
364 ConstantCoefficient one(1.0);
365 ParLinearForm lf(L2);
367 lf.Assemble();
368
369 real_t newmass = lf(*dc.GetParField("density"));
370 if (Mpi::Root())
371 {
372 cout.precision(18);
373 cout << space << " " << prefix << " mass = " << newmass;
374 if (massL2 >= 0)
375 {
376 cout.precision(4);
377 cout << " (" << fabs(newmass-massL2)*100/massL2 << "%)";
378 }
379 cout << endl;
380 }
381 return newmass;
382}
@ GaussLobatto
Closed type.
Definition fe_base.hpp:36
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....
A coefficient that is constant across space and time.
GridFunction * GetField(const std::string &field_name)
Get a pointer to a grid function in the collection.
ParGridFunction * GetParField(const std::string &field_name)
Get a pointer to a parallel grid function in the collection.
Mesh * GetMesh()
Get a pointer to the mesh in the collection.
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
Definition device.hpp:123
void Print(std::ostream &out=mfem::out)
Print the configuration of the MFEM virtual device object.
Definition device.cpp:297
Class for domain integration .
Definition lininteg.hpp:106
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition fe_coll.hpp:27
A general function coefficient.
void SetTrueVector()
Shortcut for calling GetTrueDofs() with GetTrueVector() as argument.
Definition gridfunc.hpp:147
void SetFromTrueVector()
Shortcut for calling SetFromTrueDofs() with GetTrueVector() as argument.
Definition gridfunc.hpp:153
const Vector & GetTrueVector() const
Read only access to the (optional) internal true-dof Vector.
Definition gridfunc.hpp:133
Base class for transfer algorithms that construct transfer Operators between two finite element (FE) ...
Definition transfer.hpp:30
void UseEA(bool use_ea_)
Definition transfer.hpp:76
virtual bool SupportsBackwardsOperator() const
Definition transfer.hpp:115
virtual const Operator & ForwardOperator()=0
Return an Operator that transfers GridFunctions from the domain FE space to GridFunctions in the rang...
virtual const Operator & BackwardOperator()=0
Return an Operator that transfers GridFunctions from the range FE space back to GridFunctions in the ...
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:275
Wrapper for hypre's ParCSR matrix class.
Definition hypre.hpp:408
HYPRE_Int Mult(HypreParVector &x, HypreParVector &y, real_t alpha=1.0, real_t beta=0.0) const
Computes y = alpha * A * x + beta * y.
Definition hypre.cpp:1861
static void Init()
Initialize hypre by calling HYPRE_Init() and set default options. After calling Hypre::Init(),...
Definition hypre.cpp:33
Transfer data between a coarse mesh and an embedded refined mesh using interpolation.
Definition transfer.hpp:137
Transfer data in L2 and H1 finite element spaces between a coarse mesh and an embedded refined mesh u...
Definition transfer.hpp:184
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:346
void AddDomainIntegrator(LinearFormIntegrator *lfi)
Adds new Domain Integrator. Assumes ownership of lfi.
Mesh data type.
Definition mesh.hpp:64
void Clear()
Clear the contents of the Mesh.
Definition mesh.hpp:761
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 bool Root()
Return true if the rank in MPI_COMM_WORLD is zero.
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).
Abstract operator.
Definition operator.hpp:25
virtual void Mult(const Vector &x, Vector &y) const =0
Operator application: y=A(x).
virtual void MultTranspose(const Vector &x, Vector &y) const
Action of the transpose operator: y=A^t(x). The default behavior in class Operator is to generate an ...
Definition operator.hpp:93
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.
HypreParMatrix * ParallelAssemble()
Returns the matrix assembled on the true dofs, i.e. P^t A P.
void Assemble(int skip_zeros=1)
Assemble the local matrix.
Abstract parallel finite element space.
Definition pfespace.hpp:29
const Operator * GetRestrictionOperator() const override
int GetTrueVSize() const override
Return the number of local vector true dofs.
Definition pfespace.hpp:350
Class for parallel grid function.
Definition pgridfunc.hpp:50
HypreParVector * GetTrueDofs() const
Returns the true dofs in a new HypreParVector.
void ProjectCoefficient(Coefficient &coeff) override
Project coeff Coefficient to this GridFunction. The projection computation depends on the choice of t...
Class for parallel linear form.
void Assemble()
Assembles the ParLinearForm i.e. sums over all domain/bdr integrators.
Class for parallel meshes.
Definition pmesh.hpp:34
static ParMesh MakeRefined(ParMesh &orig_mesh, int ref_factor, int ref_type)
Create a uniformly refined (by any factor) version of orig_mesh.
Definition pmesh.cpp:1374
Vector data type.
Definition vector.hpp:82
real_t Norml2() const
Returns the l2 norm of the vector.
Definition vector.cpp:931
Data collection with VisIt I/O routines.
void RegisterField(const std::string &field_name, GridFunction *gf) override
Add a grid function to the collection and update the root file.
int dim
Definition ex24.cpp:53
int main()
float real_t
Definition config.hpp:43
const char vishost[]
int Ww
int Wy
int Wx
real_t compute_mass(ParFiniteElementSpace *, real_t, VisItDataCollection &, string)
int problem
int offx
int Wh
string direction
void visualize(VisItDataCollection &, string, int, int, int)
real_t RHO_exact(const Vector &x)
string space
int offy
Helper struct to convert a C++ type to an MPI type.