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

Open-Sn / opensn / 22249939997

20 Feb 2026 06:11PM UTC coverage: 74.156% (-0.2%) from 74.335%
22249939997

push

github

web-flow
Merge pull request #937 from andrsd/issue/90-num-precursors

Number of precursors is `unsigned int`

4 of 7 new or added lines in 5 files covered. (57.14%)

527 existing lines in 19 files now uncovered.

19997 of 26966 relevant lines covered (74.16%)

67202602.73 hits per line

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

75.91
/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/lbs_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,
68✔
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)
68✔
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)
136✔
38
{
39
  if (sdm_.GetType() != SpatialDiscretizationType::PIECEWISE_LINEAR_DISCONTINUOUS)
68✔
UNCOV
40
    throw std::logic_error("acceleration::DiffusionMIPSolver can only be used with PWLD.");
×
41
}
68✔
42

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

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

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

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

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

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

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

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

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

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

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

109
        if (source_function_)
400✔
110
        {
111
          for (size_t qp : fe_vol_data.GetQuadraturePointIndices())
5,200✔
112
            entry_rhs_i += source_function_(fe_vol_data.QPointXYZ(qp)) *
4,800✔
113
                           fe_vol_data.ShapeValue(i, qp) * fe_vol_data.JxW(qp);
4,800✔
114
        }
115

116
        cell_rhs(i) += entry_rhs_i;
400✔
117
      } // for i
118

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

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

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

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

140
          // Compute kappa
141
          double kappa = 1.0;
360✔
142
          if (cell.GetType() == CellType::SLAB)
360✔
UNCOV
143
            kappa = fmax(options.penalty_factor * (adj_Dg / hp + Dg / hm) * 0.5, 0.25);
×
144
          if (cell.GetType() == CellType::POLYGON)
360✔
145
            kappa = fmax(options.penalty_factor * (adj_Dg / hp + Dg / hm) * 0.5, 0.25);
360✔
146
          if (cell.GetType() == CellType::POLYHEDRON)
360✔
UNCOV
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.);
360✔
150
          DenseMatrix<double> adj_AT(num_face_nodes, num_nodes, 0.);
360✔
151
          Vector<PetscInt> adj_idxs(num_face_nodes);
360✔
152
          for (size_t fj = 0; fj < num_face_nodes; ++fj)
1,080✔
153
          {
154
            const auto jp =
720✔
155
              MapFaceNodeDisc(cell, adj_cell, cc_nodes, ac_nodes, f, acf, fj); // j-plus
720✔
156
            adj_idxs(fj) = static_cast<PetscInt>(sdm_.MapDOF(adj_cell, jp, uk_man_, 0, g));
720✔
157
          }
158

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

164
            for (size_t fj = 0; fj < num_face_nodes; ++fj)
