Chi-Tech
CodeTut91a.h
Go to the documentation of this file.
1
/**\page CodeTut91a Coding Tutorial 91a - Discrete Ordinates with PWLD
2
3
## Table of contents
4
- \ref CodeTut91aSecX
5
6
7
\section CodeTut91aSecX The complete program
8
\code
9
#include "chi_runtime.h"
10
#include "chi_log.h"
11
12
#include "mesh/MeshHandler/chi_meshhandler.h"
13
14
#include "math/SpatialDiscretization/FiniteElement/PiecewiseLinear/pwlc.h"
15
#include "math/PETScUtils/petsc_utils.h"
16
17
#include "physics/FieldFunction/fieldfunction2.h"
18
19
#include "console/chi_console.h"
20
#include "chi_lua.h"
21
22
int main(int argc, char* argv[])
23
{
24
chi::Initialize(argc,argv);
25
chi::RunBatch(argc, argv);
26
27
const std::string fname = "Tutorial_07";
28
29
if (chi::mpi.process_count != 1)
30
throw std::logic_error(fname + ": Is serial only.");
31
32
//============================================= Get grid
33
auto grid_ptr = chi_mesh::GetCurrentHandler().GetGrid();
34
const auto& grid = *grid_ptr;
35
36
chi::log.Log() << "Global num cells: " << grid.GetGlobalNumberOfCells();
37
38
//============================================= Make Orthogonal mapping
39
const auto ijk_info = grid.GetIJKInfo();
40
const auto& ijk_mapping = grid.MakeIJKToGlobalIDMapping();
41
42
43
const auto Nx = static_cast<int64_t>(ijk_info[0]);
44
const auto Ny = static_cast<int64_t>(ijk_info[1]);
45
const auto Nz = static_cast<int64_t>(ijk_info[2]);
46
47
const auto Dim1 = chi_mesh::DIMENSION_1;
48
const auto Dim2 = chi_mesh::DIMENSION_2;
49
const auto Dim3 = chi_mesh::DIMENSION_3;
50
51
int dimension = 0;
52
if (grid.Attributes() & Dim1) dimension = 1;
53
if (grid.Attributes() & Dim2) dimension = 2;
54
if (grid.Attributes() & Dim3) dimension = 3;
55
56
//============================================= Make SDM
57
typedef std::shared_ptr<chi_math::SpatialDiscretization> SDMPtr;
58
SDMPtr sdm_ptr = chi_math::SpatialDiscretization_PWLD::New(grid_ptr);
59
const auto& sdm = *sdm_ptr;
60
61
const auto& OneDofPerNode = sdm.UNITARY_UNKNOWN_MANAGER;
62
63
const size_t num_local_nodes = sdm.GetNumLocalDOFs(OneDofPerNode);
64
const size_t num_globl_nodes = sdm.GetNumGlobalDOFs(OneDofPerNode);
65
66
chi::log.Log() << "Num local nodes: " << num_local_nodes;
67
chi::log.Log() << "Num globl nodes: " << num_globl_nodes;
68
69
//============================================= Make an angular quadrature
70
std::shared_ptr<chi_math::AngularQuadrature> quadrature;
71
if (dimension == 1)
72
quadrature = std::make_shared<chi_math::AngularQuadratureProdGL>(8);
73
else if (dimension == 2)
74
{
75
quadrature = std::make_shared<chi_math::AngularQuadratureProdGLC>(8,8);
76
quadrature->OptimizeForPolarSymmetry(4.0*M_PI);
77
}
78
else if (dimension == 3)
79
quadrature = std::make_shared<chi_math::AngularQuadratureProdGLC>(8,8);
80
else
81
throw std::logic_error(fname + "Error with the dimensionality "
82
"of the mesh.");
83
chi::log.Log() << "Quadrature created." << std::endl;
84
85
//============================================= Set/Get params
86
const size_t scat_order = 1;
87
const size_t num_groups = 20;
88
89
quadrature->BuildMomentToDiscreteOperator(scat_order,dimension);
90
quadrature->BuildDiscreteToMomentOperator(scat_order,dimension);
91
92
const auto& m2d = quadrature->GetMomentToDiscreteOperator();
93
const auto& d2m = quadrature->GetDiscreteToMomentOperator();
94
const auto& m_ell_em_map = quadrature->GetMomentToHarmonicsIndexMap();
95
96
const size_t num_moments = m_ell_em_map.size();
97
const size_t num_dirs = quadrature->omegas.size();
98
99
chi::log.Log() << "End Set/Get params." << std::endl;
100
chi::log.Log() << "Num Moments: " << num_moments << std::endl;
101
102
//============================================= Make Unknown Managers
103
const auto VecN = chi_math::UnknownType::VECTOR_N;
104
using Unknown = chi_math::Unknown;
105
106
std::vector<Unknown> phi_uks(num_moments, Unknown(VecN, num_groups));
107
std::vector<Unknown> psi_uks(num_dirs, Unknown(VecN, num_groups));
108
109
const chi_math::UnknownManager phi_uk_man(phi_uks);
110
const chi_math::UnknownManager psi_uk_man(psi_uks);
111
112
const size_t num_local_phi_dofs = sdm.GetNumLocalDOFs(phi_uk_man);
113
const size_t num_local_psi_dofs = sdm.GetNumLocalDOFs(psi_uk_man);
114
115
chi::log.Log() << "End ukmanagers." << std::endl;
116
117
//============================================= Make XSs
118
chi_physics::TransportCrossSections xs;
119
xs.MakeFromCHIxsFile("tests/xs_graphite_pure.cxs");
120
121
//============================================= Initializes vectors
122
std::vector<double> phi_old(num_local_phi_dofs,0.0);
123
std::vector<double> psi_old(num_local_psi_dofs, 0.0);
124
auto source_moments = phi_old;
125
auto phi_new = phi_old;
126
auto q_source = phi_old;
127
128
chi::log.Log() << "End vectors." << std::endl;
129
130
//============================================= Make material source term
131
for (const auto& cell : grid.local_cells)
132
{
133
const auto& cc = cell.centroid;
134
const auto& cell_mapping = sdm.GetCellMapping(cell);
135
const size_t num_nodes = cell_mapping.NumNodes();
136
137
if (cc.x < 0.5 and cc.y < 0.5 and cc.z < 0.5 and
138
cc.x >-0.5 and cc.y >-0.5 and cc.z >-0.5)
139
{
140
for (size_t i=0; i<num_nodes; ++i)
141
{
142
const int64_t dof_map = sdm.MapDOFLocal(cell,i,phi_uk_man,0,0);
143
144
q_source[dof_map] = 1.0;
145
}
146
}
147
}
148
149
//============================================= Precompute cell matrices
150
typedef chi_mesh::Vector3 Vec3;
151
typedef std::vector<Vec3> VecVec3;
152
typedef std::vector<VecVec3> MatVec3;
153
typedef std::vector<double> VecDbl;
154
typedef std::vector<VecDbl> MatDbl;
155
typedef std::vector<MatDbl> VecMatDbl;
156
157
std::vector<MatVec3> cell_Gmatrices;
158
std::vector<MatDbl> cell_Mmatrices;
159
std::vector<VecMatDbl> cell_faceMmatrices;
160
161
for (const auto& cell : grid.local_cells)
162
{
163
const auto& cell_mapping = sdm.GetCellMapping(cell);
164
const size_t num_nodes = cell_mapping.NumNodes();
165
const auto vol_qp_data = cell_mapping.MakeVolumeQuadraturePointData();
166
167
MatVec3 IntV_shapeI_gradshapeJ(num_nodes, VecVec3(num_nodes,Vec3(0,0,0)));
168
MatDbl IntV_shapeI_shapeJ(num_nodes, VecDbl(num_nodes,0.0));
169
170
for (unsigned int i = 0; i < num_nodes; ++i)
171
for (unsigned int j = 0; j < num_nodes; ++j)
172
for (const auto& qp : vol_qp_data.QuadraturePointIndices())
173
{
174
IntV_shapeI_gradshapeJ[i][j]
175
+= vol_qp_data.ShapeValue(i, qp) *
176
vol_qp_data.ShapeGrad(j, qp) *
177
vol_qp_data.JxW(qp);
178
179
IntV_shapeI_shapeJ[i][j]
180
+= vol_qp_data.ShapeValue(i, qp) *
181
vol_qp_data.ShapeValue(j, qp) *
182
vol_qp_data.JxW(qp);
183
}// for qp
184
185
cell_Gmatrices.push_back(std::move(IntV_shapeI_gradshapeJ));
186
cell_Mmatrices.push_back(std::move(IntV_shapeI_shapeJ));
187
188
const size_t num_faces = cell.faces.size();
189
VecMatDbl faces_Mmatrices;
190
for (size_t f=0; f<num_faces; ++f)
191
{
192
const auto face_qp_data = cell_mapping.MakeFaceQuadraturePointData(f);
193
MatDbl IntS_shapeI_shapeJ(num_nodes,VecDbl(num_nodes,0.0));
194
for (unsigned int i = 0; i < num_nodes; ++i)
195
for (unsigned int j = 0; j < num_nodes; ++j)
196
for (const auto& qp : face_qp_data.QuadraturePointIndices())
197
IntS_shapeI_shapeJ[i][j]
198
+= face_qp_data.ShapeValue(i, qp) *
199
face_qp_data.ShapeValue(j, qp) *
200
face_qp_data.JxW(qp);
201
202
faces_Mmatrices.push_back(std::move(IntS_shapeI_shapeJ));
203
}//for face f
204
205
cell_faceMmatrices.push_back(std::move(faces_Mmatrices));
206
207
}//for cell
208
209
chi::log.Log() << "End cell matrices." << std::endl;
210
211
//============================================= Make Grid internal face mapping
212
const auto cell_adj_mapping = sdm.MakeInternalFaceNodeMappings();
213
214
//============================================= Define sweep chunk
215
auto SweepChunk = [&ijk_mapping, &grid, &sdm,
216
&num_moments,
217
&phi_uk_man, &psi_uk_man,
218
&m2d,&d2m,
219
&phi_new, &source_moments, &psi_old,
220
&cell_Gmatrices, &cell_Mmatrices, &cell_faceMmatrices,
221
&cell_adj_mapping]
222
(const std::array<int64_t,3>& ijk,
223
const Vec3& omega,
224
const size_t d,
225
const chi_physics::TransportCrossSections& cell_xs)
226
{
227
const auto cell_global_id = ijk_mapping.MapNDtoLin(ijk[1],ijk[0],ijk[2]);
228
const auto& cell = grid.cells[cell_global_id];
229
const auto cell_local_id = cell.local_id;
230
const auto& cell_mapping = sdm.GetCellMapping(cell);
231
const size_t num_nodes = cell_mapping.NumNodes();
232
const size_t num_faces = cell.faces.size();
233
234
const std::vector<double> zero_vector(num_groups,0.0);
235
236
const auto& G = cell_Gmatrices[cell_local_id];
237
const auto& M = cell_Mmatrices[cell_local_id];
238
239
MatDbl A(num_nodes, VecDbl(num_nodes, 0.0));
240
MatDbl b(num_groups, VecDbl(num_nodes, 0.0));
241
242
//================================= Gradient matrix
243
for (size_t i = 0; i < num_nodes; ++i)
244
for (size_t j = 0; j < num_nodes; ++j)
245
A[i][j] = omega.Dot(G[i][j]);
246
247
//================================= Surface integrals
248
for (size_t f=0; f<num_faces; ++f)
249
{
250
const auto& face = cell.faces[f];
251
const double mu = omega.Dot(face.normal);
252
253
if (mu < 0.0)
254
{
255
const auto& M_surf = cell_faceMmatrices[cell_local_id][f];
256
257
const size_t num_face_nodes = cell_mapping.NumFaceNodes(f);
258
for (size_t fi=0; fi<num_face_nodes; ++fi)
259
{
260
const int i = cell_mapping.MapFaceNode(f,fi);
261
for (size_t fj=0; fj<num_face_nodes; ++fj)
262
{
263
const int j = cell_mapping.MapFaceNode(f,fj);
264
265
const double* upwind_psi = zero_vector.data();
266
if (face.has_neighbor)
267
{
268
const auto& adj_cell = grid.cells[face.neighbor_id];
269
const int aj = cell_adj_mapping[cell.local_id][f][fj];
270
const int64_t ajmap = sdm.MapDOFLocal(adj_cell,aj,psi_uk_man,d,0);
271
upwind_psi = &psi_old[ajmap];
272
}
273
274
const double mu_Nij = -mu * M_surf[i][j];
275
A[i][j] += mu_Nij;
276
for (int g=0; g<num_groups; ++g)
277
b[g][i] += upwind_psi[g]*mu_Nij;
278
}//for fj
279
}//for fi
280
}//if internal incident face
281
}//for face
282
283
for (size_t g=0; g<num_groups; ++g)
284
{
285
auto Atemp = A;
286
VecDbl source(num_nodes, 0.0);
287
//Nodal source moments
288
for (size_t i=0; i<num_nodes; ++i)
289
{
290
double temp_src = 0.0;
291
for (size_t m=0; m<num_moments; ++m)
292
{
293
const int64_t dof_map = sdm.MapDOFLocal(cell,i,phi_uk_man,m,g);
294
temp_src += m2d[m][d]*source_moments[dof_map];
295
}//for m
296
source[i] = temp_src;
297
}//for i
298
299
300
//Mass Matrix and Source
301
const double sigma_tg = cell_xs.sigma_t[g];
302
303
for (int i = 0; i < num_nodes; ++i)
304
{
305
double temp = 0.0;
306
for (int j = 0; j < num_nodes; ++j)
307
{
308
const double Mij = M[i][j];
309
Atemp[i][j] = A[i][j] + Mij*sigma_tg;
310
temp += Mij*source[j];
311
}//for j
312
b[g][i] += temp;
313
}//for i
314
315
// ============================= Solve system
316
chi_math::GaussElimination(Atemp, b[g], static_cast<int>(num_nodes));
317
}//for g
318
319
//Accumulate flux-moments
320
for (size_t m=0; m<num_moments; ++m)
321
{
322
const double wn_d2m = d2m[m][d];
323
for (size_t i=0; i<num_nodes; ++i)
324
{
325
const int64_t dof_map = sdm.MapDOFLocal(cell,i,phi_uk_man,m,0);
326
for (size_t g=0; g<num_groups; ++g)
327
phi_new[dof_map + g] += wn_d2m * b[g][i];
328
}
329
}
330
331
//Save angular fluxes
332
for (size_t i=0; i<num_nodes; ++i)
333
{
334
const int64_t dof_map = sdm.MapDOFLocal(cell,i,psi_uk_man,d,0);
335
for (size_t g=0; g<num_groups; ++g)
336
psi_old[dof_map + g] = b[g][i];
337
}
338
};
339
340
341
//============================================= Define sweep for all dirs
342
auto Sweep = [&num_dirs,&quadrature,Nx,Ny,Nz,&SweepChunk,&xs]()
343
{
344
for (size_t d=0; d<num_dirs; ++d)
345
{
346
const auto &omega = quadrature->omegas[d];
347
const auto &weight = quadrature->weights[d];
348
349
std::vector<int64_t> iorder, jorder, korder;
350
if (omega.x > 0.0) iorder = chi_math::Range<int64_t>(0, Nx);
351
else iorder = chi_math::Range<int64_t>(Nx - 1, -1, -1);
352
if (omega.y > 0.0) jorder = chi_math::Range<int64_t>(0, Ny);
353
else jorder = chi_math::Range<int64_t>(Ny - 1, -1, -1);
354
if (omega.z > 0.0) korder = chi_math::Range<int64_t>(0, Nz);
355
else korder = chi_math::Range<int64_t>(Nz - 1, -1, -1);
356
357
for (auto i: iorder)
358
for (auto j: jorder)
359
for (auto k: korder)
360
SweepChunk({i,j,k}, omega, d, xs);
361
}//for d
362
};
363
364
//============================================= Define SetSource routine
365
auto SetSource = [&source_moments, &phi_old, &q_source, &grid, &sdm,
366
&m_ell_em_map, &xs, num_moments,
367
&phi_uk_man]()
368
{
369
for (const auto& cell : grid.local_cells)
370
{
371
const auto& cell_mapping = sdm.GetCellMapping(cell);
372
const size_t num_nodes = cell_mapping.NumNodes();
373
const auto& S = xs.transfer_matrices;
374
375
for (size_t i=0; i<num_nodes; ++i)
376
{
377
for (size_t m=0; m<num_moments; ++m)
378
{
379
const int64_t dof_map = sdm.MapDOFLocal(cell,i,phi_uk_man,m,0);
380
const auto ell = m_ell_em_map[m].ell;
381
382
for (size_t g=0; g<num_groups; ++g)
383
{
384
//Fixed source
385
source_moments[dof_map + g] = q_source[dof_map + g];
386
387
//Inscattering
388
if (ell < S.size())
389
{
390
double inscat_g = 0.0;
391
for (const auto& [row_g, gprime, sigma_sm] : S[ell].Row(g))
392
inscat_g += sigma_sm * phi_old[dof_map + gprime];
393
394
source_moments[dof_map + g] += inscat_g;
395
}
396
}//for g
397
}//for m
398
}//for node i
399
}//for cell
400
};
401
402
//============================================= Define L-infinite-norm
403
auto ComputeRelativePWChange = [&grid,&sdm,
404
&num_moments,
405
&phi_uk_man]
406
(const std::vector<double>& in_phi_new,
407
const std::vector<double>& in_phi_old)
408
{
409
double pw_change = 0.0;
410
411
for (const auto& cell : grid.local_cells)
412
{
413
const auto& cell_mapping = sdm.GetCellMapping(cell);
414
const size_t num_nodes = cell_mapping.NumNodes();
415
416
for (size_t i=0; i<num_nodes; ++i)
417
{
418
//Get scalar moments
419
const int64_t m0_map = sdm.MapDOFLocal(cell,i,phi_uk_man,0,0);
420
421
const double* phi_new_m0 = &in_phi_new[m0_map];
422
const double* phi_old_m0 = &in_phi_old[m0_map];
423
for (size_t m=0; m<num_moments; ++m)
424
{
425
const int64_t m_map = sdm.MapDOFLocal(cell,i,phi_uk_man,m,0);
426
427
const double* phi_new_m = &in_phi_new[m_map];
428
const double* phi_old_m = &in_phi_old[m_map];
429
430
for (size_t g=0; g<num_groups; ++g)
431
{
432
const double abs_phi_new_g_m0 = std::fabs(phi_new_m0[g]);
433
const double abs_phi_old_g_m0 = std::fabs(phi_old_m0[g]);
434
435
const double max_denominator = std::max(abs_phi_new_g_m0,
436
abs_phi_old_g_m0);
437
438
const double delta_phi = std::fabs(phi_new_m[g] - phi_old_m[g]);
439
440
if (max_denominator >= std::numeric_limits<double>::min())
441
pw_change = std::max(delta_phi/max_denominator,pw_change);
442
else
443
pw_change = std::max(delta_phi,pw_change);
444
}//for g
445
}//for m
446
}//for i
447
}//for cell
448
449
return pw_change;
450
};
451
452
//============================================= Classic Richardson iteration
453
chi::log.Log() << "Starting iterations" << std::endl;
454
for (size_t iter=0; iter<200; ++iter)
455
{
456
phi_new.assign(phi_new.size(), 0.0);
457
//Build rhs
458
SetSource();
459
Sweep();
460
461
const double rel_change = ComputeRelativePWChange(phi_new, phi_old);
462
463
std::stringstream outstr;
464
outstr << "Iteration " << std::setw(5) << iter << " ";
465
{
466
char buffer[100];
467
sprintf(buffer, "%11.3e\n", rel_change);
468
outstr << buffer;
469
}
470
471
chi::log.Log() << outstr.str();
472
473
phi_old = phi_new;
474
475
if (rel_change < 1.0e-6 and iter > 0)
476
break;
477
}//for iteration
478
479
//============================================= Create Field Functions
480
std::vector<std::shared_ptr<chi_physics::FieldFunction>> ff_list;
481
482
ff_list.push_back(std::make_shared<chi_physics::FieldFunction>(
483
"Phi", //Text name
484
sdm_ptr, //Spatial Discr.
485
chi_math::Unknown(chi_math::UnknownType::VECTOR_N,num_groups) //Unknown
486
));
487
488
const std::vector<std::string> dim_strings = {"x","y","z"};
489
for (const std::string& dim : dim_strings)
490
ff_list.push_back(std::make_shared<chi_physics::FieldFunction>(
491
"J-"+dim, //Text name
492
sdm_ptr, //Spatial Discr.
493
chi_math::Unknown(chi_math::UnknownType::VECTOR_N,num_groups) //Unknown
494
));
495
496
//============================================= Localize zeroth moment
497
//This routine extracts a single moment vector
498
//from the vector that contains multiple moments
499
const chi_math::UnknownManager m0_uk_man(
500
{chi_math::Unknown(chi_math::UnknownType::VECTOR_N,num_groups)});
501
const size_t num_m0_dofs = sdm.GetNumLocalDOFs(m0_uk_man);
502
503
std::vector<double> m0_phi(num_m0_dofs, 0.0);
504
std::vector<double> mx_phi(num_m0_dofs, 0.0); //Y(1,1) - X-component
505
std::vector<double> my_phi(num_m0_dofs, 0.0); //Y(1,-1) - Y-component
506
std::vector<double> mz_phi(num_m0_dofs, 0.0); //Y(1,0) - Z-component
507
508
sdm.CopyVectorWithUnknownScope(phi_old, //from vector
509
m0_phi, //to vector
510
phi_uk_man, //from dof-structure
511
0, //from unknown-id
512
m0_uk_man, //to dof-structure
513
0); //to unknown-id
514
515
ff_list[0]->UpdateFieldVector(m0_phi);
516
517
std::array<unsigned int,3> j_map = {0,0,0};
518
if (dimension == 1 and num_moments >= 2) j_map = {0,0,1};
519
if (dimension == 2 and num_moments >= 3) j_map = {2,1,0};
520
if (dimension == 3 and num_moments >= 4) j_map = {3,1,2};
521
522
sdm.CopyVectorWithUnknownScope(
523
phi_old, mx_phi, phi_uk_man, j_map[0], m0_uk_man, 0);
524
sdm.CopyVectorWithUnknownScope(
525
phi_old, my_phi, phi_uk_man, j_map[1], m0_uk_man, 0);
526
sdm.CopyVectorWithUnknownScope(
527
phi_old, mz_phi, phi_uk_man, j_map[2], m0_uk_man, 0);
528
529
ff_list[1]->UpdateFieldVector(mx_phi);
530
ff_list[2]->UpdateFieldVector(my_phi);
531
ff_list[3]->UpdateFieldVector(mz_phi);
532
533
534
//============================================= Update field function
535
chi_physics::FieldFunction::FFList const_ff_list;
536
for (const auto& ff_ptr : ff_list)
537
const_ff_list.push_back(ff_ptr);
538
chi_physics::FieldFunction::ExportMultipleToVTK("SimTest_91a_PWLD",
539
const_ff_list);
540
541
chi::Finalize();
542
return 0;
543
}
544
\endcode
545
546
*/
doc
PAGES
ProgrammersManual
CodingTutorials
CodeTut91a.h
Generated by
1.9.3