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
22int 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*/