• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Open-Sn / opensn / 27259186575

10 Jun 2026 06:11AM UTC coverage: 77.473% (+0.2%) from 77.306%
27259186575

push

github

web-flow
Merge pull request #1084 from wdhawkins/cmfd_tutorial

Adding CMFD tutorial.

24792 of 32001 relevant lines covered (77.47%)

82088528.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.94
/modules/diffusion/diffusion_mip_solver.cc
1
// SPDX-FileCopyrightText: 2024 The OpenSn Authors <https://open-sn.github.io/opensn/>
2
// SPDX-License-Identifier: MIT
3

4
#include "modules/diffusion/diffusion_mip_solver.h"
5
#include "modules/linear_boltzmann_solvers/discrete_ordinates_problem/acceleration/acceleration.h"
6
#include "modules/linear_boltzmann_solvers/lbs_problem/lbs_structs.h"
7
#include "framework/mesh/mesh_continuum/mesh_continuum.h"
8
#include "framework/math/spatial_discretization/finite_element/finite_element_data.h"
9
#include "framework/math/spatial_discretization/spatial_discretization.h"
10
#include "framework/math/spatial_discretization/finite_element/unit_cell_matrices.h"
11
#include "framework/math/petsc_utils/petsc_utils.h"
12
#include "framework/math/functions/function.h"
13
#include "framework/runtime.h"
14
#include "framework/logging/log.h"
15
#include "framework/utils/timer.h"
16
#include <utility>
17

