Chi-Tech
CodeTut4.h
Go to the documentation of this file.
1
/**\page CodeTut4 Coding Tutorial 4 - MMS for Poisson's problem with PWLC
2
3
## Table of contents
4
- \ref CodeTut4Sec1
5
- \ref CodeTut4Sec2
6
- \ref CodeTut4Sec3
7
- \ref CodeTut4Sec4
8
- \ref CodeTut4SecX
9
10
\section CodeTut4Sec1 1 Defining a wrapper to call a lua function
11
To implement a manufactured solution in our PWLC solver from \ref CodeTut3
12
we are required to call a lua-function
13
with spatial coordinates x,y,z. In order to use the lua-console, specifically
14
the lua state, we have to include the headers
15
\code
16
#include "console/chi_console.h"
17
#include "chi_lua.h"
18
\endcode
19
20
When then have to grab the console state from the runtime environment. To do this
21
we add the following code, in the beginning of the program,
22
just below `chi::RunBatch`
23
\code
24
auto L = chi::console.consoleState;
25
\endcode
26
27
Next we have to tell the c++ code exactly how to call a lua-function. To this
28
end we define a wrapper using a c++ lambda as shown below. You can define this
29
function where ever you like as long as it can capture the lua state, `L`.
30
For this tutorial we defined it just before the matrix assembly.
31
\code
32
auto CallLuaXYZFunction = [&L](const std::string& lua_func_name,
33
const chi_mesh::Vector3& xyz)
34
{
35
//============= Load lua function
36
lua_getglobal(L, lua_func_name.c_str());
37
38
//============= Error check lua function
39
if (not lua_isfunction(L, -1))
40
throw std::logic_error("CallLuaXYZFunction attempted to access lua-function, " +
41
lua_func_name + ", but it seems the function"
42
" could not be retrieved.");
43
44
//============= Push arguments
45
lua_pushnumber(L, xyz.x);
46
lua_pushnumber(L, xyz.y);
47
lua_pushnumber(L, xyz.z);
48
49
//============= Call lua function
50
//3 arguments, 1 result (double), 0=original error object
51
double lua_return = 0.0;
52
if (lua_pcall(L,3,1,0) == 0)
53
{
54
LuaCheckNumberValue("CallLuaXYZFunction", L, -1);
55
lua_return = lua_tonumber(L,-1);
56
}
57
else
58
throw std::logic_error("CallLuaXYZFunction attempted to call lua-function, " +
59
lua_func_name + ", but the call failed." +
60
xyz.PrintStr());
61
62
lua_pop(L,1); //pop the double, or error code
63
64
return lua_return;
65
};
66
\endcode
67
Notice this function takes 2 arguments: a string value representing the lua
68
function's name, and a `chi_mesh::Vector3` indicating the real-word XYZ
69
coordinates at which to evaluate the function.
70
71
In order to implement a manufactures solution we need a right-hand-side (RHS)
72
function and an actual manufactured solution (MS) function. The RHS function will essentially
73
steer the entire solution towards the manufactured solution. The evaluation of
74
the actual manufactured solution is required in two places, i.e., in the
75
implementation of Dirichlet boundary conditions, and when we compute the L2-norm
76
of the error between our FEM-solution and the manufactured one.
77
78
\section CodeTut4Sec2 2 Lua functions
79
The RHS function and the MS function both need to exist within the lua state.
80
We therefore ad the following to the `mesh.lua` input file.
81
\code
82
function MMS_phi(x,y,z)
83
return math.cos(math.pi*x) + math.cos(math.pi*y)
84
end
85
function MMS_q(x,y,z)
86
return math.pi*math.pi * (math.cos(math.pi*x)+math.cos(math.pi*y))
87
end
88
\endcode
89
The function `MMS_phi` is the MS function and the function `MMS_q` is the RHS
90
function. These functions can be defined anywhere in the input file as long as
91
they are available in the state when solver needs them.
92
93
\section CodeTut4Sec3 3 Getting guadrature-point real world XYZ
94
Quadrautre-point real-world positions are stored when the `qp_data` structure
95
is created. They are accessed with the call
96
`chi_math::finite_element::InternalQuadraturePointData::QPointXYZ`, taking the
97
quadrature point index as a parameter.
98
\code
99
for (size_t qp : qp_data.QuadraturePointIndices())
100
cell_rhs[i] += CallLuaXYZFunction(qp_data.QPointXYZ(qp),"MMS_q", L) *
101
qp_data.ShapeValue(i, qp) * qp_data.JxW(qp);
102
\endcode
103
104
\section CodeTut4Sec4 4 Convergence study
105
The Method of Manufactured Solution (MMS) is often used to verify the rate of
106
convergence of computational methods. In order to determine magnitude of the
107
error of the FEM solution, \f$ \phi_{FEM} \f$, compared to the MS,
108
\f$ \phi_{MMS} \f$, we need to evaluate the following norm
109
\f[
110
||e||_2 = \sqrt{\int_V \biggr( \phi_{MMS}(\mathbf{x}) - \phi_{FEM}(\mathbf{x})\biggr)^2 dV}
111
\f]
112
We again here use the quadrature rules to obtain this integral as
113
\f[
114
\int_V \biggr( \phi_{MMS}(\mathbf{x}) - \phi_{FEM}(\mathbf{x})\biggr)^2 dV = \sum_c \sum_n^{N_V}
115
w_n \biggr( \phi_{MMS}(\mathbf{x}_n) - \phi_{FEM}(\tilde{\mathbf{x}}_n)\biggr)^2 \ | J(\tilde{\mathbf{x}}_n |
116
\f]
117
for which we use the code
118
\code
119
//============================================= Compute error
120
//First get ghosted values
121
const auto field_wg = ff->GetGhostedFieldVector();
122
123
double local_error = 0.0;
124
for (const auto& cell : grid.local_cells)
125
{
126
const auto& cell_mapping = sdm.GetCellMapping(cell);
127
const size_t num_nodes = cell_mapping.NumNodes();
128
const auto qp_data = cell_mapping.MakeVolumeQuadraturePointData();
129
130
//======================= Grab nodal phi values
131
std::vector<double> nodal_phi(num_nodes,0.0);
132
for (size_t j=0; j < num_nodes; ++j)
133
{
134
const int64_t imap = sdm.MapDOFLocal(cell, j);
135
nodal_phi[j] = field_wg[imap];
136
}//for j
137
138
//======================= Quadrature loop
139
for (size_t qp : qp_data.QuadraturePointIndices())
140
{
141
double phi_fem = 0.0;
142
for (size_t j=0; j < num_nodes; ++j)
143
phi_fem += nodal_phi[j] * qp_data.ShapeValue(j, qp);
144
145
double phi_true = CallLuaXYZFunction("MMS_phi",qp_data.QPointXYZ(qp));
146
147
local_error += std::pow(phi_true - phi_fem,2.0) * qp_data.JxW(qp);
148
}
149
}//for cell
150
151
double global_error = 0.0;
152
MPI_Allreduce(&local_error, //sendbuf
153
&global_error, //recvbuf
154
1, MPI_DOUBLE, //count+datatype
155
MPI_SUM, //operation
156
Chi::mpi.comm); //communicator
157
158
global_error = std::sqrt(global_error);
159
160
chi::log.Log() << "Error: " << std::scientific << global_error
161
<< " Num-cells: " << grid.GetGlobalNumberOfCells();
162
\endcode
163
Notice that we grab the nodal values of \f$ \phi_{FEM} \f$ before we loop over
164
quadrature points. We then use these nodal values within the quadrature-point
165
loop to construct \f$ \phi_{FEM}(\tilde{\mathbf{x}}_n) \f$ as
166
\f[
167
\phi_{FEM}(\tilde{\mathbf{x}}_n) = \sum_j \phi_j b_j(\tilde{\mathbf{x}}_n)
168
\f]
169
with the code
170
\code
171
for (size_t j=0; j < num_nodes; ++j)
172
phi_fem += nodal_phi[j] * qp_data.ShapeValue(j, qp);
173
\endcode
174
175
Notice also that we first have to compute a local error, from the cells that are
176
on the local partition. This is then followed by an `MPI_Allreduce`, with the
177
`MPI_SUM` operation, to gather all the local error values into a single global
178
error value. This is done via the code
179
\code
180
double global_error = 0.0;
181
MPI_Allreduce(&local_error, //sendbuf
182
&global_error, //recvbuf
183
1, MPI_DOUBLE, //count+datatype
184
MPI_SUM, //operation
185
Chi::mpi.comm); //communicator
186
187
global_error = std::sqrt(global_error);
188
\endcode
189
190
With this code in-place we can run the program several times with a different
191
amount of cells, which provides us with the data below:
192
193
\image html CodingTutorials/Tut4_OrderOfConv.png width=700px
194
195
\section CodeTut4SecX The complete program
196
\code
197
#include "chi_runtime.h"
198
#include "chi_log.h"
199
200
#include "mesh/MeshHandler/chi_meshhandler.h"
201
202
#include "math/SpatialDiscretization/FiniteElement/PiecewiseLinear/pwlc.h"
203
#include "math/PETScUtils/petsc_utils.h"
204
205
#include "physics/FieldFunction/fieldfunction2.h"
206
207
#include "console/chi_console.h"
208
#include "chi_lua.h"
209
210
int main(int argc, char* argv[])
211
{
212
chi::Initialize(argc,argv);
213
chi::RunBatch(argc, argv);
214
215
chi::log.Log() << "Coding Tutorial 4";
216
217
//============================================= Get grid
218
auto grid_ptr = chi_mesh::GetCurrentHandler().GetGrid();
219
const auto& grid = *grid_ptr;
220
221
chi::log.Log() << "Global num cells: " << grid.GetGlobalNumberOfCells();
222
223
//============================================= Make SDM
224
typedef std::shared_ptr<chi_math::SpatialDiscretization> SDMPtr;
225
SDMPtr sdm_ptr = chi_math::SpatialDiscretization_PWLC::New(grid_ptr);
226
const auto& sdm = *sdm_ptr;
227
228
const auto& OneDofPerNode = sdm.UNITARY_UNKNOWN_MANAGER;
229
230
const size_t num_local_dofs = sdm.GetNumLocalDOFs(OneDofPerNode);
231
const size_t num_globl_dofs = sdm.GetNumGlobalDOFs(OneDofPerNode);
232
233
chi::log.Log() << "Num local DOFs: " << num_local_dofs;
234
chi::log.Log() << "Num globl DOFs: " << num_globl_dofs;
235
236
//============================================= Initializes Mats and Vecs
237
const auto n = static_cast<int64_t>(num_local_dofs);
238
const auto N = static_cast<int64_t>(num_globl_dofs);
239
Mat A;
240
Vec x,b;
241
242
A = chi_math::PETScUtils::CreateSquareMatrix(n,N);
243
x = chi_math::PETScUtils::CreateVector(n,N);
244
b = chi_math::PETScUtils::CreateVector(n,N);
245
246
std::vector<int64_t> nodal_nnz_in_diag;
247
std::vector<int64_t> nodal_nnz_off_diag;
248
sdm.BuildSparsityPattern(nodal_nnz_in_diag,nodal_nnz_off_diag, OneDofPerNode);
249
250
chi_math::PETScUtils::InitMatrixSparsity(A,
251
nodal_nnz_in_diag,
252
nodal_nnz_off_diag);
253
254
//============================================= Source lambda
255
auto CallLuaXYZFunction = [&L](const std::string& lua_func_name,
256
const chi_mesh::Vector3& xyz)
257
{
258
//============= Load lua function
259
lua_getglobal(L, lua_func_name.c_str());
260
261
//============= Error check lua function
262
if (not lua_isfunction(L, -1))
263
throw std::logic_error("CallLuaXYZFunction attempted to access lua-function, " +
264
lua_func_name + ", but it seems the function"
265
" could not be retrieved.");
266
267
//============= Push arguments
268
lua_pushnumber(L, xyz.x);
269
lua_pushnumber(L, xyz.y);
270
lua_pushnumber(L, xyz.z);
271
272
//============= Call lua function
273
//3 arguments, 1 result (double), 0=original error object
274
double lua_return = 0.0;
275
if (lua_pcall(L,3,1,0) == 0)
276
{
277
LuaCheckNumberValue("CallLuaXYZFunction", L, -1);
278
lua_return = lua_tonumber(L,-1);
279
}
280
else
281
throw std::logic_error("CallLuaXYZFunction attempted to call lua-function, " +
282
lua_func_name + ", but the call failed." +
283
xyz.PrintStr());
284
285
lua_pop(L,1); //pop the double, or error code
286
287
return lua_return;
288
};
289
290
//============================================= Assemble the system
291
chi::log.Log() << "Assembling system: ";
292
for (const auto& cell : grid.local_cells)
293
{
294
const auto& cell_mapping = sdm.GetCellMapping(cell);
295
const auto qp_data = cell_mapping.MakeVolumeQuadraturePointData();
296
const auto cell_node_xyzs = cell_mapping.GetNodeLocations();
297
298
const size_t num_nodes = cell_mapping.NumNodes();
299
MatDbl Acell(num_nodes, VecDbl(num_nodes, 0.0));
300
VecDbl cell_rhs(num_nodes, 0.0);
301
302
for (size_t i=0; i<num_nodes; ++i)
303
{
304
for (size_t j=0; j<num_nodes; ++j)
305
{
306
double entry_aij = 0.0;
307
for (size_t qp : qp_data.QuadraturePointIndices())
308
{
309
entry_aij +=
310
qp_data.ShapeGrad(i, qp).Dot(qp_data.ShapeGrad(j, qp)) *
311
qp_data.JxW(qp);
312
}//for qp
313
Acell[i][j] = entry_aij;
314
}//for j
315
for (size_t qp : qp_data.QuadraturePointIndices())
316
cell_rhs[i] += CallLuaXYZFunction("MMS_q",qp_data.QPointXYZ(qp)) *
317
qp_data.ShapeValue(i, qp) * qp_data.JxW(qp);
318
}//for i
319
320
//======================= Flag nodes for being on dirichlet boundary
321
std::vector<bool> node_boundary_flag(num_nodes, false);
322
const size_t num_faces = cell.faces.size();
323
for (size_t f=0; f<num_faces; ++f)
324
{
325
const auto& face = cell.faces[f];
326
if (face.has_neighbor) continue;
327
328
const size_t num_face_nodes = face.vertex_ids.size();
329
for (size_t fi=0; fi<num_face_nodes; ++fi)
330
{
331
const uint i = cell_mapping.MapFaceNode(f,fi);
332
node_boundary_flag[i] = true;
333
}//for fi
334
}//for face f
335
336
//======================= Develop node mapping
337
std::vector<int64_t> imap(num_nodes, 0); //node-mapping
338
for (size_t i=0; i<num_nodes; ++i)
339
imap[i] = sdm.MapDOF(cell, i);
340
341
//======================= Assembly into system
342
for (size_t i=0; i<num_nodes; ++i)
343
{
344
if (node_boundary_flag[i]) //if dirichlet node
345
{
346
MatSetValue(A, imap[i], imap[i], 1.0, ADD_VALUES);
347
double bval = CallLuaXYZFunction("MMS_phi",cell_node_xyzs[i]);
348
VecSetValue(b, imap[i], bval, ADD_VALUES);
349
}
350
else
351
{
352
for (size_t j=0; j<num_nodes; ++j)
353
{
354
if (not node_boundary_flag[j])
355
MatSetValue(A, imap[i], imap[j], Acell[i][j], ADD_VALUES);
356
else
357
{
358
double bval = CallLuaXYZFunction("MMS_phi",cell_node_xyzs[j]);
359
VecSetValue(b, imap[i], -Acell[i][j]*bval, ADD_VALUES);
360
}
361
}//for j
362
VecSetValue(b, imap[i], cell_rhs[i], ADD_VALUES);
363
}
364
}//for i
365
}//for cell
366
367
chi::log.Log() << "Global assembly";
368
369
MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY);
370
MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY);
371
VecAssemblyBegin(b);
372
VecAssemblyEnd(b);
373
374
chi::log.Log() << "Done global assembly";
375
376
//============================================= Create Krylov Solver
377
chi::log.Log() << "Solving: ";
378
auto petsc_solver =
379
chi_math::PETScUtils::CreateCommonKrylovSolverSetup(
380
A, //Matrix
381
"PWLCDiffSolver", //Solver name
382
KSPCG, //Solver type
383
PCGAMG, //Preconditioner type
384
1.0e-6, //Relative residual tolerance
385
1000); //Max iterations
386
387
//============================================= Solve
388
KSPSolve(petsc_solver.ksp,b,x);
389
390
chi::log.Log() << "Done solving";
391
392
//============================================= Extract PETSc vector
393
std::vector<double> field;
394
sdm.LocalizePETScVector(x,field,OneDofPerNode);
395
396
//============================================= Clean up
397
KSPDestroy(&petsc_solver.ksp);
398
399
VecDestroy(&x);
400
VecDestroy(&b);
401
MatDestroy(&A);
402
403
chi::log.Log() << "Done cleanup";
404
405
//============================================= Create Field Function
406
auto ff = std::make_shared<chi_physics::FieldFunction>(
407
"Phi", //Text name
408
sdm_ptr, //Spatial Discr.
409
chi_math::Unknown(chi_math::UnknownType::SCALAR) //Unknown
410
);
411
412
ff->UpdateFieldVector(field);
413
414
ff->ExportToVTK("CodeTut4_PWLC");
415
416
//============================================= Compute error
417
//First get ghosted values
418
const auto field_wg = ff->GetGhostedFieldVector();
419
420
double local_error = 0.0;
421
for (const auto& cell : grid.local_cells)
422
{
423
const auto& cell_mapping = sdm.GetCellMapping(cell);
424
const size_t num_nodes = cell_mapping.NumNodes();
425
const auto qp_data = cell_mapping.MakeVolumeQuadraturePointData();
426
427
//======================= Grab nodal phi values
428
std::vector<double> nodal_phi(num_nodes,0.0);
429
for (size_t j=0; j < num_nodes; ++j)
430
{
431
const int64_t jmap = sdm.MapDOFLocal(cell, j);
432
nodal_phi[j] = field_wg[jmap];
433
}//for j
434
435
//======================= Quadrature loop
436
for (size_t qp : qp_data.QuadraturePointIndices())
437
{
438
double phi_fem = 0.0;
439
for (size_t j=0; j < num_nodes; ++j)
440
phi_fem += nodal_phi[j] * qp_data.ShapeValue(j, qp);
441
442
double phi_true = CallLuaXYZFunction("MMS_phi",qp_data.QPointXYZ(qp));
443
444
local_error += std::pow(phi_true - phi_fem,2.0) * qp_data.JxW(qp);
445
}
446
}//for cell
447
448
double global_error = 0.0;
449
MPI_Allreduce(&local_error, //sendbuf
450
&global_error, //recvbuf
451
1, MPI_DOUBLE, //count+datatype
452
MPI_SUM, //operation
453
Chi::mpi.comm); //communicator
454
455
global_error = std::sqrt(global_error);
456
457
chi::log.Log() << "Error: " << std::scientific << global_error
458
<< " Num-cells: " << grid.GetGlobalNumberOfCells();
459
460
chi::Finalize();
461
return 0;
462
}
463
\endcode
464
465
*/
doc
PAGES
ProgrammersManual
CodingTutorials
CodeTut4.h
Generated by
1.9.3