2,160✔
165
            {
166
              const int jm = cell_mapping.MapFaceNode(f, fj); // j-minus
1,440✔
167

168
              double aij = 0.0;
1,440✔
169
              for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
4,320✔
170
                aij += kappa * fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeValue(jm, qp) *
5,760✔
171
                       fe_srf_data.JxW(qp);
2,880✔
172

173
              cell_A(i, jm) += aij;
1,440✔
174
              adj_A(i, fj) -= aij;
1,440✔
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)
1,800✔
184
          {
185
            for (size_t fj = 0; fj < num_face_nodes; ++fj)
4,320✔
186
            {
187
              const int jm = cell_mapping.MapFaceNode(f, fj); // j-minus
2,880✔
188

189
              Vector3 vec_aij;
2,880✔
190
              for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
8,640✔
191
                vec_aij += fe_srf_data.ShapeValue(jm, qp) * fe_srf_data.ShapeGrad(i, qp) *
5,760✔
192
                           fe_srf_data.JxW(qp);
11,520✔
193
              const double aij = -0.5 * Dg * n_f.Dot(vec_aij);
2,880✔
194

195
              cell_A(i, jm) += aij;
2,880✔
196
              adj_A(i, fj) -= aij;
2,880✔
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)
1,080✔
202
          {
203
            const int im = cell_mapping.MapFaceNode(f, fi); // i-minus
720✔
204

205
            for (size_t j = 0; j < num_nodes; ++j)
3,600✔
206
            {
207
              Vector3 vec_aij;
2,880✔
208
              for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
8,640✔
209
                vec_aij += fe_srf_data.ShapeValue(im, qp) * fe_srf_data.ShapeGrad(j, qp) *
5,760✔
210
                           fe_srf_data.JxW(qp);
11,520✔
211
              const double aij = -0.5 * Dg * n_f.Dot(vec_aij);
2,880✔
212

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

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

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

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

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

237
            // Compute kappa
238
            double kappa = 1.0;
40✔
239
            if (cell.GetType() == CellType::SLAB)
40✔
UNCOV
240
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
×
241
            if (cell.GetType() == CellType::POLYGON)
40✔
242
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
40✔
243
            if (cell.GetType() == CellType::POLYHEDRON)
40✔
UNCOV
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)
120✔
248
            {
249
              const int i = cell_mapping.MapFaceNode(f, fi);
80✔
250

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

255
                double aij = 0.0;
160✔
256
                for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
480✔
257
                  aij += kappa * fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeValue(jm, qp) *
640✔
258
                         fe_srf_data.JxW(qp);
320✔
259
                double aij_bc_value = aij * bc_value;
160✔
260

261
                if (ref_solution_function_)
160✔
262
                {
263
                  aij_bc_value = 0.0;
160✔
264
                  for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
480✔
265
                    aij_bc_value += kappa * ref_solution_function_(fe_srf_data.QPointXYZ(qp)) *
320✔
266
                                    fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeValue(jm, qp) *
640✔
267
                                    fe_srf_data.JxW(qp);
320✔
268
                }
269

270
                cell_A(i, jm) += aij;
160✔
271
                cell_rhs(i) += aij_bc_value;
160✔
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)
200✔
281
            {
282
              for (size_t j = 0; j < num_nodes; ++j)
800✔
283
              {
284
                Vector3 vec_aij;
640✔
285
                for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
1,920✔
286
                  vec_aij += fe_srf_data.ShapeValue(j, qp) * fe_srf_data.ShapeGrad(i, qp) *
1,280✔
287
                               fe_srf_data.JxW(qp) +
2,560✔
288
                             fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeGrad(j, qp) *
2,560✔
289
                               fe_srf_data.JxW(qp);
3,840✔
290
                const double aij = -Dg * n_f.Dot(vec_aij);
640✔
291

292
                double aij_bc_value = aij * bc_value;
640✔
293

294
                if (ref_solution_function_)
640✔
295
                {
296
                  Vector3 vec_aij_mms;
640✔
297
                  for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
1,920✔
298
                    vec_aij_mms += ref_solution_function_(fe_srf_data.QPointXYZ(qp)) *
3,840✔
299
                                   (fe_srf_data.ShapeValue(j, qp) * fe_srf_data.ShapeGrad(i, qp) *
1,280✔
300
                                      fe_srf_data.JxW(qp) +
3,840✔
301
                                    fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeGrad(j, qp) *
1,280✔
302
                                      fe_srf_data.JxW(qp));
3,840✔
303
                  aij_bc_value = -Dg * n_f.Dot(vec_aij_mms);
640✔
304
                }
305

306
                cell_A(i, j) += aij;
640✔
307
                cell_rhs(i) += aij_bc_value;
640✔
308
              } // for fj
309
            } // for i
310
          } // Dirichlet BC
311
          else if (bc.type == BCType::ROBIN)
×
312
          {
UNCOV
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)
×
UNCOV
318
              continue; // a and f assumed zero
×
319

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

UNCOV
324
              if (std::fabs(aval) >= 1.0e-12)
×
325
              {
UNCOV
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())
×
UNCOV
332
                    aij += fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeValue(j, qp) *
×
333
                           fe_srf_data.JxW(qp);
×
UNCOV
334
                  aij *= (aval / bval);
×
335

UNCOV
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;
×
UNCOV
343
                for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
×
344
                  rhs_val += fe_srf_data.ShapeValue(i, qp) * fe_srf_data.JxW(qp);
×
UNCOV
345
                rhs_val *= (fval / bval);
×
346

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

354
      auto nn = static_cast<PetscInt>(num_nodes);
100✔
355
      OpenSnPETScCall(
100✔
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));
100✔
358
    } // for g