18
namespace opensn
19
{
20

21
DiffusionMIPSolver::DiffusionMIPSolver(std::string name,
873✔
22
                                       const opensn::SpatialDiscretization& sdm,
23
                                       const UnknownManager& uk_man,
24
                                       std::map<uint64_t, BoundaryCondition> bcs,
25
                                       MatID2XSMap map_mat_id_2_xs,
26
                                       const std::vector<UnitCellMatrices>& unit_cell_matrices,
27
                                       const bool suppress_bcs,
28
                                       const bool verbose)
873✔
29
  : DiffusionSolver(std::move(name),
30
                    sdm,
31
                    uk_man,
32
                    std::move(bcs),
33
                    std::move(map_mat_id_2_xs),
34
                    unit_cell_matrices,
35
                    false,
36
                    suppress_bcs,
37
                    verbose)
1,746✔
38
{
39
  if (sdm_.GetType() != SpatialDiscretizationType::PIECEWISE_LINEAR_DISCONTINUOUS)
873✔
40
    throw std::logic_error("acceleration::DiffusionMIPSolver can only be used with PWLD.");
×
41
}
873✔
42

43
void
44
DiffusionMIPSolver::AssembleAand_b_wQpoints(const std::vector<double>& q_vector)
6✔
45
{
46
  const std::string fname = "acceleration::DiffusionMIPSolver::"
6✔
47
                            "AssembleAand_b_wQpoints";
6✔
48
  if (A_ == nullptr or rhs_ == nullptr or ksp_ == nullptr)
6✔
49
    throw std::logic_error(fname + ": Some or all PETSc elements are null. "
×
50
                                   "Check that Initialize has been called.");
×
51
  if (options.verbose)
6✔
52
    log.Log() << program_timer.GetTimeString() << " Starting assembly";
18✔
53

54
  const unsigned int num_groups = uk_man_.unknowns.front().num_components;
6✔
55

56
  OpenSnPETScCall(VecSet(rhs_, 0.0));
6✔
57

58
  for (const auto& cell : grid_->local_cells)
8,631✔
59
  {
60
    const size_t num_faces = cell.faces.size();
8,625✔
61
    const auto& cell_mapping = sdm_.GetCellMapping(cell);
8,625✔
62
    const auto num_nodes = cell_mapping.GetNumNodes();
8,625✔
63
    const auto cc_nodes = cell_mapping.GetNodeLocations();
8,625✔
64
    const auto fe_vol_data = cell_mapping.MakeVolumetricFiniteElementData();
8,625✔
65

66
    const auto& xs = mat_id_2_xs_map_.at(cell.block_id);
8,625✔
67

68
    DenseMatrix<double> cell_A(num_nodes, num_nodes);
8,625✔
69
    Vector<double> cell_rhs(num_nodes);
8,625✔
70
    Vector<PetscInt> cell_idxs(num_nodes);
8,625✔
71
    // For component/group
72
    for (unsigned int g = 0; g < num_groups; ++g)
17,250✔
73
    {
74
      cell_A.Set(0.);
8,625✔
75
      cell_rhs.Set(0.);
8,625✔
76
      for (size_t i = 0; i < num_nodes; ++i)
43,125✔
77
        cell_idxs(i) = static_cast<PetscInt>(sdm_.MapDOF(cell, i, uk_man_, 0, g));
34,500✔
78

79
      // Get coefficient and nodal src
80
      const double Dg = xs.Dg[g];
8,625✔
81
      const double sigr_g = xs.sigR[g];
8,625✔
82

83
      std::vector<double> qg(num_nodes, 0.0);
8,625✔
84
      for (size_t j = 0; j < num_nodes; ++j)
43,125✔
85
        qg[j] = q_vector[sdm_.MapDOFLocal(cell, j, uk_man_, 0, g)];
34,500✔
86

87
      // Assemble continuous terms
88
      for (size_t i = 0; i < num_nodes; ++i)
43,125✔
89
      {
90
        double entry_rhs_i = 0.0; // entry may accumulate over j
91
        for (size_t j = 0; j < num_nodes; ++j)
172,500✔
92
        {
93
          double entry_aij = 0.0;
138,000✔
94
          for (size_t qp : fe_vol_data.GetQuadraturePointIndices())
1,794,000✔
95
          {
96
            entry_aij += Dg * fe_vol_data.ShapeGrad(i, qp).Dot(fe_vol_data.ShapeGrad(j, qp)) *
1,656,000✔
97
                         fe_vol_data.JxW(qp);
1,656,000✔
98

99
            entry_aij += sigr_g * fe_vol_data.ShapeValue(i, qp) * fe_vol_data.ShapeValue(j, qp) *
1,656,000✔
100
                         fe_vol_data.JxW(qp);
1,656,000✔
101

102
            if (not source_function_)
1,656,000✔
103
              entry_rhs_i += fe_vol_data.ShapeValue(i, qp) * fe_vol_data.ShapeValue(j, qp) *
×
104
                             fe_vol_data.JxW(qp) * qg[j];
×
105
          } // for qp
106
          cell_A(i, j) += entry_aij;
138,000✔
107
        } // for j
108

109
        if (source_function_)
34,500✔
110
        {
111
          for (size_t qp : fe_vol_data.GetQuadraturePointIndices())
448,500✔
112
            entry_rhs_i += source_function_(fe_vol_data.QPointXYZ(qp)) *
414,000✔
113
                           fe_vol_data.ShapeValue(i, qp) * fe_vol_data.JxW(qp);
414,000✔
114
        }
115

116
        cell_rhs(i) += entry_rhs_i;
34,500✔
117
      } // for i
118

119
      // Assemble face terms
120
      for (size_t f = 0; f < num_faces; ++f)
43,125✔
121
      {
122
        const auto& face = cell.faces[f];
34,500✔
123
        const auto& n_f = face.normal;
34,500✔
124
        const auto num_face_nodes = cell_mapping.GetNumFaceNodes(f);
34,500✔
125
        const auto fe_srf_data = cell_mapping.MakeSurfaceFiniteElementData(f);
34,500✔
126

127
        const double hm = HPerpendicular(cell, f);
34,500✔
128

129
        if (face.has_neighbor)
34,500✔
130
        {
131
          const auto& adj_cell = grid_->cells[face.neighbor_id];
33,840✔
132
          const auto& adj_cell_mapping = sdm_.GetCellMapping(adj_cell);
33,840✔
133
          const auto ac_nodes = adj_cell_mapping.GetNodeLocations();
33,840✔
134
          const size_t acf = MeshContinuum::MapCellFace(cell, adj_cell, f);
33,840✔
135
          const double hp = HPerpendicular(adj_cell, acf);
33,840✔
136

137
          const auto& adj_xs = mat_id_2_xs_map_.at(adj_cell.block_id);
33,840✔
138
          const double adj_Dg = adj_xs.Dg[g];
33,840✔
139

140
          // Compute kappa
141
          double kappa = 1.0;
33,840✔
142
          if (cell.GetType() == CellType::SLAB)
33,840✔
143
            kappa = fmax(options.penalty_factor * (adj_Dg / hp + Dg / hm) * 0.5, 0.25);
×
144
          if (cell.GetType() == CellType::POLYGON)
33,840✔
145
            kappa = fmax(options.penalty_factor * (adj_Dg / hp + Dg / hm) * 0.5, 0.25);
33,840✔
146
          if (cell.GetType() == CellType::POLYHEDRON)
33,840✔
147
            kappa = fmax(options.penalty_factor * 2.0 * (adj_Dg / hp + Dg / hm) * 0.5, 0.25);
×
148

149
          DenseMatrix<double> adj_A(num_nodes, num_face_nodes, 0.);
33,840✔
150
          DenseMatrix<double> adj_AT(num_face_nodes, num_nodes, 0.);
33,840✔
151
          Vector<PetscInt> adj_idxs(num_face_nodes);
33,840✔
152
          for (size_t fj = 0; fj < num_face_nodes; ++fj)
101,520✔
153
          {
154
            const auto jp =
67,680✔
155
              MapFaceNodeDisc(cell, adj_cell, cc_nodes, ac_nodes, f, acf, fj); // j-plus
67,680✔
156
            adj_idxs(fj) = static_cast<PetscInt>(sdm_.MapDOF(adj_cell, jp, uk_man_, 0, g));
67,680✔
157
          }
158

159
          // Assembly penalty terms
160
          for (size_t fi = 0; fi < num_face_nodes; ++fi)
101,520✔
161
          {
162
            const int i = cell_mapping.MapFaceNode(f, fi);
67,680✔
163

164
            for (size_t fj = 0; fj < num_face_nodes; ++fj)
203,040✔
165
            {
166
              const int jm = cell_mapping.MapFaceNode(f, fj); // j-minus
135,360✔
167

168
              double aij = 0.0;
135,360✔
169
              for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
406,080✔
170
                aij += kappa * fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeValue(jm, qp) *
541,440✔
171
                       fe_srf_data.JxW(qp);
270,720✔
172

173
              cell_A(i, jm) += aij;
135,360✔
174
              adj_A(i, fj) -= aij;
135,360✔
175
            } // for fj
176
          } // for fi
177

178
          // Assemble gradient terms
179
          // For the following comments we use the notation:
180
          // Dk = 0.5* n dot nabla bk
181

182
          // 0.5*D* n dot (b_j^+ - b_j^-)*nabla b_i^-
183
          for (size_t i = 0; i < num_nodes; ++i)
169,200✔
184
          {
185
            for (size_t fj = 0; fj < num_face_nodes; ++fj)
406,080✔
186
            {
187
              const int jm = cell_mapping.MapFaceNode(f, fj); // j-minus
270,720✔
188

189
              Vector3 vec_aij;
270,720✔
190
              for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
812,160✔
191
                vec_aij += fe_srf_data.ShapeValue(jm, qp) * fe_srf_data.ShapeGrad(i, qp) *
541,440✔
192
                           fe_srf_data.JxW(qp);
1,082,880✔
193
              const double aij = -0.5 * Dg * n_f.Dot(vec_aij);
270,720✔
194

195
              cell_A(i, jm) += aij;
270,720✔
196
              adj_A(i, fj) -= aij;
270,720✔
197
            } // for fj
198
          } // for i
199

200
          // 0.5*D* n dot (b_i^+ - b_i^-)*nabla b_j^-
201
          for (size_t fi = 0; fi < num_face_nodes; ++fi)
101,520✔
202
          {
203
            const int im = cell_mapping.MapFaceNode(f, fi); // i-minus
67,680✔
204

205
            for (size_t j = 0; j < num_nodes; ++j)
338,400✔
206
            {
207
              Vector3 vec_aij;
270,720✔
208
              for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
812,160✔
209
                vec_aij += fe_srf_data.ShapeValue(im, qp) * fe_srf_data.ShapeGrad(j, qp) *
541,440✔
210
                           fe_srf_data.JxW(qp);
1,082,880✔
211
              const double aij = -0.5 * Dg * n_f.Dot(vec_aij);
270,720✔
212

213
              cell_A(im, j) += aij;
270,720✔
214
              adj_AT(fi, j) -= aij;
270,720✔
215
            } // for j
216
          } // for fi
217

218
          auto nn = static_cast<PetscInt>(num_nodes);
33,840✔
219
          auto nf = static_cast<PetscInt>(num_face_nodes);
33,840✔
220
          OpenSnPETScCall(
33,840✔
221
            MatSetValues(A_, nn, cell_idxs.data(), nf, adj_idxs.data(), adj_A.data(), ADD_VALUES));
222

223
          OpenSnPETScCall(
33,840✔
224
            MatSetValues(A_, nf, adj_idxs.data(), nn, cell_idxs.data(), adj_AT.data(), ADD_VALUES));
225

226
        } // internal face
101,520✔
227
        else
228
        {
229
          BoundaryCondition bc;
660✔
230
          if (bcs_.count(face.neighbor_id) > 0)
660✔
231
            bc = bcs_.at(face.neighbor_id);
660✔
232

233
          if (bc.type == BCType::DIRICHLET)
660✔
234
          {
235
            const double bc_value = bc.values[0];
660✔
236

237
            // Compute kappa
238
            double kappa = 1.0;
660✔
239
            if (cell.GetType() == CellType::SLAB)
660✔
240
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
×
241
            if (cell.GetType() == CellType::POLYGON)
660✔
242
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
660✔
243
            if (cell.GetType() == CellType::POLYHEDRON)
660✔
244
              kappa = fmax(options.penalty_factor * 2.0 * Dg / hm, 0.25);
×
245

246
            // Assembly penalty terms
247
            for (size_t fi = 0; fi < num_face_nodes; ++fi)
1,980✔
248
            {
249
              const int i = cell_mapping.MapFaceNode(f, fi);
1,320✔
250

251
              for (size_t fj = 0; fj < num_face_nodes; ++fj)
3,960✔
252
              {
253
                const int jm = cell_mapping.MapFaceNode(f, fj);
2,640✔
254

255
                double aij = 0.0;
2,640✔
256
                for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
7,920✔
257
                  aij += kappa * fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeValue(jm, qp) *
10,560✔
258
                         fe_srf_data.JxW(qp);
5,280✔
259
                double aij_bc_value = aij * bc_value;
2,640✔
260

261
                if (ref_solution_function_)
2,640✔
262
                {
263
                  aij_bc_value = 0.0;
2,640✔
264
                  for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
7,920✔
265
                    aij_bc_value += kappa * ref_solution_function_(fe_srf_data.QPointXYZ(qp)) *
5,280✔
266
                                    fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeValue(jm, qp) *
10,560✔
267
                                    fe_srf_data.JxW(qp);
5,280✔
268
                }
269

270
                cell_A(i, jm) += aij;
2,640✔
271
                cell_rhs(i) += aij_bc_value;
2,640✔
272
              } // for fj
273
            } // for fi
274

275
            // Assemble gradient terms
276
            // For the following comments we use the notation:
277
            // Dk = n dot nabla bk
278

279
            // D* n dot (b_j^+ - b_j^-)*nabla b_i^-
280
            for (size_t i = 0; i < num_nodes; ++i)
3,300✔
281
            {
282
              for (size_t j = 0; j < num_nodes; ++j)
13,200✔
283
              {
284
                Vector3 vec_aij;
10,560✔
285
                for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
31,680✔
286
                  vec_aij += fe_srf_data.ShapeValue(j, qp) * fe_srf_data.ShapeGrad(i, qp) *
21,120✔
287
                               fe_srf_data.JxW(qp) +
42,240✔
288
                             fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeGrad(j, qp) *
42,240✔
289
                               fe_srf_data.JxW(qp);
63,360✔
290
                const double aij = -Dg * n_f.Dot(vec_aij);
10,560✔
291

292
                double aij_bc_value = aij * bc_value;
10,560✔
293

294
                if (ref_solution_function_)
10,560✔
295
                {
296
                  Vector3 vec_aij_mms;
10,560✔
297
                  for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
31,680✔
298
                    vec_aij_mms += ref_solution_function_(fe_srf_data.QPointXYZ(qp)) *
63,360✔
299
                                   (fe_srf_data.ShapeValue(j, qp) * fe_srf_data.ShapeGrad(i, qp) *
21,120✔
300
                                      fe_srf_data.JxW(qp) +
63,360✔
301
                                    fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeGrad(j, qp) *
21,120✔
302
                                      fe_srf_data.JxW(qp));
63,360✔
303
                  aij_bc_value = -Dg * n_f.Dot(vec_aij_mms);
10,560✔
304
                }
305

306
                cell_A(i, j) += aij;
10,560✔
307
                cell_rhs(i) += aij_bc_value;
10,560✔
308
              } // for fj
309
            } // for i
310
          } // Dirichlet BC
311
          else if (bc.type == BCType::ROBIN)
×
312
          {
313
            const double aval = bc.values[0];
×
314
            const double bval = bc.values[1];
×
315
            const double fval = bc.values[2];
×
316

317
            if (std::fabs(bval) < 1.0e-12)
×
318
              continue; // a and f assumed zero
×
319

320
            for (size_t fi = 0; fi < num_face_nodes; ++fi)
×
321
            {
322
              const int i = cell_mapping.MapFaceNode(f, fi);
×
323

324
              if (std::fabs(aval) >= 1.0e-12)
×
325
              {
326
                for (size_t fj = 0; fj < num_face_nodes; ++fj)
×
327
                {
328
                  const int j = cell_mapping.MapFaceNode(f, fj);
×
329

330
                  double aij = 0.0;
×
331
                  for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
×
332
                    aij += fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeValue(j, qp) *
×
333
                           fe_srf_data.JxW(qp);
×
334
                  aij *= (aval / bval);
×
335

336
                  cell_A(i, j) += aij;
×
337
                } // for fj
338
              } // if a nonzero
339

340
              if (std::fabs(fval) >= 1.0e-12)
×
341
              {
342
                double rhs_val = 0.0;
×
343
                for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
×
344
                  rhs_val += fe_srf_data.ShapeValue(i, qp) * fe_srf_data.JxW(qp);
×
345
                rhs_val *= (fval / bval);
×
346

347
                cell_rhs(i) += rhs_val;
×
348
              } // if f nonzero
349
            } // for fi
350
          } // Robin BC
351
        } // boundary face
352
      } // for face
34,500✔
353

354
      auto nn = static_cast<PetscInt>(num_nodes);
8,625✔
355
      OpenSnPETScCall(
8,625✔
356
        MatSetValues(A_, nn, cell_idxs.data(), nn, cell_idxs.data(), cell_A.data(), ADD_VALUES));
357
      OpenSnPETScCall(VecSetValues(rhs_, nn, cell_idxs.data(), cell_rhs.data(), ADD_VALUES));
8,625✔
358
    } // for g
8,625✔
359
  } // for cell
25,875✔
360

361
  OpenSnPETScCall(MatAssemblyBegin(A_, MAT_FINAL_ASSEMBLY));
6✔
362
  OpenSnPETScCall(MatAssemblyEnd(A_, MAT_FINAL_ASSEMBLY));
6✔
363
  OpenSnPETScCall(VecAssemblyBegin(rhs_));
6✔
364
  OpenSnPETScCall(VecAssemblyEnd(rhs_));
6✔
365

366
  if (options.perform_symmetry_check)
6✔
367
  {
368
    PetscBool symmetry = PETSC_FALSE;
6✔
369
    OpenSnPETScCall(MatIsSymmetric(A_, 1.0e-6, &symmetry));
6✔
370
    if (symmetry == PETSC_FALSE)
6✔
371
      throw std::logic_error(fname + ":Symmetry check failed");
×
372
  }
373

374
  OpenSnPETScCall(KSPSetOperators(ksp_, A_, A_));
6✔
375

376
  if (options.verbose)
6✔
377
    log.Log() << program_timer.GetTimeString() << " Assembly completed";
18✔
378

379
  PC pc = nullptr;
6✔
380
  OpenSnPETScCall(KSPGetPC(ksp_, &pc));
6✔
381
  OpenSnPETScCall(PCSetUp(pc));
6✔
382

383
  OpenSnPETScCall(KSPSetUp(ksp_));
6✔
384
}
6✔
385

386
void
387
DiffusionMIPSolver::Assemble_b_wQpoints(const std::vector<double>& q_vector)
6✔
388
{
389
  const std::string fname = "acceleration::DiffusionMIPSolver::"
6✔
390
                            "AssembleAand_b_wQpoints";
6✔
391
  if (A_ == nullptr or rhs_ == nullptr or ksp_ == nullptr)
6✔
392
    throw std::logic_error(fname + ": Some or all PETSc elements are null. "
×
393
                                   "Check that Initialize has been called.");
×
394
  if (options.verbose)
6✔
395
    log.Log() << program_timer.GetTimeString() << " Starting assembly";
18✔
396

397
  OpenSnPETScCall(VecSet(rhs_, 0.0));
6✔
398

399
  for (const auto& cell : grid_->local_cells)
8,631✔
400
  {
401
    const size_t num_faces = cell.faces.size();
8,625✔
402
    const auto& cell_mapping = sdm_.GetCellMapping(cell);
8,625✔
403
    const auto num_nodes = cell_mapping.GetNumNodes();
8,625✔
404
    const auto fe_vol_data = cell_mapping.MakeVolumetricFiniteElementData();
8,625✔
405
    const unsigned int num_groups = uk_man_.unknowns.front().num_components;
8,625✔
406

407
    const auto& xs = mat_id_2_xs_map_.at(cell.block_id);
8,625✔
408

409
    Vector<double> cell_rhs(num_nodes);
8,625✔
410
    Vector<PetscInt> cell_idxs(num_nodes);
8,625✔
411
    // For component/group
412
    for (unsigned int g = 0; g < num_groups; ++g)
17,250✔
413
    {
414
      cell_rhs.Set(0.);
8,625✔
415
      for (size_t i = 0; i < num_nodes; ++i)
43,125✔
416
        cell_idxs(i) = static_cast<PetscInt>(sdm_.MapDOF(cell, i, uk_man_, 0, g));
34,500✔
417

418
      // Get coefficient and nodal src
419
      const double Dg = xs.Dg[g];
8,625✔
420

421
      std::vector<double> qg(num_nodes, 0.0);
8,625✔
422
      for (size_t j = 0; j < num_nodes; ++j)
43,125✔
423
        qg[j] = q_vector[sdm_.MapDOFLocal(cell, j, uk_man_, 0, g)];
34,500✔
424

425
      // Assemble continuous terms
426
      for (size_t i = 0; i < num_nodes; ++i)
43,125✔
427
      {
428
        double entry_rhs_i = 0.0; // entry may accumulate over j
34,500✔
429
        if (not source_function_)
34,500✔
430
          for (size_t j = 0; j < num_nodes; ++j)
×
431
          {
432
            for (size_t qp : fe_vol_data.GetQuadraturePointIndices())
×
433
            {
434
              entry_rhs_i += fe_vol_data.ShapeValue(i, qp) * fe_vol_data.ShapeValue(j, qp) *
×
435
                             fe_vol_data.JxW(qp) * qg[j];
×
436
            } // for qp
437
          } // for j
438
        else
439
        {
440
          for (size_t qp : fe_vol_data.GetQuadraturePointIndices())
448,500✔
441
            entry_rhs_i += source_function_(fe_vol_data.QPointXYZ(qp)) *
414,000✔
442
                           fe_vol_data.ShapeValue(i, qp) * fe_vol_data.JxW(qp);
414,000✔
443
        }
444

445
        cell_rhs(i) += entry_rhs_i;
34,500✔
446
      } // for i
447

448
      // Assemble face terms
449
      for (size_t f = 0; f < num_faces; ++f)
43,125✔
450
      {
451
        const auto& face = cell.faces[f];
34,500✔
452
        const auto& n_f = face.normal;
34,500✔
453
        const size_t num_face_nodes = cell_mapping.GetNumFaceNodes(f);
34,500✔
454
        const auto fe_srf_data = cell_mapping.MakeSurfaceFiniteElementData(f);
34,500✔
455

456
        const double hm = HPerpendicular(cell, f);
34,500✔
457

458
        if (not face.has_neighbor and not suppress_bcs_)
34,500✔
459
        {
460
          BoundaryCondition bc;
660✔
461
          if (bcs_.count(face.neighbor_id) > 0)
660✔
462
            bc = bcs_.at(face.neighbor_id);
660✔
463

464
          if (bc.type == BCType::DIRICHLET)
660✔
465
          {
466
            const double bc_value = bc.values[0];
660✔
467

468
            // Compute kappa
469
            double kappa = 1.0;
660✔
470
            if (cell.GetType() == CellType::SLAB)
660✔
471
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
×
472
            if (cell.GetType() == CellType::POLYGON)
660✔
473
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
660✔
474
            if (cell.GetType() == CellType::POLYHEDRON)
660✔
475
              kappa = fmax(options.penalty_factor * 2.0 * Dg / hm, 0.25);
×
476

477
            // Assembly penalty terms
478
            for (size_t fi = 0; fi < num_face_nodes; ++fi)
1,980✔
479
            {
480
              const int i = cell_mapping.MapFaceNode(f, fi);
1,320✔
481

482
              for (size_t fj = 0; fj < num_face_nodes; ++fj)
3,960✔
483
              {
484
                const int jm = cell_mapping.MapFaceNode(f, fj);
2,640✔
485

486
                double aij = 0.0;
2,640✔
487
                for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
7,920✔
488
                  aij += kappa * fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeValue(jm, qp) *
10,560✔
489
                         fe_srf_data.JxW(qp);
5,280✔
490
                double aij_bc_value = aij * bc_value;
2,640✔
491

492
                if (ref_solution_function_)
2,640✔
493
                {
494
                  aij_bc_value = 0.0;
2,640✔
495
                  for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
7,920✔
496
                    aij_bc_value += kappa * ref_solution_function_(fe_srf_data.QPointXYZ(qp)) *
5,280✔
497
                                    fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeValue(jm, qp) *
10,560✔
498
                                    fe_srf_data.JxW(qp);
5,280✔
499
                }
500

501
                cell_rhs(i) += aij_bc_value;
2,640✔
502
              } // for fj
503
            } // for fi
504

505
            // Assemble gradient terms
506
            // For the following comments we use the notation:
507
            // Dk = 0.5* n dot nabla bk
508

509
            // 0.5*D* n dot (b_j^+ - b_j^-)*nabla b_i^-
510
            for (size_t i = 0; i < num_nodes; ++i)
3,300✔
511
            {
512
              for (size_t j = 0; j < num_nodes; ++j)
13,200✔
513
              {
514
                Vector3 vec_aij;
10,560✔
515
                for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
31,680✔
516
                  vec_aij += fe_srf_data.ShapeValue(j, qp) * fe_srf_data.ShapeGrad(i, qp) *
21,120✔
517
                               fe_srf_data.JxW(qp) +
42,240✔
518
                             fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeGrad(j, qp) *
42,240✔
519
                               fe_srf_data.JxW(qp);
63,360✔
520
                const double aij = -Dg * n_f.Dot(vec_aij);
10,560✔
521

522
                double aij_bc_value = aij * bc_value;
10,560✔
523

524
                if (ref_solution_function_)
10,560✔
525
                {
526
                  Vector3 vec_aij_mms;
10,560✔
527
                  for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
31,680✔
528
                    vec_aij_mms += ref_solution_function_(fe_srf_data.QPointXYZ(qp)) *
63,360✔
529
                                   (fe_srf_data.ShapeValue(j, qp) * fe_srf_data.ShapeGrad(i, qp) *
21,120✔
530
                                      fe_srf_data.JxW(qp) +
63,360✔
531
                                    fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeGrad(j, qp) *
21,120✔
532
                                      fe_srf_data.JxW(qp));
63,360✔
533
                  aij_bc_value = -Dg * n_f.Dot(vec_aij_mms);
10,560✔
534
                }
535

536
                cell_rhs(i) += aij_bc_value;
10,560✔
537
              } // for fj
538
            } // for i
539
          } // Dirichlet BC
540
          else if (bc.type == BCType::ROBIN)
×
541
          {
542
            const double bval = bc.values[1];
×
543
            const double fval = bc.values[2];
×
544

545
            if (std::fabs(bval) < 1.0e-12)
×
546
              continue; // a and f assumed zero
×
547

548
            for (size_t fi = 0; fi < num_face_nodes; ++fi)
×
549
            {
550
              const int i = cell_mapping.MapFaceNode(f, fi);
×
551

552
              if (std::fabs(fval) >= 1.0e-12)
×
553
              {
554
                double rhs_val = 0.0;
×
555
                for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
×
556
                  rhs_val += fe_srf_data.ShapeValue(i, qp) * fe_srf_data.JxW(qp);
×
557
                rhs_val *= (fval / bval);
×
558

559
                cell_rhs(i) += rhs_val;
×
560
              } // if f nonzero
561
            } // for fi
562
          } // Robin BC
563
        } // boundary face
564
      } // for face
34,500✔
565
      auto nn = static_cast<PetscInt>(num_nodes);
8,625✔
566
      OpenSnPETScCall(VecSetValues(rhs_, nn, cell_idxs.data(), cell_rhs.data(), ADD_VALUES));
8,625✔
567
    } // for g
8,625✔
568
  } // for cell
17,250✔
569

570
  OpenSnPETScCall(VecAssemblyBegin(rhs_));
6✔
571
  OpenSnPETScCall(VecAssemblyEnd(rhs_));
6✔
572

573
  OpenSnPETScCall(KSPSetOperators(ksp_, A_, A_));
6✔
574

575
  if (options.verbose)
6✔
576
    log.Log() << program_timer.GetTimeString() << " Assembly completed";
18✔
577

578
  PC pc = nullptr;
6✔
579
  OpenSnPETScCall(KSPGetPC(ksp_, &pc));
6✔
580
  OpenSnPETScCall(PCSetUp(pc));
6✔
581

582
  OpenSnPETScCall(KSPSetUp(ksp_));
6✔
583
}
6✔
584

585
void
586
DiffusionMIPSolver::AssembleAand_b(const std::vector<double>& q_vector)
867✔
587
{
588
  const std::string fname = "acceleration::DiffusionMIPSolver::"
867✔
589
                            "AssembleAand_b";
867✔
590
  if (A_ == nullptr or rhs_ == nullptr or ksp_ == nullptr)
867✔
591
    throw std::logic_error(fname + ": Some or all PETSc elements are null. "
×
592
                                   "Check that Initialize has been called.");
×
593
  if (options.verbose)
867✔
594
    log.Log() << program_timer.GetTimeString() << " Starting assembly";
12✔
595

596
  const unsigned int num_groups = uk_man_.unknowns.front().num_components;
867✔
597

598
  OpenSnPETScCall(VecSet(rhs_, 0.0));
867✔
599
  for (const auto& cell : grid_->local_cells)
36,275✔
600
  {
601
    const size_t num_faces = cell.faces.size();
35,408✔
602
    const auto& cell_mapping = sdm_.GetCellMapping(cell);
35,408✔
603
    const auto num_nodes = cell_mapping.GetNumNodes();
35,408✔
604
    const auto cc_nodes = cell_mapping.GetNodeLocations();
35,408✔
605
    const auto& unit_cell_matrices = unit_cell_matrices_[cell.local_id];
35,408✔
606

607
    const auto& intV_gradshapeI_gradshapeJ = unit_cell_matrices.intV_gradshapeI_gradshapeJ;
35,408✔
608
    const auto& intV_shapeI_shapeJ = unit_cell_matrices.intV_shapeI_shapeJ;
35,408✔
609

610
    const auto& xs = mat_id_2_xs_map_.at(cell.block_id);
35,408✔
611

612
    DenseMatrix<double> cell_A(num_nodes, num_nodes);
35,408✔
613
    Vector<double> cell_rhs(num_nodes);
35,408✔
614
    Vector<PetscInt> cell_idxs(num_nodes);
35,408✔
615
    std::vector<double> qg(num_nodes, 0.0);
35,408✔
616
    for (unsigned int g = 0; g < num_groups; ++g)
756,965✔
617
    {
618
      cell_A.Set(0.);
721,557✔
619
      cell_rhs.Set(0.);
721,557✔
620
      for (size_t i = 0; i < num_nodes; ++i)
4,393,047✔
621
        cell_idxs(i) = static_cast<PetscInt>(sdm_.MapDOF(cell, i, uk_man_, 0, g));
3,671,490✔
622

623
      // Get coefficient and nodal src
624
      const double Dg = xs.Dg[g];
721,557✔
625
      const double sigr_g = xs.sigR[g];
721,557✔
626

627
      for (size_t j = 0; j < num_nodes; ++j)
4,393,047✔
628
        qg[j] = q_vector[sdm_.MapDOFLocal(cell, j, uk_man_, 0, g)];
3,671,490✔
629

630
      // Assemble continuous terms
631
      for (size_t i = 0; i < num_nodes; ++i)
4,393,047✔
632
      {
633
        double entry_rhs_i = 0.0;
634
        for (size_t j = 0; j < num_nodes; ++j)
26,669,574✔
635
        {
636
          const double entry_aij =
22,998,084✔
637
            Dg * intV_gradshapeI_gradshapeJ(i, j) + sigr_g * intV_shapeI_shapeJ(i, j);
22,998,084✔
638

639
          entry_rhs_i += intV_shapeI_shapeJ(i, j) * qg[j];
22,998,084✔
640

641
          cell_A(i, j) += entry_aij;
22,998,084✔
642
        } // for j
643

644
        cell_rhs(i) += entry_rhs_i;
3,671,490✔
645
      } // for i
646

647
      // Assemble face terms
648
      for (size_t f = 0; f < num_faces; ++f)
3,831,247✔
649
      {
650
        const auto& face = cell.faces[f];
3,109,690✔
651
        const auto& n_f = face.normal;
3,109,690✔
652
        const auto num_face_nodes = cell_mapping.GetNumFaceNodes(f);
3,109,690✔
653

654
        const auto& intS_shapeI_shapeJ = unit_cell_matrices.intS_shapeI_shapeJ[f];
3,109,690✔
655
        const auto& intS_shapeI_gradshapeJ = unit_cell_matrices.intS_shapeI_gradshapeJ[f];
3,109,690✔
656
        const auto& intS_shapeI = unit_cell_matrices.intS_shapeI[f];
3,109,690✔
657

658
        const double hm = HPerpendicular(cell, f);
3,109,690✔
659

660
        if (face.has_neighbor)
3,109,690✔
661
        {
662
          const auto& adj_cell = grid_->cells[face.neighbor_id];
2,840,726✔
663
          const auto& adj_cell_mapping = sdm_.GetCellMapping(adj_cell);
2,840,726✔
664
          const auto ac_nodes = adj_cell_mapping.GetNodeLocations();
2,840,726✔
665
          const size_t acf = MeshContinuum::MapCellFace(cell, adj_cell, f);
2,840,726✔
666
          const double hp = HPerpendicular(adj_cell, acf);
2,840,726✔
667

668
          const auto& adj_xs = mat_id_2_xs_map_.at(adj_cell.block_id);
2,840,726✔
669
          const double adj_Dg = adj_xs.Dg[g];
2,840,726✔
670

671
          // Compute kappa
672
          double kappa = 1.0;
2,840,726✔
673
          if (cell.GetType() == CellType::SLAB)
2,840,726✔
674
            kappa = fmax(options.penalty_factor * (adj_Dg / hp + Dg / hm) * 0.5, 0.25);
337,662✔
675
          if (cell.GetType() == CellType::POLYGON)
2,840,726✔
676
            kappa = fmax(options.penalty_factor * (adj_Dg / hp + Dg / hm) * 0.5, 0.25);
1,019,544✔
677
          if (cell.GetType() == CellType::POLYHEDRON)
2,840,726✔
678
            kappa = fmax(options.penalty_factor * 2.0 * (adj_Dg / hp + Dg / hm) * 0.5, 0.25);
1,483,520✔
679

680
          DenseMatrix<double> adj_A(num_nodes, num_face_nodes, 0.);
2,840,726✔
681
          DenseMatrix<double> adj_AT(num_face_nodes, num_nodes, 0.);
2,840,726✔
682
          Vector<PetscInt> adj_idxs(num_face_nodes);
2,840,726✔
683
          for (size_t fj = 0; fj < num_face_nodes; ++fj)
11,151,556✔
684
          {
685
            const auto jp =
8,310,830✔
686
              MapFaceNodeDisc(cell, adj_cell, cc_nodes, ac_nodes, f, acf, fj); // j-plus
8,310,830✔
687
            adj_idxs(fj) = static_cast<PetscInt>(sdm_.MapDOF(adj_cell, jp, uk_man_, 0, g));
8,310,830✔
688
          }
689

690
          // Assembly penalty terms
691
          for (size_t fi = 0; fi < num_face_nodes; ++fi)
11,151,556✔
692
          {
693
            const int i = cell_mapping.MapFaceNode(f, fi);
8,310,830✔
694

695
            for (size_t fj = 0; fj < num_face_nodes; ++fj)
36,462,988✔
696
            {
697
              const int jm = cell_mapping.MapFaceNode(f, fj); // j-minus
28,152,158✔
698

699
              const double aij = kappa * intS_shapeI_shapeJ(i, jm);
28,152,158✔
700

701
              cell_A(i, jm) += aij;
28,152,158✔
702
              adj_A(i, fj) -= aij;
28,152,158✔
703
            } // for fj
704
          } // for fi
705

706
          // Assemble gradient terms
707
          // For the following comments we use the notation:
708
          // Dk = 0.5* n dot nabla bk
709

710
          // 0.5*D* n dot (b_j^+ - b_j^-)*nabla b_i^-
711
          for (size_t i = 0; i < num_nodes; ++i)
19,462,386✔
712
          {
713
            for (size_t fj = 0; fj < num_face_nodes; ++fj)
72,925,976✔
714
            {
715
              const int jm = cell_mapping.MapFaceNode(f, fj); // j-minus
56,304,316✔
716

717
              const double aij = -0.5 * Dg * n_f.Dot(intS_shapeI_gradshapeJ(jm, i));
56,304,316✔
718

719
              cell_A(i, jm) += aij;
56,304,316✔
720
              adj_A(i, fj) -= aij;
56,304,316✔
721
            } // for fj
722
          } // for i
723

724
          // 0.5*D* n dot (b_i^+ - b_i^-)*nabla b_j^-
725
          for (size_t fi = 0; fi < num_face_nodes; ++fi)
11,151,556✔
726
          {
727
            const int im = cell_mapping.MapFaceNode(f, fi); // i-minus
8,310,830✔
728

729
            for (size_t j = 0; j < num_nodes; ++j)
64,615,146✔
730
            {
731
              const double aij = -0.5 * Dg * n_f.Dot(intS_shapeI_gradshapeJ(im, j));
56,304,316✔
732

733
              cell_A(im, j) += aij;
56,304,316✔
734
              adj_AT(fi, j) -= aij;
56,304,316✔
735
            } // for j
736
          } // for fi
737

738
          auto nn = static_cast<PetscInt>(num_nodes);
2,840,726✔
739
          auto nf = static_cast<PetscInt>(num_face_nodes);
2,840,726✔
740
          OpenSnPETScCall(
2,840,726✔
741
            MatSetValues(A_, nn, cell_idxs.data(), nf, adj_idxs.data(), adj_A.data(), ADD_VALUES));
742

743
          OpenSnPETScCall(
2,840,726✔
744
            MatSetValues(A_, nf, adj_idxs.data(), nn, cell_idxs.data(), adj_AT.data(), ADD_VALUES));
745

746
        } // internal face
8,522,178✔
747
        else
748
        {
749
          BoundaryCondition bc;
268,964✔
750
          if (bcs_.count(face.neighbor_id) > 0)
268,964✔
751
            bc = bcs_.at(face.neighbor_id);
268,964✔
752

753
          if (bc.type == BCType::DIRICHLET)
268,964✔
754
          {
755
            const double bc_value = bc.values[0];
239,124✔
756

757
            // Compute kappa
758
            double kappa = 1.0;
239,124✔
759
            if (cell.GetType() == CellType::SLAB)
239,124✔
760
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
676✔
761
            if (cell.GetType() == CellType::POLYGON)
239,124✔
762
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
49,168✔
763
            if (cell.GetType() == CellType::POLYHEDRON)
239,124✔
764
              kappa = fmax(options.penalty_factor * 2.0 * Dg / hm, 0.25);
189,280✔
765

766
            // Assembly penalty terms
767
            for (size_t fi = 0; fi < num_face_nodes; ++fi)
1,095,256✔
768
            {
769
              const int i = cell_mapping.MapFaceNode(f, fi);
856,132✔
770

771
              for (size_t fj = 0; fj < num_face_nodes; ++fj)
4,081,960✔
772
              {
773
                const int jm = cell_mapping.MapFaceNode(f, fj);
3,225,828✔
774

775
                const double aij = kappa * intS_shapeI_shapeJ(i, jm);
3,225,828✔
776
                const double aij_bc_value = aij * bc_value;
3,225,828✔
777

778
                cell_A(i, jm) += aij;
3,225,828✔
779
                cell_rhs(i) += aij_bc_value;
3,225,828✔
780
              } // for fj
781
            } // for fi
782

783
            // Assemble gradient terms
784
            // For the following comments we use the notation:
785
            // Dk = n dot nabla bk
786

787
            // D* n dot (b_j^+ - b_j^-)*nabla b_i^-
788
            for (size_t i = 0; i < num_nodes; ++i)
1,951,388✔
789
            {
790
              for (size_t j = 0; j < num_nodes; ++j)
14,615,576✔
791
              {
792
                const double aij =
12,903,312✔
793
                  -Dg * n_f.Dot(intS_shapeI_gradshapeJ(j, i) + intS_shapeI_gradshapeJ(i, j));
12,903,312✔
794
                const double aij_bc_value = aij * bc_value;
12,903,312✔
795

796
                cell_A(i, j) += aij;
12,903,312✔
797
                cell_rhs(i) += aij_bc_value;
12,903,312✔
798
              } // for fj
799
            } // for i
800
          } // Dirichlet BC
801
          else if (bc.type == BCType::ROBIN)
29,840✔
802
          {
803
            const double aval = bc.values[0];
29,840✔
804
            const double bval = bc.values[1];
29,840✔
805
            const double fval = bc.values[2];
29,840✔
806

807
            if (std::fabs(bval) < 1.0e-12)
29,840✔
808
              continue; // a and f assumed zero
×
809

810
            for (size_t fi = 0; fi < num_face_nodes; ++fi)
114,720✔
811
            {
812
              const int i = cell_mapping.MapFaceNode(f, fi);
84,880✔
813

814
              if (std::fabs(aval) >= 1.0e-12)
84,880✔
815
              {
816
                for (size_t fj = 0; fj < num_face_nodes; ++fj)
×
817
                {
818
                  const int j = cell_mapping.MapFaceNode(f, fj);
×
819

820
                  const double aij = (aval / bval) * intS_shapeI_shapeJ(i, j);
×
821

822
                  cell_A(i, j) += aij;
×
823
                } // for fj
824
              } // if a nonzero
825

826
              if (std::fabs(fval) >= 1.0e-12)
84,880✔
827
              {
828
                const double rhs_val = (fval / bval) * intS_shapeI(i);
×
829

830
                cell_rhs(i) += rhs_val;
×
831
              } // if f nonzero
832
            } // for fi
833
          } // Robin BC
834
        } // boundary face
835
      } // for face
836

837
      auto nn = static_cast<PetscInt>(num_nodes);
721,557✔
838
      OpenSnPETScCall(
721,557✔
839
        MatSetValues(A_, nn, cell_idxs.data(), nn, cell_idxs.data(), cell_A.data(), ADD_VALUES));
840
      OpenSnPETScCall(VecSetValues(rhs_, nn, cell_idxs.data(), cell_rhs.data(), ADD_VALUES));
721,557✔
841
    } // for g
842
  } // for cell
141,632✔
843

844
  OpenSnPETScCall(MatAssemblyBegin(A_, MAT_FINAL_ASSEMBLY));
867✔
845
  OpenSnPETScCall(MatAssemblyEnd(A_, MAT_FINAL_ASSEMBLY));
867✔
846
  OpenSnPETScCall(VecAssemblyBegin(rhs_));
867✔
847
  OpenSnPETScCall(VecAssemblyEnd(rhs_));
867✔
848

849
  if (options.verbose)
867✔
850
  {
851
    MatInfo info;
4✔
852
    OpenSnPETScCall(MatGetInfo(A_, MAT_GLOBAL_SUM, &info));
4✔
853

854
    log.Log() << "Number of mallocs used = " << info.mallocs
12✔
855
              << "\nNumber of non-zeros allocated = " << info.nz_allocated
4✔
856
              << "\nNumber of non-zeros used = " << info.nz_used
4✔
857
              << "\nNumber of unneeded non-zeros = " << info.nz_unneeded;
8✔
858
  }
859

860
  if (options.perform_symmetry_check)
867✔
861
  {
862
    PetscBool symmetry = PETSC_FALSE;
×
863
    OpenSnPETScCall(MatIsSymmetric(A_, 1.0e-6, &symmetry));
×
864
    if (symmetry == PETSC_FALSE)
×
865
      throw std::logic_error(fname + ":Symmetry check failed");
×
866
  }
867

868
  OpenSnPETScCall(KSPSetOperators(ksp_, A_, A_));
867✔
869

870
  if (options.verbose)
867✔
871
    log.Log() << program_timer.GetTimeString() << " Assembly completed";
12✔
872

873
  PC pc = nullptr;
867✔
874
  OpenSnPETScCall(KSPGetPC(ksp_, &pc));
867✔
875
  OpenSnPETScCall(PCSetUp(pc));
867✔
876

877
  OpenSnPETScCall(KSPSetUp(ksp_));
867✔
878
}
867✔
879

880
void
881
DiffusionMIPSolver::Assemble_b(const std::vector<double>& q_vector)
23,160✔
882
{
883
  const std::string fname = "acceleration::DiffusionMIPSolver::"
23,160✔
884
                            "Assemble_b";
23,160✔
885
  if (A_ == nullptr or rhs_ == nullptr or ksp_ == nullptr)
23,160✔
886
    throw std::logic_error(fname + ": Some or all PETSc elements are null. "
×
887
                                   "Check that Initialize has been called.");
×
888
  if (options.verbose)
23,160✔
889
    log.Log() << program_timer.GetTimeString() << " Starting assembly";
384✔
890

891
  const unsigned int num_groups = uk_man_.unknowns.front().num_components;
23,160✔
892

893
  OpenSnPETScCall(VecSet(rhs_, 0.0));
23,160✔
894
  for (const auto& cell : grid_->local_cells)
1,983,288✔
895
  {
896
    const size_t num_faces = cell.faces.size();
1,960,128✔
897
    const auto& cell_mapping = sdm_.GetCellMapping(cell);
1,960,128✔
898
    const auto num_nodes = cell_mapping.GetNumNodes();
1,960,128✔
899
    const auto cc_nodes = cell_mapping.GetNodeLocations();
1,960,128✔
900
    const auto& unit_cell_matrices = unit_cell_matrices_[cell.local_id];
1,960,128✔
901

902
    const auto& intV_shapeI_shapeJ = unit_cell_matrices.intV_shapeI_shapeJ;
1,960,128✔
903

904
    const auto& xs = mat_id_2_xs_map_.at(cell.block_id);
1,960,128✔
905

906
    Vector<double> cell_rhs(num_nodes);
1,960,128✔
907
    Vector<PetscInt> cell_idxs(num_nodes);
1,960,128✔
908
    std::vector<double> qg(num_nodes, 0.0);
1,960,128✔
909
    for (unsigned int g = 0; g < num_groups; ++g)
44,873,664✔
910
    {
911
      cell_rhs.Set(0.);
42,913,536✔
912
      for (size_t i = 0; i < num_nodes; ++i)
266,869,120✔
913
        cell_idxs(i) = static_cast<PetscInt>(sdm_.MapDOF(cell, i, uk_man_, 0, g));
223,955,584✔
914

915
      // Get coefficient and nodal src
916
      const double Dg = xs.Dg[g];
42,913,536✔
917

918
      for (size_t j = 0; j < num_nodes; ++j)
266,869,120✔
919
        qg[j] = q_vector[sdm_.MapDOFLocal(cell, j, uk_man_, 0, g)];
223,955,584✔
920

921
      // Assemble continuous terms
922
      for (size_t i = 0; i < num_nodes; ++i)
266,869,120✔
923
      {
924
        double entry_rhs_i = 0.0;
925
        for (size_t j = 0; j < num_nodes; ++j)
1,614,563,200✔
926
          entry_rhs_i += intV_shapeI_shapeJ(i, j) * qg[j];
1,390,607,616✔
927

928
        cell_rhs(i) += entry_rhs_i;
223,955,584✔
929
      } // for i
930

931
      // Assemble face terms
932
      for (size_t f = 0; f < num_faces; ++f)
234,353,920✔
933
      {
934
        const auto& face = cell.faces[f];
191,440,384✔
935
        const auto& n_f = face.normal;
191,440,384✔
936
        const size_t num_face_nodes = cell_mapping.GetNumFaceNodes(f);
191,440,384✔
937

938
        const auto& intS_shapeI_shapeJ = unit_cell_matrices.intS_shapeI_shapeJ[f];
191,440,384✔
939
        const auto& intS_shapeI_gradshapeJ = unit_cell_matrices.intS_shapeI_gradshapeJ[f];
191,440,384✔
940
        const auto& intS_shapeI = unit_cell_matrices.intS_shapeI[f];
191,440,384✔
941

942
        const double hm = HPerpendicular(cell, f);
191,440,384✔
943

944
        if (not face.has_neighbor and not suppress_bcs_)
191,440,384✔
945
        {
946
          BoundaryCondition bc;
17,262,988✔
947
          if (bcs_.count(face.neighbor_id) > 0)
17,262,988✔
948
            bc = bcs_.at(face.neighbor_id);
17,262,988✔
949

950
          if (bc.type == BCType::DIRICHLET)
17,262,988✔
951
          {
952
            const double bc_value = bc.values[0];
15,891,932✔
953

954
            // Compute kappa
955
            double kappa = 1.0;
15,891,932✔
956
            if (cell.GetType() == CellType::SLAB)
15,891,932✔
957
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
57,644✔
958
            if (cell.GetType() == CellType::POLYGON)
15,891,932✔
959
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
4,689,168✔
960
            if (cell.GetType() == CellType::POLYHEDRON)
15,891,932✔
961
              kappa = fmax(options.penalty_factor * 2.0 * Dg / hm, 0.25);
11,145,120✔
962

963
            // Assembly penalty terms
964
            for (size_t fi = 0; fi < num_face_nodes; ++fi)
69,908,392✔
965
            {
966
              const int i = cell_mapping.MapFaceNode(f, fi);
54,016,460✔
967

968
              for (size_t fj = 0; fj < num_face_nodes; ++fj)
251,152,696✔
969
              {
970
                const int jm = cell_mapping.MapFaceNode(f, fj);
197,136,236✔
971

972
                const double aij = kappa * intS_shapeI_shapeJ(i, jm);
197,136,236✔
973
                const double aij_bc_value = aij * bc_value;
197,136,236✔
974

975
                cell_rhs(i) += aij_bc_value;
197,136,236✔
976
              } // for fj
977
            } // for fi
978

979
            // Assemble gradient terms
980
            // For the following comments we use the notation:
981
            // Dk = n dot nabla bk
982

983
            // D* n dot (b_j^+ - b_j^-)*nabla b_i^-
984
            for (size_t i = 0; i < num_nodes; ++i)
123,924,852✔
985
            {
986
              for (size_t j = 0; j < num_nodes; ++j)
896,577,864✔
987
              {
988
                const double aij =
788,544,944✔
989
                  -Dg * n_f.Dot(intS_shapeI_gradshapeJ(j, i) + intS_shapeI_gradshapeJ(i, j));
788,544,944✔
990
                const double aij_bc_value = aij * bc_value;
788,544,944✔
991

992
                cell_rhs(i) += aij_bc_value;
788,544,944✔
993
              } // for fj
994
            } // for i
995
          } // Dirichlet BC
996
          else if (bc.type == BCType::ROBIN)
1,371,056✔
997
          {
998
            const double bval = bc.values[1];
1,371,056✔
999
            const double fval = bc.values[2];
1,371,056✔
1000

1001
            if (std::fabs(bval) < 1.0e-12)
1,371,056✔
1002
              continue; // a and f assumed zero
×
1003

1004
            for (size_t fi = 0; fi < num_face_nodes; ++fi)
4,919,568✔
1005
            {
1006
              const int i = cell_mapping.MapFaceNode(f, fi);
3,548,512✔
1007

1008
              if (std::fabs(fval) >= 1.0e-12)
3,548,512✔
1009
              {
1010
                const double rhs_val = (fval / bval) * intS_shapeI(i);
×
1011

1012
                cell_rhs(i) += rhs_val;
×
1013
              } // if f nonzero
1014
            } // for fi
1015
          } // Robin BC
1016
        } // boundary face
1017
      } // for face
1018

1019
      auto nn = static_cast<PetscInt>(num_nodes);
42,913,536✔
1020
      OpenSnPETScCall(VecSetValues(rhs_, nn, cell_idxs.data(), cell_rhs.data(), ADD_VALUES));
42,913,536✔
1021
    } // for g
1022
  } // for cell
5,880,384✔
1023

1024
  OpenSnPETScCall(VecAssemblyBegin(rhs_));
23,160✔
1025
  OpenSnPETScCall(VecAssemblyEnd(rhs_));
23,160✔
1026

1027
  if (options.verbose)
23,160✔
1028
    log.Log() << program_timer.GetTimeString() << " Assembly completed";
384✔
1029
}
23,160✔
1030

1031
void
1032
DiffusionMIPSolver::Assemble_b(Vec petsc_q_vector)
×
1033
{
1034
  const std::string fname = "acceleration::DiffusionMIPSolver::"
×
1035
                            "Assemble_b";
×
1036
  if (A_ == nullptr or rhs_ == nullptr or ksp_ == nullptr)
×
1037
    throw std::logic_error(fname + ": Some or all PETSc elements are null. "
×
1038
                                   "Check that Initialize has been called.");
×
1039
  if (options.verbose)
×
1040
    log.Log() << program_timer.GetTimeString() << " Starting assembly";
×
1041

1042
  const unsigned int num_groups = uk_man_.unknowns.front().num_components;
×
1043

1044
  const PetscScalar* q_vector = nullptr;
×
1045
  OpenSnPETScCall(VecGetArrayRead(petsc_q_vector, &q_vector));
×
1046

1047
  OpenSnPETScCall(VecSet(rhs_, 0.0));
×
1048
  for (const auto& cell : grid_->local_cells)
×
1049
  {
1050
    const size_t num_faces = cell.faces.size();
×
1051
    const auto& cell_mapping = sdm_.GetCellMapping(cell);
×
1052
    const auto num_nodes = cell_mapping.GetNumNodes();
×
1053
    const auto cc_nodes = cell_mapping.GetNodeLocations();
×
1054
    const auto& unit_cell_matrices = unit_cell_matrices_[cell.local_id];
×
1055

1056
    const auto& intV_shapeI_shapeJ = unit_cell_matrices.intV_shapeI_shapeJ;
×
1057

1058
    const auto& xs = mat_id_2_xs_map_.at(cell.block_id);
×
1059

1060
    Vector<double> cell_rhs(num_nodes);
×
1061
    Vector<PetscInt> cell_idxs(num_nodes);
×
1062
    std::vector<double> qg(num_nodes, 0.0);
×
1063
    for (unsigned int g = 0; g < num_groups; ++g)
×
1064
    {
1065
      cell_rhs.Set(0.);
×
1066
      for (size_t i = 0; i < num_nodes; ++i)
×
1067
        cell_idxs(i) = static_cast<PetscInt>(sdm_.MapDOF(cell, i, uk_man_, 0, g));
×
1068

1069
      // Get coefficient and nodal src
1070
      const double Dg = xs.Dg[g];
×
1071

1072
      for (size_t j = 0; j < num_nodes; ++j)
×
1073
        qg[j] = q_vector[sdm_.MapDOFLocal(cell, j, uk_man_, 0, g)];
×
1074

1075
      // Assemble continuous terms
1076
      for (size_t i = 0; i < num_nodes; ++i)
×
1077
      {
1078
        double entry_rhs_i = 0.0;
1079
        for (size_t j = 0; j < num_nodes; ++j)
×
1080
          entry_rhs_i += intV_shapeI_shapeJ(i, j) * qg[j];
×
1081

1082
        cell_rhs(i) += entry_rhs_i;
×
1083
      } // for i
1084

1085
      // Assemble face terms
1086
      for (size_t f = 0; f < num_faces; ++f)
×
1087
      {
1088
        const auto& face = cell.faces[f];
×
1089
        const auto& n_f = face.normal;
×
1090
        const size_t num_face_nodes = cell_mapping.GetNumFaceNodes(f);
×
1091

1092
        const auto& intS_shapeI_shapeJ = unit_cell_matrices.intS_shapeI_shapeJ[f];
×
1093
        const auto& intS_shapeI_gradshapeJ = unit_cell_matrices.intS_shapeI_gradshapeJ[f];
×
1094
        const auto& intS_shapeI = unit_cell_matrices.intS_shapeI[f];
×
1095

1096
        const double hm = HPerpendicular(cell, f);
×
1097

1098
        if (not face.has_neighbor and not suppress_bcs_)
×
1099
        {
1100
          BoundaryCondition bc;
×
1101
          if (bcs_.count(face.neighbor_id) > 0)
×
1102
            bc = bcs_.at(face.neighbor_id);
×
1103

1104
          if (bc.type == BCType::DIRICHLET)
×
1105
          {
1106
            const double bc_value = bc.values[0];
×
1107

1108
            // Compute kappa
1109
            double kappa = 1.0;
×
1110
            if (cell.GetType() == CellType::SLAB)
×
1111
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
×
1112
            if (cell.GetType() == CellType::POLYGON)
×
1113
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
×
1114
            if (cell.GetType() == CellType::POLYHEDRON)
×
1115
              kappa = fmax(options.penalty_factor * 2.0 * Dg / hm, 0.25);
×
1116

1117
            // Assembly penalty terms
1118
            for (size_t fi = 0; fi < num_face_nodes; ++fi)
×
1119
            {
1120
              const int i = cell_mapping.MapFaceNode(f, fi);
×
1121

1122
              for (size_t fj = 0; fj < num_face_nodes; ++fj)
×
1123
              {
1124
                const int jm = cell_mapping.MapFaceNode(f, fj);
×
1125

1126
                const double aij = kappa * intS_shapeI_shapeJ(i, jm);
×
1127
                const double aij_bc_value = aij * bc_value;
×
1128

1129
                cell_rhs(i) += aij_bc_value;
×
1130
              } // for fj
1131
            } // for fi
1132

1133
            // Assemble gradient terms
1134
            // For the following comments we use the notation:
1135
            // Dk = n dot nabla bk
1136

1137
            // D* n dot (b_j^+ - b_j^-)*nabla b_i^-
1138
            for (size_t i = 0; i < num_nodes; ++i)
×
1139
            {
1140
              for (size_t j = 0; j < num_nodes; ++j)
×
1141
              {
1142
                const double aij =
×
1143
                  -Dg * n_f.Dot(intS_shapeI_gradshapeJ(j, i) + intS_shapeI_gradshapeJ(i, j));
×
1144
                const double aij_bc_value = aij * bc_value;
×
1145

1146
                cell_rhs(i) += aij_bc_value;
×
1147
              } // for fj
1148
            } // for i
1149
          } // Dirichlet BC
1150
          else if (bc.type == BCType::ROBIN)
×
1151
          {
1152
            const double bval = bc.values[1];
×
1153
            const double fval = bc.values[2];
×
1154

1155
            if (std::fabs(bval) < 1.0e-12)
×
1156
              continue; // a and f assumed zero
×
1157

1158
            for (size_t fi = 0; fi < num_face_nodes; ++fi)
×
1159
            {
1160
              const int i = cell_mapping.MapFaceNode(f, fi);
×
1161

1162
              if (std::fabs(fval) >= 1.0e-12)
×
1163
              {
1164
                const double rhs_val = (fval / bval) * intS_shapeI(i);
×
1165

1166
                cell_rhs(i) += rhs_val;
×
1167
              } // if f nonzero
1168
            } // for fi
1169
          } // Robin BC
1170
        } // boundary face
1171
      } // for face
1172
      auto nn = static_cast<PetscInt>(num_nodes);
×
1173
      OpenSnPETScCall(VecSetValues(rhs_, nn, cell_idxs.data(), cell_rhs.data(), ADD_VALUES));
×
1174
    } // for g
1175
  } // for cell
×
1176

1177
  OpenSnPETScCall(VecRestoreArrayRead(petsc_q_vector, &q_vector));
×
1178

1179
  OpenSnPETScCall(VecAssemblyBegin(rhs_));
×
1180
  OpenSnPETScCall(VecAssemblyEnd(rhs_));
×
1181

1182
  if (options.verbose)
×
1183
    log.Log() << program_timer.GetTimeString() << " Assembly completed";
×
1184
}
×
1185

1186
double
1187
DiffusionMIPSolver::HPerpendicular(const Cell& cell, unsigned int f)
197,493,640✔
1188
{
1189
  const auto& cell_mapping = sdm_.GetCellMapping(cell);
197,493,640✔
1190
  double hp = 0.0;
197,493,640✔
1191

1192
  const auto num_faces = cell.faces.size();
197,493,640✔
1193
  const auto num_vertices = cell.vertex_ids.size();
197,493,640✔
1194

1195
  const auto volume = cell.volume;
197,493,640✔
1196
  const auto face_area = cell.faces.at(f).area;
197,493,640✔
1197

1198
  /**Lambda to compute surface area.*/
1199
  auto ComputeSurfaceArea = [&cell, &num_faces]()
298,208,160✔
1200
  {
1201
    double surface_area = 0.0;
100,714,520✔
1202
    for (size_t fr = 0; fr < num_faces; ++fr)
705,001,640✔
1203
      surface_area += cell.faces[fr].area;
604,287,120✔
1204

1205
    return surface_area;
100,714,520✔
1206
  };
197,493,640✔
1207

1208
  if (cell.GetType() == CellType::SLAB)
197,493,640✔
1209
  {
1210
    hp = volume / 2.0;
13,404,960✔
1211
  }
1212
  else if (cell.GetType() == CellType::POLYGON)
184,088,680✔
1213
  {
1214
    if (num_faces == 3)
83,374,160✔
1215
      hp = 2.0 * volume / face_area;
×
1216
    else if (num_faces == 4)
83,374,160✔
1217
      hp = volume / face_area;
83,374,160✔
1218
    else // Nv > 4
1219
    {
1220
      const auto surface_area = ComputeSurfaceArea();
×
1221

1222
      if (num_faces % 2 == 0)
×
1223
        hp = 4.0 * volume / surface_area;
×
1224
      else
1225
      {
1226
        hp = 2.0 * volume / surface_area;
×
1227
        hp +=
×
1228
          sqrt(2.0 * volume /
×
1229
               (static_cast<double>(num_faces) * sin(2.0 * M_PI / static_cast<double>(num_faces))));
×
1230
      }
1231
    }
1232
  }
1233
  else if (cell.GetType() == CellType::POLYHEDRON)
100,714,520✔
1234
  {
1235
    const auto surface_area = ComputeSurfaceArea();
100,714,520✔
1236

1237
    if (num_faces == 4) // Tet
100,714,520✔
1238
      hp = 3 * volume / surface_area;
×
1239
    else if (num_faces == 6 and num_vertices == 8) // Hex
100,714,520✔
1240
      hp = volume / surface_area;
100,714,520✔
1241
    else // Polyhedron
1242
      hp = 6 * volume / surface_area;
×
1243
  } // Polyhedron
1244
  else
1245
    throw std::logic_error("acceleration::DiffusionMIPSolver::HPerpendicular: "
×
1246
                           "Unsupported cell type in call to HPerpendicular");
×
1247

1248
  return hp;
197,493,640✔
1249
}
1250

1251
int
1252
DiffusionMIPSolver::MapFaceNodeDisc(const Cell& cur_cell,
8,378,510✔
1253
                                    const Cell& adj_cell,
1254
                                    const std::vector<Vector3>& cc_node_locs,
1255
                                    const std::vector<Vector3>& ac_node_locs,
1256
                                    size_t ccf,
1257
                                    size_t acf,
1258
                                    size_t ccfi,
1259
                                    double epsilon)
1260
{
1261
  const auto& cur_cell_mapping = sdm_.GetCellMapping(cur_cell);
8,378,510✔
1262
  const auto& adj_cell_mapping = sdm_.GetCellMapping(adj_cell);
8,378,510✔
1263

1264
  const int i = cur_cell_mapping.MapFaceNode(ccf, ccfi);
8,378,510✔
1265
  const auto& node_i_loc = cc_node_locs[i];
8,378,510✔
1266

1267
  const size_t adj_face_num_nodes = adj_cell_mapping.GetNumFaceNodes(acf);
8,378,510✔
1268

1269
  for (size_t fj = 0; fj < adj_face_num_nodes; ++fj)
18,333,014✔
1270
  {
1271
    const int j = adj_cell_mapping.MapFaceNode(acf, fj);
18,333,014✔
1272
    if ((node_i_loc - ac_node_locs[j]).NormSquare() < epsilon)
18,333,014✔
1273
      return j;
8,378,510✔
1274
  }
1275

1276
  throw std::logic_error("acceleration::DiffusionMIPSolver::MapFaceNodeDisc: Mapping failure.");
×
1277
}
1278

1279
} // namespace opensn
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc