Chi-Tech
cfem_diffusion_solver.cc
Go to the documentation of this file.
2
3#include "chi_runtime.h"
4#include "chi_log.h"
5#include "utils/chi_timer.h"
6
9
11
13
15
16//============================================= constructor
17cfem_diffusion::Solver::Solver(const std::string& in_solver_name):
18 chi_physics::Solver(in_solver_name, { {"max_iters", int64_t(500) },
19 {"residual_tolerance", 1.0e-2}})
20{}
21
22//============================================= destructor
24{
25 VecDestroy(&x_);
26 VecDestroy(&b_);
27 MatDestroy(&A_);
28}
29
30//============================================= Initialize
32{
33 const std::string fname = "cfem_diffusion::Solver::Initialize";
34 Chi::log.Log() << "\n"
36 << TextName() << ": Initializing CFEM Diffusion solver ";
37
38 //============================================= Get grid
40 const auto& grid = *grid_ptr_;
41 if (grid_ptr_ == nullptr)
42 throw std::logic_error(std::string(__PRETTY_FUNCTION__) +
43 " No grid defined.");
44
45 Chi::log.Log() << "Global num cells: " << grid.GetGlobalNumberOfCells();
46
47 //============================================= BIDs
48 auto globl_unique_bndry_ids = grid.GetDomainUniqueBoundaryIDs();
49
50 const auto& grid_boundary_id_map = grid_ptr_->GetBoundaryIDMap();
51 for (uint64_t bndry_id : globl_unique_bndry_ids)
52 {
53 if (grid_boundary_id_map.count(bndry_id) == 0)
54 throw std::logic_error(fname + ": Boundary id " +
55 std::to_string(bndry_id) + " does not have a name-assignment.");
56
57 const auto& bndry_name = grid_boundary_id_map.at(bndry_id);
58 if (boundary_preferences_.find(bndry_name) != boundary_preferences_.end())
59 {
60 BoundaryInfo bndry_info = boundary_preferences_.at(bndry_name);
61 auto& bndry_vals = bndry_info.second;
62 switch (bndry_info.first)
63 {
65 {
66 boundaries_.insert(std::make_pair(
67 bndry_id,Boundary{BoundaryType::Reflecting, {0., 0., 0.}}));
68 Chi::log.Log() << "Boundary " << bndry_name << " set to reflecting.";
69 break;
70 }
72 {
73 if (bndry_vals.empty()) bndry_vals.resize(1,0.0);
74 boundaries_.insert(std::make_pair(
75 bndry_id,Boundary{BoundaryType::Dirichlet, {bndry_vals[0], 0., 0.}}));
76 Chi::log.Log() << "Boundary " << bndry_name << " set to dirichlet.";
77 break;
78 }
80 {
81 if (bndry_vals.size()!=3)
82 throw std::logic_error(std::string(__PRETTY_FUNCTION__) +
83 " Robin needs 3 values in bndry vals.");
84 boundaries_.insert(std::make_pair(
85 bndry_id,Boundary{BoundaryType::Robin, {bndry_vals[0],
86 bndry_vals[1],
87 bndry_vals[2]}}));
88 Chi::log.Log() << "Boundary " << bndry_name
89 << " set to robin." << bndry_vals[0]<<","
90 << bndry_vals[1]<<","<<bndry_vals[2];
91 break;
92 }
94 {
95 boundaries_.insert(std::make_pair(
96 bndry_id,Boundary{BoundaryType::Robin, {0.25, 0.5, 0.}}));
97 Chi::log.Log() << "Boundary " << bndry_name << " set to vacuum.";
98 break;
99 }
101 {
102 if (bndry_vals.size()!=3)
103 throw std::logic_error(std::string(__PRETTY_FUNCTION__) +
104 " Neumann needs 3 values in bndry vals.");
105 boundaries_.insert(std::make_pair(
106 bndry_id,Boundary{BoundaryType::Robin, {0., bndry_vals[0],
107 bndry_vals[1]}}));
108 Chi::log.Log() << "Boundary " << bndry_name
109 << " set to neumann." << bndry_vals[0];
110 break;
111 }
112 }//switch boundary type
113 }
114 else
115 {
116 boundaries_.insert(std::make_pair(
117 bndry_id,Boundary{BoundaryType::Dirichlet, {0., 0., 0.}}));
119 << "No boundary preference found for boundary index " << bndry_name
120 << "Dirichlet boundary added with zero boundary value.";
121 }
122 }//for bndry
123
124 //============================================= Make SDM
126 const auto& sdm = *sdm_ptr_;
127
128 const auto& OneDofPerNode = sdm.UNITARY_UNKNOWN_MANAGER;
129 num_local_dofs_ = sdm.GetNumLocalDOFs(OneDofPerNode);
130 num_globl_dofs_ = sdm.GetNumGlobalDOFs(OneDofPerNode);
131
132 Chi::log.Log() << "Num local DOFs: " << num_local_dofs_;
133 Chi::log.Log() << "Num globl DOFs: " << num_globl_dofs_;
134
135 //============================================= Initializes Mats and Vecs
136 const auto n = static_cast<int64_t>(num_local_dofs_);
137 const auto N = static_cast<int64_t>(num_globl_dofs_);
138
142
143 std::vector<int64_t> nodal_nnz_in_diag;
144 std::vector<int64_t> nodal_nnz_off_diag;
145 sdm.BuildSparsityPattern(nodal_nnz_in_diag,nodal_nnz_off_diag, OneDofPerNode);
146
148 nodal_nnz_in_diag,
149 nodal_nnz_off_diag);
150
151 if (field_functions_.empty())
152 {
153 std::string solver_name;
154 if (not TextName().empty()) solver_name = TextName() + "-";
155
156 std::string text_name = solver_name + "phi";
157
158 using namespace chi_math;
159 auto initial_field_function =
160 std::make_shared<chi_physics::FieldFunctionGridBased>(
161 text_name, //Text name
162 sdm_ptr_, //Spatial Discretization
163 Unknown(UnknownType::SCALAR)); //Unknown/Variable
164
165 field_functions_.push_back(initial_field_function);
166 Chi::field_function_stack.push_back(initial_field_function);
167 }//if not ff set
168
169}//end initialize
170
171//========================================================== Execute
173{
174 Chi::log.Log() << "\nExecuting CFEM Diffusion solver";
175
176 const auto& grid = *grid_ptr_;
177 const auto& sdm = *sdm_ptr_;
178
179 lua_State* L = Chi::console.GetConsoleState();
180
181 //============================================= Assemble the system
182 Chi::log.Log() << "Assembling system: ";
183 for (const auto& cell : grid.local_cells)
184 {
185 const auto& cell_mapping = sdm.GetCellMapping(cell);
186 const auto qp_data = cell_mapping.MakeVolumetricQuadraturePointData();
187
188 const auto imat = cell.material_id_;
189 const size_t num_nodes = cell_mapping.NumNodes();
190 MatDbl Acell(num_nodes, VecDbl(num_nodes, 0.0));
191 VecDbl cell_rhs(num_nodes, 0.0);
192
193 for (size_t i=0; i<num_nodes; ++i)
194 {
195 for (size_t j=0; j<num_nodes; ++j)
196 {
197 double entry_aij = 0.0;
198 for (size_t qp : qp_data.QuadraturePointIndices())
199 {
200 entry_aij +=
201 (
202 CallLua_iXYZFunction(L, "D_coef",imat,qp_data.QPointXYZ(qp)) *
203 qp_data.ShapeGrad(i, qp).Dot(qp_data.ShapeGrad(j, qp))
204 +
205 CallLua_iXYZFunction(L, "Sigma_a",imat,qp_data.QPointXYZ(qp)) *
206 qp_data.ShapeValue(i, qp) * qp_data.ShapeValue(j, qp)
207 )
208 *
209 qp_data.JxW(qp);
210 }//for qp
211 Acell[i][j] = entry_aij;
212 }//for j
213 for (size_t qp : qp_data.QuadraturePointIndices())
214 cell_rhs[i] += CallLua_iXYZFunction(L,"Q_ext",imat,qp_data.QPointXYZ(qp)) * qp_data.ShapeValue(i, qp) * qp_data.JxW(qp);
215 }//for i
216
217 //======================= Flag nodes for being on a boundary
218 std::vector<int> dirichlet_count(num_nodes, 0);
219 std::vector<double> dirichlet_value(num_nodes, 0.0);
220
221 const size_t num_faces = cell.faces_.size();
222 for (size_t f=0; f<num_faces; ++f)
223 {
224 const auto& face = cell.faces_[f];
225 // not a boundary face
226 if (face.has_neighbor_) continue;
227
228 const auto& bndry = boundaries_[face.neighbor_id_];
229
230 // Robin boundary
231 if (bndry.type_ == BoundaryType::Robin)
232 {
233 const auto qp_face_data =
234 cell_mapping.MakeSurfaceQuadraturePointData(f);
235 const size_t num_face_nodes = face.vertex_ids_.size();
236
237 const auto& aval = bndry.values_[0];
238 const auto& bval = bndry.values_[1];
239 const auto& fval = bndry.values_[2];
240
241 Chi::log.Log0Verbose1() << "Boundary set as Robin with a,b,f = ("
242 << aval << ","
243 << bval << ","
244 << fval << ") ";
245 // true Robin when a!=0, otherwise, it is a Neumann:
246 // Assert if b=0
247 if (std::fabs(bval) < 1e-8)
248 throw std::logic_error("if b=0, this is a Dirichlet BC, not a Robin BC");
249
250 // loop over nodes of that face
251 for (size_t fi=0; fi<num_face_nodes; ++fi)
252 {
253 const uint i = cell_mapping.MapFaceNode(f,fi);
254
255 double entry_rhsi = 0.0;
256 for (size_t qp : qp_face_data.QuadraturePointIndices() )
257 entry_rhsi += qp_face_data.ShapeValue(i, qp) * qp_face_data.JxW(qp);
258 cell_rhs[i] += fval / bval * entry_rhsi;
259
260 // only do this part if true Robin (i.e., a!=0)
261 if (std::fabs(aval) > 1.0e-8)
262 {
263 for (size_t fj=0; fj<num_face_nodes; ++fj)
264 {
265 const uint j = cell_mapping.MapFaceNode(f,fj);
266
267 double entry_aij = 0.0;
268 for (size_t qp : qp_face_data.QuadraturePointIndices())
269 entry_aij += qp_face_data.ShapeValue(i, qp) *qp_face_data.ShapeValue(j, qp)
270 * qp_face_data.JxW(qp);
271 Acell[i][j] += aval / bval * entry_aij;
272 }//for fj
273 }//end true Robin
274 }//for fi
275 }//if Robin
276
277 // Dirichlet boundary
278 if (bndry.type_ == BoundaryType::Dirichlet)
279 {
280 const size_t num_face_nodes = face.vertex_ids_.size();
281
282 const auto& boundary_value = bndry.values_[0];
283
284 // loop over nodes of that face
285 for (size_t fi=0; fi<num_face_nodes; ++fi)
286 {
287 const uint i = cell_mapping.MapFaceNode(f,fi);
288 dirichlet_count[i] += 1;
289 dirichlet_value[i] += boundary_value;
290 }//for fi
291 }//if Dirichlet
292
293 }//for face f
294
295 //======================= Develop node mapping
296 std::vector<int64_t> imap(num_nodes, 0); //node-mapping
297 for (size_t i=0; i<num_nodes; ++i)
298 imap[i] = sdm.MapDOF(cell, i);
299
300 //======================= Assembly into system
301 for (size_t i=0; i<num_nodes; ++i)
302 {
303 if (dirichlet_count[i]>0) //if Dirichlet boundary node
304 {
305 MatSetValue(A_, imap[i], imap[i], 1.0, ADD_VALUES);
306 // because we use CFEM, a given node is common to several faces
307 const double aux = dirichlet_value[i]/dirichlet_count[i];
308 VecSetValue(b_, imap[i], aux, ADD_VALUES);
309 }
310 else
311 {
312 for (size_t j=0; j<num_nodes; ++j)
313 {
314 if (dirichlet_count[j]==0) // not related to a dirichlet node
315 MatSetValue(A_, imap[i], imap[j], Acell[i][j], ADD_VALUES);
316 else
317 {
318 const double aux = dirichlet_value[j]/dirichlet_count[j];
319 cell_rhs[i] -= Acell[i][j]*aux;
320 }
321 }//for j
322 VecSetValue(b_, imap[i], cell_rhs[i], ADD_VALUES);
323 }
324 }//for i
325 }//for cell
326
327 Chi::log.Log() << "Global assembly";
328
329 MatAssemblyBegin(A_, MAT_FINAL_ASSEMBLY);
330 MatAssemblyEnd(A_, MAT_FINAL_ASSEMBLY);
331 VecAssemblyBegin(b_);
332 VecAssemblyEnd(b_);
333
334 Chi::log.Log() << "Done global assembly";
335
336 //============================================= Create Krylov Solver
337 Chi::log.Log() << "Solving: ";
338 auto petsc_solver =
340 A_, //Matrix
341 TextName(), //Solver name
342 KSPCG, //Solver type
343 PCGAMG, //Preconditioner type
344 basic_options_("residual_tolerance").FloatValue(), //Relative residual tolerance
345 basic_options_("max_iters").IntegerValue() //Max iterations
346 );
347
348 //============================================= Solve
349 KSPSolve(petsc_solver.ksp, b_, x_);
350
351 UpdateFieldFunctions();
352
353 Chi::log.Log() << "Done solving";
354
355}
std::vector< VecDbl > MatDbl
Definition: chi_math.h:13
std::vector< double > VecDbl
Definition: chi_math.h:12
static std::vector< chi_physics::FieldFunctionPtr > field_function_stack
Definition: chi_runtime.h:92
static chi::Timer program_timer
Definition: chi_runtime.h:79
static chi::ChiLog & log
Definition: chi_runtime.h:81
static chi::Console & console
Definition: chi_runtime.h:80
Solver(const std::string &in_solver_name)
std::pair< BoundaryType, std::vector< double > > BoundaryInfo
LogStream Log(LOG_LVL level=LOG_0)
Definition: chi_log.cc:35
LogStream Log0Verbose1()
Definition: chi_log.h:234
lua_State *& GetConsoleState()
Definition: chi_console.h:130
std::string GetTimeString() const
Definition: chi_timer.cc:38
static std::shared_ptr< PieceWiseLinearContinuous > New(const chi_mesh::MeshContinuum &grid, QuadratureOrder q_order=QuadratureOrder::SECOND, CoordinateSystemType cs_type=CoordinateSystemType::CARTESIAN)
chi_mesh::MeshContinuumPtr & GetGrid() const
void InitMatrixSparsity(Mat &A, const std::vector< int64_t > &nodal_nnz_in_diag, const std::vector< int64_t > &nodal_nnz_off_diag)
PETScSolverSetup CreateCommonKrylovSolverSetup(Mat ref_matrix, const std::string &in_solver_name="KSPSolver", const std::string &in_solver_type=KSPGMRES, const std::string &in_preconditioner_type=PCNONE, double in_relative_residual_tolerance=1.0e-6, int64_t in_maximum_iterations=100)
Mat CreateSquareMatrix(int64_t local_size, int64_t global_size)
Vec CreateVector(int64_t local_size, int64_t global_size)
MeshHandler & GetCurrentHandler()
#define uint