100✔
359
  } // for cell
300✔
360

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

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

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

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

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

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

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

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

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

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

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

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

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

425
      // Assemble continuous terms
426
      for (size_t i = 0; i < num_nodes; ++i)
500✔
427
      {
428
        double entry_rhs_i = 0.0; // entry may accumulate over j
400✔
429
        if (not source_function_)
400✔
430
          for (size_t j = 0; j < num_nodes; ++j)
×
431
          {
UNCOV
432
            for (size_t qp : fe_vol_data.GetQuadraturePointIndices())
×
433
            {
UNCOV
434
              entry_rhs_i += fe_vol_data.ShapeValue(i, qp) * fe_vol_data.ShapeValue(j, qp) *
×
UNCOV
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())
5,200✔
441
            entry_rhs_i += source_function_(fe_vol_data.QPointXYZ(qp)) *
4,800✔
442
                           fe_vol_data.ShapeValue(i, qp) * fe_vol_data.JxW(qp);
4,800✔
443
        }
444

445
        cell_rhs(i) += entry_rhs_i;
400✔
446
      } // for i
447

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

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

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

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

468
            // Compute kappa
469
            double kappa = 1.0;
40✔
470
            if (cell.GetType() == CellType::SLAB)
40✔
471
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
×
472
            if (cell.GetType() == CellType::POLYGON)
40✔
473
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
40✔
474
            if (cell.GetType() == CellType::POLYHEDRON)
40✔
UNCOV
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)
120✔
479
            {
480
              const int i = cell_mapping.MapFaceNode(f, fi);
80✔
481

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

486
                double aij = 0.0;
160✔
487
                for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
480✔
488
                  aij += kappa * fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeValue(jm, qp) *
640✔
489
                         fe_srf_data.JxW(qp);
320✔
490
                double aij_bc_value = aij * bc_value;
160✔
491

492
                if (ref_solution_function_)
160✔
493
                {
494
                  aij_bc_value = 0.0;
160✔
495
                  for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
480✔
496
                    aij_bc_value += kappa * source_function_(fe_srf_data.QPointXYZ(qp)) *
320✔
497
                                    fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeValue(jm, qp) *
640✔
498
                                    fe_srf_data.JxW(qp);
320✔
499
                }
500

501
                cell_rhs(i) += aij_bc_value;
160✔
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)
200✔
511
            {
512
              for (size_t j = 0; j < num_nodes; ++j)
800✔
513
              {
514
                Vector3 vec_aij;
640✔
515
                for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
1,920✔
516
                  vec_aij += fe_srf_data.ShapeValue(j, qp) * fe_srf_data.ShapeGrad(i, qp) *
1,280✔
517
                               fe_srf_data.JxW(qp) +
2,560✔
518
                             fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeGrad(j, qp) *
2,560✔
519
                               fe_srf_data.JxW(qp);
3,840✔
520
                const double aij = -Dg * n_f.Dot(vec_aij);
640✔
521

522
                double aij_bc_value = aij * bc_value;
640✔
523

524
                if (ref_solution_function_)
640✔
525
                {
526
                  Vector3 vec_aij_mms;
640✔
527
                  for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
1,920✔
528
                    vec_aij_mms += ref_solution_function_(fe_srf_data.QPointXYZ(qp)) *
3,840✔
529
                                   (fe_srf_data.ShapeValue(j, qp) * fe_srf_data.ShapeGrad(i, qp) *
1,280✔
530
                                      fe_srf_data.JxW(qp) +
3,840✔
531
                                    fe_srf_data.ShapeValue(i, qp) * fe_srf_data.ShapeGrad(j, qp) *
1,280✔
532
                                      fe_srf_data.JxW(qp));
3,840✔
533
                  aij_bc_value = -Dg * n_f.Dot(vec_aij_mms);
640✔
534
                }
535

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

UNCOV
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
              {
UNCOV
554
                double rhs_val = 0.0;
×
555
                for (size_t qp : fe_srf_data.GetQuadraturePointIndices())
×
UNCOV
556
                  rhs_val += fe_srf_data.ShapeValue(i, qp) * fe_srf_data.JxW(qp);
×
UNCOV
557
                rhs_val *= (fval / bval);
×
558

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

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

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

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

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

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

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

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

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

607
    const auto& intV_gradshapeI_gradshapeJ = unit_cell_matrices.intV_gradshapeI_gradshapeJ;
18,528✔
608
    const auto& intV_shapeI_shapeJ = unit_cell_matrices.intV_shapeI_shapeJ;
18,528✔
609

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

612
    DenseMatrix<double> cell_A(num_nodes, num_nodes);
18,528✔
613
    Vector<double> cell_rhs(num_nodes);
18,528✔
614
    Vector<PetscInt> cell_idxs(num_nodes);
18,528✔
615
    for (unsigned int g = 0; g < num_groups; ++g)
620,197✔
616
    {
617
      cell_A.Set(0.);
601,669✔
618
      cell_rhs.Set(0.);
601,669✔
619
      for (size_t i = 0; i < num_nodes; ++i)
3,793,607✔
620
        cell_idxs(i) = static_cast<PetscInt>(sdm_.MapDOF(cell, i, uk_man_, 0, g));
3,191,938✔
621

622
      // Get coefficient and nodal src
623
      const double Dg = xs.Dg[g];
601,669✔
624
      const double sigr_g = xs.sigR[g];
601,669✔
625

626
      std::vector<double> qg(num_nodes, 0.0);
601,669✔
627
      for (size_t j = 0; j < num_nodes; ++j)
3,793,607✔
628
        qg[j] = q_vector[sdm_.MapDOFLocal(cell, j, uk_man_, 0, g)];
3,191,938✔
629

630
      // Assemble continuous terms
631
      for (size_t i = 0; i < num_nodes; ++i)
3,793,607✔
632
      {
633
        double entry_rhs_i = 0.0;
634
        for (size_t j = 0; j < num_nodes; ++j)
24,271,814✔
635
        {
636
          const double entry_aij =
21,079,876✔
637
            Dg * intV_gradshapeI_gradshapeJ(i, j) + sigr_g * intV_shapeI_shapeJ(i, j);
21,079,876✔
638

639
          entry_rhs_i += intV_shapeI_shapeJ(i, j) * qg[j];
21,079,876✔
640

641
          cell_A(i, j) += entry_aij;
21,079,876✔
642
        } // for j
643

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

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

654
        const auto& intS_shapeI_shapeJ = unit_cell_matrices.intS_shapeI_shapeJ[f];
2,630,138✔
655
        const auto& intS_shapeI_gradshapeJ = unit_cell_matrices.intS_shapeI_gradshapeJ[f];
2,630,138✔
656
        const auto& intS_shapeI = unit_cell_matrices.intS_shapeI[f];
2,630,138✔
657

658
        const double hm = HPerpendicular(cell, f);
2,630,138✔
659

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

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

671
          // Compute kappa
672
          double kappa = 1.0;
2,399,302✔
673
          if (cell.GetType() == CellType::SLAB)
2,399,302✔
674
            kappa = fmax(options.penalty_factor * (adj_Dg / hp + Dg / hm) * 0.5, 0.25);
337,662✔
675
          if (cell.GetType() == CellType::POLYGON)
2,399,302✔
676
            kappa = fmax(options.penalty_factor * (adj_Dg / hp + Dg / hm) * 0.5, 0.25);
578,120✔
677
          if (cell.GetType() == CellType::POLYHEDRON)
2,399,302✔
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,399,302✔
681
          DenseMatrix<double> adj_AT(num_face_nodes, num_nodes, 0.);
2,399,302✔
682
          Vector<PetscInt> adj_idxs(num_face_nodes);
2,399,302✔
683
          for (size_t fj = 0; fj < num_face_nodes; ++fj)
9,827,284✔
684
          {
685
            const auto jp =
7,427,982✔
686
              MapFaceNodeDisc(cell, adj_cell, cc_nodes, ac_nodes, f, acf, fj); // j-plus
7,427,982✔
687
            adj_idxs(fj) = static_cast<PetscInt>(sdm_.MapDOF(adj_cell, jp, uk_man_, 0, g));
7,427,982✔
688
          }
689

690
          // Assembly penalty terms
691
          for (size_t fi = 0; fi < num_face_nodes; ++fi)
9,827,284✔
692
          {
693
            const int i = cell_mapping.MapFaceNode(f, fi);
7,427,982✔
694

695
            for (size_t fj = 0; fj < num_face_nodes; ++fj)
33,814,444✔
696
            {
697
              const int jm = cell_mapping.MapFaceNode(f, fj); // j-minus
26,386,462✔
698

699
              const double aij = kappa * intS_shapeI_shapeJ(i, jm);
26,386,462✔
700

701
              cell_A(i, jm) += aij;
26,386,462✔
702
              adj_A(i, fj) -= aij;
26,386,462✔
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)
17,255,266✔
712
          {
713
            for (size_t fj = 0; fj < num_face_nodes; ++fj)
67,628,888✔
714
            {
715
              const int jm = cell_mapping.MapFaceNode(f, fj); // j-minus
52,772,924✔
716

717
              const double aij = -0.5 * Dg * n_f.Dot(intS_shapeI_gradshapeJ(jm, i));
52,772,924✔
718

719
              cell_A(i, jm) += aij;
52,772,924✔
720
              adj_A(i, fj) -= aij;
52,772,924✔
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)
9,827,284✔
726
          {
727
            const int im = cell_mapping.MapFaceNode(f, fi); // i-minus
7,427,982✔
728

729
            for (size_t j = 0; j < num_nodes; ++j)
60,200,906✔
730
            {
731
              const double aij = -0.5 * Dg * n_f.Dot(intS_shapeI_gradshapeJ(im, j));
52,772,924✔
732

733
              cell_A(im, j) += aij;
52,772,924✔
734
              adj_AT(fi, j) -= aij;
52,772,924✔
735
            } // for j
736
          } // for fi
737

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

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

746
        } // internal face
7,197,906✔
747
        else
748
        {
749
          BoundaryCondition bc;
230,836✔
750
          if (bcs_.count(face.neighbor_id) > 0)
230,836✔
751
            bc = bcs_.at(face.neighbor_id);
230,836✔
752

753
          if (bc.type == BCType::DIRICHLET)
230,836✔
754
          {
755
            const double bc_value = bc.values[0];
210,956✔
756

757
            // Compute kappa
758
            double kappa = 1.0;
210,956✔
759
            if (cell.GetType() == CellType::SLAB)
210,956✔
760
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
676✔
761
            if (cell.GetType() == CellType::POLYGON)
210,956✔
762
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
21,000✔
763
            if (cell.GetType() == CellType::POLYHEDRON)
210,956✔
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,010,752✔
768
            {
769
              const int i = cell_mapping.MapFaceNode(f, fi);
799,796✔
770

771
              for (size_t fj = 0; fj < num_face_nodes; ++fj)
3,912,952✔
772
              {
773
                const int jm = cell_mapping.MapFaceNode(f, fj);
3,113,156✔
774

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

778
                cell_A(i, jm) += aij;
3,113,156✔
779
                cell_rhs(i) += aij_bc_value;
3,113,156✔
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,810,548✔
789
            {
790
              for (size_t j = 0; j < num_nodes; ++j)
14,052,216✔
791
              {
792
                const double aij =
12,452,624✔
793
                  -Dg * n_f.Dot(intS_shapeI_gradshapeJ(j, i) + intS_shapeI_gradshapeJ(i, j));
12,452,624✔
794
                const double aij_bc_value = aij * bc_value;
12,452,624✔
795

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

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

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

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

UNCOV
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)
64,960✔
827
              {
UNCOV
828
                const double rhs_val = (fval / bval) * intS_shapeI(i);
×
829

UNCOV
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);
601,669✔
838
      OpenSnPETScCall(
601,669✔
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));
601,669✔
841
    } // for g
601,669✔
842
  } // for cell
55,584✔
843

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

849
  if (options.verbose)
67✔
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)
67✔
861
  {
UNCOV
862
    PetscBool symmetry = PETSC_FALSE;
×
UNCOV
863
    OpenSnPETScCall(MatIsSymmetric(A_, 1.0e-6, &symmetry));
×
UNCOV
864
    if (symmetry == PETSC_FALSE)
×
UNCOV
865
      throw std::logic_error(fname + ":Symmetry check failed");
×
866
  }
867

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

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

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

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

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

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

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

902
    const auto& intV_shapeI_shapeJ = unit_cell_matrices.intV_shapeI_shapeJ;
1,599,074✔
903

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

906
    Vector<double> cell_rhs(num_nodes);
1,599,074✔
907
    Vector<PetscInt> cell_idxs(num_nodes);
1,599,074✔
908
    for (unsigned int g = 0; g < num_groups; ++g)
34,610,616✔
909
    {
910
      cell_rhs.Set(0.);
33,011,542✔
911
      for (size_t i = 0; i < num_nodes; ++i)
216,989,026✔
912
        cell_idxs(i) = static_cast<PetscInt>(sdm_.MapDOF(cell, i, uk_man_, 0, g));
183,977,484✔
913

914
      // Get coefficient and nodal src
915
      const double Dg = xs.Dg[g];
33,011,542✔
916

917
      std::vector<double> qg(num_nodes, 0.0);
33,011,542✔
918
      for (size_t j = 0; j < num_nodes; ++j)
216,989,026✔
919
        qg[j] = q_vector[sdm_.MapDOFLocal(cell, j, uk_man_, 0, g)];
183,977,484✔
920

921
      // Assemble continuous terms
922
      for (size_t i = 0; i < num_nodes; ++i)
216,989,026✔
923
      {
924
        double entry_rhs_i = 0.0;
925
        for (size_t j = 0; j < num_nodes; ++j)
1,413,932,452✔
926
          entry_rhs_i += intV_shapeI_shapeJ(i, j) * qg[j];
1,229,954,968✔
927

928
        cell_rhs(i) += entry_rhs_i;
183,977,484✔
929
      } // for i
930

931
      // Assemble face terms
932
      for (size_t f = 0; f < num_faces; ++f)
184,473,826✔
933
      {
934
        const auto& face = cell.faces[f];
151,462,284✔
935
        const auto& n_f = face.normal;
151,462,284✔
936
        const size_t num_face_nodes = cell_mapping.GetNumFaceNodes(f);
151,462,284✔
937

938
        const auto& intS_shapeI_shapeJ = unit_cell_matrices.intS_shapeI_shapeJ[f];
151,462,284✔
939
        const auto& intS_shapeI_gradshapeJ = unit_cell_matrices.intS_shapeI_gradshapeJ[f];
151,462,284✔
940
        const auto& intS_shapeI = unit_cell_matrices.intS_shapeI[f];
151,462,284✔
941

942
        const double hm = HPerpendicular(cell, f);
151,462,284✔
943

944
        if (not face.has_neighbor and not suppress_bcs_)
151,462,284✔
945
        {
946
          BoundaryCondition bc;
13,800,648✔
947
          if (bcs_.count(face.neighbor_id) > 0)
13,800,648✔
948
            bc = bcs_.at(face.neighbor_id);
13,800,648✔
949

950
          if (bc.type == BCType::DIRICHLET)
13,800,648✔
951
          {
952
            const double bc_value = bc.values[0];
12,895,008✔
953

954
            // Compute kappa
955
            double kappa = 1.0;
12,895,008✔
956
            if (cell.GetType() == CellType::SLAB)
12,895,008✔
957
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
427,768✔
958
            if (cell.GetType() == CellType::POLYGON)
12,895,008✔
959
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
1,322,120✔
960
            if (cell.GetType() == CellType::POLYHEDRON)
12,895,008✔
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)
60,547,496✔
965
            {
966
              const int i = cell_mapping.MapFaceNode(f, fi);
47,652,488✔
967

968
              for (size_t fj = 0; fj < num_face_nodes; ++fj)
231,690,656✔
969
              {
970
                const int jm = cell_mapping.MapFaceNode(f, fj);
184,038,168✔
971

972
                const double aij = kappa * intS_shapeI_shapeJ(i, jm);
184,038,168✔
973
                const double aij_bc_value = aij * bc_value;
184,038,168✔
974

975
                cell_rhs(i) += aij_bc_value;
184,038,168✔
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)
108,199,984✔
985
            {
986
              for (size_t j = 0; j < num_nodes; ++j)
831,457,648✔
987
              {
988
                const double aij =
736,152,672✔
989
                  -Dg * n_f.Dot(intS_shapeI_gradshapeJ(j, i) + intS_shapeI_gradshapeJ(i, j));
736,152,672✔
990
                const double aij_bc_value = aij * bc_value;
736,152,672✔
991

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

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

1004
            for (size_t fi = 0; fi < num_face_nodes; ++fi)
3,523,320✔
1005
            {
1006
              const int i = cell_mapping.MapFaceNode(f, fi);
2,617,680✔
1007

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

UNCOV
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);
33,011,542✔
1020
      OpenSnPETScCall(VecSetValues(rhs_, nn, cell_idxs.data(), cell_rhs.data(), ADD_VALUES));
33,011,542✔
1021
    } // for g
33,011,542✔
1022
  } // for cell
3,198,148✔
1023

1024
  OpenSnPETScCall(VecAssemblyBegin(rhs_));
9,098✔
1025
  OpenSnPETScCall(VecAssemblyEnd(rhs_));
9,098✔
1026

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

1031
void
1032
DiffusionMIPSolver::Assemble_b(Vec petsc_q_vector)
×
1033
{
UNCOV
1034
  const std::string fname = "acceleration::DiffusionMIPSolver::"
×
1035
                            "Assemble_b";
×
UNCOV
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.");
×
UNCOV
1039
  if (options.verbose)
×
1040
    log.Log() << program_timer.GetTimeString() << " Starting assembly";
×
1041

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

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

1047
  OpenSnPETScCall(VecSet(rhs_, 0.0));
×
UNCOV
1048
  for (const auto& cell : grid_->local_cells)
×
1049
  {
UNCOV
1050
    const size_t num_faces = cell.faces.size();
×
1051
    const auto& cell_mapping = sdm_.GetCellMapping(cell);
×
UNCOV
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

UNCOV
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

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

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

UNCOV
1071
      std::vector<double> qg(num_nodes, 0.0);
×
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
UNCOV
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)
×
UNCOV
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
      {
UNCOV
1088
        const auto& face = cell.faces[f];
×
1089
        const auto& n_f = face.normal;
×
UNCOV
1090
        const size_t num_face_nodes = cell_mapping.GetNumFaceNodes(f);
×
1091

UNCOV
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

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

UNCOV
1098
        if (not face.has_neighbor and not suppress_bcs_)
×
1099
        {
UNCOV
1100
          BoundaryCondition bc;
×
UNCOV
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
UNCOV
1109
            double kappa = 1.0;
×
UNCOV
1110
            if (cell.GetType() == CellType::SLAB)
×
1111
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
×
UNCOV
1112
            if (cell.GetType() == CellType::POLYGON)
×
1113
              kappa = fmax(options.penalty_factor * Dg / hm, 0.25);
×
UNCOV
1114
            if (cell.GetType() == CellType::POLYHEDRON)
×
1115
              kappa = fmax(options.penalty_factor * 2.0 * Dg / hm, 0.25);
×
1116

1117
            // Assembly penalty terms
UNCOV
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
              {
UNCOV
1124
                const int jm = cell_mapping.MapFaceNode(f, fj);
×
1125

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

UNCOV
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^-
UNCOV
1138
            for (size_t i = 0; i < num_nodes; ++i)
×
1139
            {
UNCOV
1140
              for (size_t j = 0; j < num_nodes; ++j)
×
1141
              {
UNCOV
1142
                const double aij =
×
1143
                  -Dg * n_f.Dot(intS_shapeI_gradshapeJ(j, i) + intS_shapeI_gradshapeJ(i, j));
×
UNCOV
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
UNCOV
1150
          else if (bc.type == BCType::ROBIN)
×
1151
          {
UNCOV
1152
            const double bval = bc.values[1];
×
1153
            const double fval = bc.values[2];
×
1154

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

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

UNCOV
1162
              if (std::fabs(fval) >= 1.0e-12)
×
1163
              {
UNCOV
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));
×
UNCOV
1174
    } // for g
×
1175
  } // for cell
×
1176

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

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

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

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

1192
  const auto num_faces = cell.faces.size();
156,492,884✔
1193
  const auto num_vertices = cell.vertex_ids.size();
156,492,884✔
1194

1195
  const auto volume = cell.volume;
156,492,884✔
1196
  const auto face_area = cell.faces.at(f).area;
156,492,884✔
1197

1198
  /**Lambda to compute surface area.*/
1199
  auto ComputeSurfaceArea = [&cell, &num_faces]()
257,207,404✔
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
  };
156,492,884✔
1207

1208
  if (cell.GetType() == CellType::SLAB)
156,492,884✔
1209
  {
1210
    hp = volume / 2.0;
13,775,084✔
1211
  }
1212
  else if (cell.GetType() == CellType::POLYGON)
142,717,800✔
1213
  {
1214
    if (num_faces == 3)
42,003,280✔
1215
      hp = 2.0 * volume / face_area;
×
1216
    else if (num_faces == 4)
42,003,280✔
1217
      hp = volume / face_area;
42,003,280✔
1218
    else // Nv > 4
1219
    {
1220
      const auto surface_area = ComputeSurfaceArea();
×
1221

1222
      if (num_faces % 2 == 0)
×
UNCOV
1223
        hp = 4.0 * volume / surface_area;
×
1224
      else
1225
      {
UNCOV
1226
        hp = 2.0 * volume / surface_area;
×
UNCOV
1227
        hp +=
×
UNCOV
1228
          sqrt(2.0 * volume /
×
UNCOV
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
UNCOV
1242
      hp = 6 * volume / surface_area;
×
1243
  } // Polyhedron
1244
  else
UNCOV
1245
    throw std::logic_error("acceleration::DiffusionMIPSolver::HPerpendicular: "
×
UNCOV
1246
                           "Unsupported cell type in call to HPerpendicular");
×
1247

1248
  return hp;
156,492,884✔
1249
}
1250

1251
int
1252
DiffusionMIPSolver::MapFaceNodeDisc(const Cell& cur_cell,
7,428,702✔
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);
7,428,702✔
1262
  const auto& adj_cell_mapping = sdm_.GetCellMapping(adj_cell);
7,428,702✔
1263

1264
  const int i = cur_cell_mapping.MapFaceNode(ccf, ccfi);
7,428,702✔
1265
  const auto& node_i_loc = cc_node_locs[i];
7,428,702✔
1266

1267
  const size_t adj_face_num_nodes = adj_cell_mapping.GetNumFaceNodes(acf);
7,428,702✔
1268

1269
  for (size_t fj = 0; fj < adj_face_num_nodes; ++fj)
16,908,302✔
1270
  {
1271
    const int j = adj_cell_mapping.MapFaceNode(acf, fj);
16,908,302✔
1272
    if ((node_i_loc - ac_node_locs[j]).NormSquare() < epsilon)
16,908,302✔
1273
      return j;
7,428,702✔
1274
  }
1275

UNCOV
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