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

Open-Sn / opensn / 25093042657

27 Apr 2026 05:34PM UTC coverage: 75.068%. Remained the same
25093042657

push

github

web-flow
Merge pull request #1033 from wdhawkins/iteration_logging

Improvements for iteration logging

364 of 485 new or added lines in 29 files covered. (75.05%)

478 existing lines in 26 files now uncovered.

21700 of 28907 relevant lines covered (75.07%)

65057883.8 hits per line

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

79.89
/modules/diffusion/diffusion.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.h"
5
#include "framework/logging/log_format.h"
6
#include "framework/math/spatial_discretization/spatial_discretization.h"
7
#include "framework/mesh/mesh_continuum/mesh_continuum.h"
8
#include "framework/math/petsc_utils/petsc_utils.h"
9
#include "framework/logging/log.h"
10
#include "framework/runtime.h"
11
#include "framework/utils/timer.h"
12
#include <sstream>
13

14
namespace opensn
15
{
16

17
namespace
18
{
19

20
void
21
LogDiffusionSolveStart(const std::string& name, Vec rhs)
132✔
22
{
23
  double rhs_norm = 0.0;
132✔
24
  OpenSnPETScCall(VecNorm(rhs, NORM_2, &rhs_norm));
132✔
25

26
  std::ostringstream out;
132✔
27
  out << name << " solve";
132✔
28
  AppendNumericField(out, "rhs_norm", rhs_norm, Scientific(6));
132✔
29
  log.Log() << program_timer.GetTimeString() << " " << out.str();
396✔
30
}
132✔
31

32
void
33
LogDiffusionSolveFinal(const std::string& name, KSP ksp, Vec x)
132✔
34
{
35
  double solution_norm = 0.0;
132✔
36
  OpenSnPETScCall(VecNorm(x, NORM_2, &solution_norm));
132✔
37

38
  KSPConvergedReason reason = KSP_CONVERGED_ITERATING;
132✔
39
  OpenSnPETScCall(KSPGetConvergedReason(ksp, &reason));
132✔
40
  PetscInt iterations = 0;
132✔
41
  OpenSnPETScCall(KSPGetIterationNumber(ksp, &iterations));
132✔
42

43
  const auto status = KSPReasonToPETScSolverStatus(reason);
132✔
44

45
  std::ostringstream out;
132✔
46
  out << name << " final, status = " << PETScSolverStatusName(status);
132✔
47
  AppendNumericField(out, "iterations", static_cast<std::size_t>(iterations));
132✔
48
  AppendNumericField(out, "solution_norm", solution_norm, Scientific(6));
132✔
49
  if (log.GetVerbosity() >= 2)
132✔
NEW
50
    out << ", detail = " << GetPETScConvergedReasonstring(reason);
×
51
  log.Log() << program_timer.GetTimeString() << " " << out.str();
396✔
52
}
132✔
53

54
} // namespace
55

56
DiffusionSolver::DiffusionSolver(std::string name,
69✔
57
                                 const opensn::SpatialDiscretization& sdm,
58
                                 const UnknownManager& uk_man,
59
                                 std::map<uint64_t, BoundaryCondition> bcs,
60
                                 MatID2XSMap map_mat_id_2_xs,
61
                                 const std::vector<UnitCellMatrices>& unit_cell_matrices,
62
                                 const bool suppress_bcs,
63
                                 const bool requires_ghosts,
64
                                 const bool verbose)
69✔
65
  : name_(std::move(name)),
69✔
66
    grid_(sdm.GetGrid()),
69✔
67
    sdm_(sdm),
69✔
68
    uk_man_(uk_man),
69✔
69
    bcs_(std::move(bcs)),
69✔
70
    mat_id_2_xs_map_(std::move(map_mat_id_2_xs)),
69✔
71
    unit_cell_matrices_(unit_cell_matrices),
69✔
72
    num_local_dofs_(static_cast<PetscInt>(sdm_.GetNumLocalDOFs(uk_man_))),
69✔
73
    num_global_dofs_(static_cast<PetscInt>(sdm_.GetNumGlobalDOFs(uk_man_))),
69✔
74
    A_(nullptr),
69✔
75
    rhs_(nullptr),
69✔
76
    ksp_(nullptr),
69✔
77
    requires_ghosts_(requires_ghosts),
69✔
78
    suppress_bcs_(suppress_bcs)
69✔
79
{
80
  options.verbose = verbose;
69✔
81
}
69✔
82

83
DiffusionSolver::~DiffusionSolver()
414✔
84
{
85
  OpenSnPETScCall(MatDestroy(&A_));
86
  OpenSnPETScCall(VecDestroy(&rhs_));
87
  OpenSnPETScCall(KSPDestroy(&ksp_));
88
}
138✔
89

69✔
90
std::string
69✔
91
DiffusionSolver::GetName() const
69✔
92
{
×
UNCOV
93
  return name_;
×
94
}
95

96
const Vec&
UNCOV
97
DiffusionSolver::GetRHS() const
×
98
{
UNCOV
99
  return rhs_;
×
100
}
101

102
const UnknownManager&
103
DiffusionSolver::GetUnknownStructure() const
20,060✔
104
{
105
  return uk_man_;
20,060✔
106
}
107

108
const SpatialDiscretization&
109
DiffusionSolver::GetSpatialDiscretization() const
13,672✔
110
{
111
  return sdm_;
13,672✔
112
}
113

114
std::pair<size_t, size_t>
UNCOV
115
DiffusionSolver::GetNumPhiIterativeUnknowns()
×
116
{
UNCOV
117
  return {sdm_.GetNumLocalDOFs(uk_man_), sdm_.GetNumGlobalDOFs(uk_man_)};
×
118
}
119

120
void
121
DiffusionSolver::AddToRHS(const std::vector<double>& values)
128✔
122
{
123
  const auto num_local_dofs = sdm_.GetNumLocalDOFs(uk_man_);
128✔
124
  if (num_local_dofs != values.size())
128✔
UNCOV
125
    throw std::invalid_argument("Vector size mismatch.");
×
126

127
  PetscScalar* rhs_ptr = nullptr;
128✔
128
  OpenSnPETScCall(VecGetArray(rhs_, &rhs_ptr));
128✔
129
  for (size_t i = 0; i < num_local_dofs; ++i)
2,688,128✔
130
    rhs_ptr[i] += values[i];
2,688,000✔
131
  OpenSnPETScCall(VecRestoreArray(rhs_, &rhs_ptr));
128✔
132
}
128✔
133

134
void
135
DiffusionSolver::AddToMatrix(const std::vector<PetscInt>& rows,
4✔
136
                             const std::vector<PetscInt>& cols,
137
                             const std::vector<double>& vals)
138
{
139
  if (rows.size() != cols.size() or rows.size() != vals.size())
4✔
UNCOV
140
    throw std::invalid_argument("The number of row entries, column entries, and value "
×
141
                                "entries do not agree.");
×
142
  for (int i = 0; i < vals.size(); ++i)
4✔
UNCOV
143
    OpenSnPETScCall(MatSetValue(A_, rows[i], cols[i], vals[i], ADD_VALUES));
×
144
  OpenSnPETScCall(MatAssemblyBegin(A_, MAT_FLUSH_ASSEMBLY));
4✔
145
  OpenSnPETScCall(MatAssemblyEnd(A_, MAT_FLUSH_ASSEMBLY));
4✔
146
}
4✔
147

148
void
149
DiffusionSolver::Initialize()
69✔
150
{
151
  if (options.verbose)
69✔
152
    log.Log() << name_ << ": Initializing PETSc items";
12✔
153

154
  if (options.verbose)
69✔
155
    log.Log() << name_ << ": Global number of DOFs=" << num_global_dofs_;
12✔
156

157
  opensn::mpi_comm.barrier();
69✔
158
  log.Log() << "Sparsity pattern";
138✔
159
  opensn::mpi_comm.barrier();
69✔
160
  // Create Matrix
161
  std::vector<int64_t> nodal_nnz_in_diag;
69✔
162
  std::vector<int64_t> nodal_nnz_off_diag;
69✔
163
  sdm_.BuildSparsityPattern(nodal_nnz_in_diag, nodal_nnz_off_diag, uk_man_);
69✔
164
  opensn::mpi_comm.barrier();
69✔
165
  log.Log() << "Done Sparsity pattern";
138✔
166
  opensn::mpi_comm.barrier();
69✔
167
  A_ = CreateSquareMatrix(num_local_dofs_, num_global_dofs_);
69✔
168
  InitMatrixSparsity(A_, nodal_nnz_in_diag, nodal_nnz_off_diag);
69✔
169
  opensn::mpi_comm.barrier();
69✔
170
  log.Log() << "Done matrix creation";
138✔
171
  opensn::mpi_comm.barrier();
69✔
172

173
  // Create RHS
174
  if (not requires_ghosts_)
69✔
175
    rhs_ = CreateVector(num_local_dofs_, num_global_dofs_);
64✔
176
  else
177
  {
178
    auto ghost_ids = sdm_.GetGhostDOFIndices(uk_man_);
5✔
179
    std::vector<int64_t> ghids(ghost_ids.begin(), ghost_ids.end());
5✔
180
    rhs_ = CreateVectorWithGhosts(num_local_dofs_,
5✔
181
                                  num_global_dofs_,
5✔
182
                                  static_cast<int64_t>(sdm_.GetNumGhostDOFs(uk_man_)),
5✔
183
                                  ghids);
184
  }
5✔
185

186
  opensn::mpi_comm.barrier();
69✔
187
  log.Log() << "Done vector creation";
138✔
188
  opensn::mpi_comm.barrier();
69✔
189

190
  // Create KSP
191
  OpenSnPETScCall(KSPCreate(opensn::mpi_comm, &ksp_));
69✔
192
  OpenSnPETScCall(KSPSetOptionsPrefix(ksp_, name_.c_str()));
69✔
193
  OpenSnPETScCall(KSPSetType(ksp_, KSPCG));
69✔
194

195
  OpenSnPETScCall(
69✔
196
    KSPSetTolerances(ksp_, 1.0e-50, options.residual_tolerance, 1.0e50, options.max_iters));
197

198
  // Set Pre-conditioner
199
  PC pc = nullptr;
69✔
200
  OpenSnPETScCall(KSPGetPC(ksp_, &pc));
69✔
201
  //  PCSetType(pc, PCGAMG);
202
  OpenSnPETScCall(PCSetType(pc, PCHYPRE));
69✔
203

204
  OpenSnPETScCall(PCHYPRESetType(pc, "boomeramg"));
69✔
205
  std::vector<std::string> pc_options = {"pc_hypre_boomeramg_agg_nl 1",
69✔
206
                                         "pc_hypre_boomeramg_P_max 4",
207
                                         "pc_hypre_boomeramg_grid_sweeps_coarse 1",
208
                                         "pc_hypre_boomeramg_max_levels 25",
209
                                         "pc_hypre_boomeramg_relax_type_all symmetric-SOR/Jacobi",
210
                                         "pc_hypre_boomeramg_coarsen_type HMIS",
211
                                         "pc_hypre_boomeramg_interp_type ext+i"};
69✔
212

213
  if (grid_->GetDimension() == 2)
69✔
214
    pc_options.emplace_back("pc_hypre_boomeramg_strong_threshold 0.6");
38✔
215
  else if (grid_->GetDimension() == 3)
31✔
216
    pc_options.emplace_back("pc_hypre_boomeramg_strong_threshold 0.8");
16✔
217

218
  for (const auto& option : pc_options)
606✔
219
    OpenSnPETScCall(PetscOptionsInsertString(nullptr, ("-" + name_ + option).c_str()));
537✔
220

221
  OpenSnPETScCall(PetscOptionsInsertString(nullptr, options.additional_options_string.c_str()));
69✔
222

223
  OpenSnPETScCall(PCSetFromOptions(pc));
69✔
224
  OpenSnPETScCall(KSPSetFromOptions(ksp_));
69✔
225
}
69✔
226

227
void
228
DiffusionSolver::Solve(std::vector<double>& solution, bool use_initial_guess)
7,952✔
229
{
230
  const std::string fname = "acceleration::DiffusionMIPSolver::Solve";
7,952✔
231
  Vec x = nullptr;
7,952✔
232
  OpenSnPETScCall(VecDuplicate(rhs_, &x));
7,952✔
233
  OpenSnPETScCall(VecSet(x, 0.0));
7,952✔
234

235
  if (not use_initial_guess)
7,952✔
236
    OpenSnPETScCall(KSPSetInitialGuessNonzero(ksp_, PETSC_FALSE));
5,460✔
237
  else
238
    OpenSnPETScCall(KSPSetInitialGuessNonzero(ksp_, PETSC_TRUE));
2,492✔
239

240
  OpenSnPETScCall(KSPSetTolerances(
7,952✔
241
    ksp_, options.residual_tolerance, options.residual_tolerance, 1.0e50, options.max_iters));
242

243
  if (options.perform_symmetry_check)
7,952✔
244
  {
245
    PetscBool symmetry = PETSC_FALSE;
4✔
246
    OpenSnPETScCall(MatIsSymmetric(A_, 1.0e-6, &symmetry));
4✔
247
    if (symmetry == PETSC_FALSE)
4✔
UNCOV
248
      throw std::logic_error(fname + ":Symmetry check failed");
×
249
  }
250

251
  if (options.verbose)
7,952✔
252
  {
253
    OpenSnPETScCall(KSPMonitorSet(ksp_, &KSPMonitorRelativeToRHS, nullptr, nullptr));
132✔
254
    LogDiffusionSolveStart(name_, rhs_);
132✔
255
  }
256

257
  if (use_initial_guess)
7,952✔
258
  {
259
    PetscScalar* x_raw = nullptr;
2,492✔
260
    OpenSnPETScCall(VecGetArray(x, &x_raw));
2,492✔
261
    size_t k = 0;
2,492✔
262
    for (const auto& value : solution)
10,255,292✔
263
      x_raw[k++] = value;
10,252,800✔
264
    OpenSnPETScCall(VecRestoreArray(x, &x_raw));
2,492✔
265
  }
266

267
  // Solve
268
  OpenSnPETScCall(KSPSolve(ksp_, rhs_, x));
7,952✔
269

270
  // Print convergence info
271
  if (options.verbose)
7,952✔
272
    LogDiffusionSolveFinal(name_, ksp_, x);
132✔
273

274
  // Transfer petsc solution to vector
275
  if (requires_ghosts_)
7,952✔
276
  {
277
    CommunicateGhostEntries(x);
130✔
278
    sdm_.LocalizePETScVectorWithGhosts(x, solution, uk_man_);
130✔
279
  }
280
  else
281
    sdm_.LocalizePETScVector(x, solution, uk_man_);
7,822✔
282

283
  // Cleanup x
284
  OpenSnPETScCall(VecDestroy(&x));
7,952✔
285
}
7,952✔
286

287
void
UNCOV
288
DiffusionSolver::Solve(Vec petsc_solution, bool use_initial_guess)
×
289
{
UNCOV
290
  const std::string fname = "acceleration::DiffusionMIPSolver::Solve";
×
UNCOV
291
  Vec x = nullptr;
×
UNCOV
292
  OpenSnPETScCall(VecDuplicate(rhs_, &x));
×
UNCOV
293
  OpenSnPETScCall(VecSet(x, 0.0));
×
294

UNCOV
295
  if (not use_initial_guess)
×
UNCOV
296
    OpenSnPETScCall(KSPSetInitialGuessNonzero(ksp_, PETSC_FALSE));
×
297
  else
UNCOV
298
    OpenSnPETScCall(KSPSetInitialGuessNonzero(ksp_, PETSC_TRUE));
×
299

UNCOV
300
  OpenSnPETScCall(KSPSetTolerances(
×
301
    ksp_, options.residual_tolerance, options.residual_tolerance, 1.0e50, options.max_iters));
302

UNCOV
303
  if (options.perform_symmetry_check)
×
304
  {
UNCOV
305
    PetscBool symmetry = PETSC_FALSE;
×
UNCOV
306
    OpenSnPETScCall(MatIsSymmetric(A_, 1.0e-6, &symmetry));
×
UNCOV
307
    if (symmetry == PETSC_FALSE)
×
UNCOV
308
      throw std::logic_error(fname + ":Symmetry check failed");
×
309
  }
310

UNCOV
311
  if (options.verbose)
×
312
  {
UNCOV
313
    OpenSnPETScCall(KSPMonitorSet(ksp_, &KSPMonitorRelativeToRHS, nullptr, nullptr));
×
NEW
314
    LogDiffusionSolveStart(name_, rhs_);
×
315
  }
316

317
  if (use_initial_guess)
×
318
  {
319
    OpenSnPETScCall(VecCopy(petsc_solution, x));
×
320
  }
321

322
  // Solve
323
  OpenSnPETScCall(KSPSolve(ksp_, rhs_, x));
×
324

325
  // Print convergence info
UNCOV
326
  if (options.verbose)
×
NEW
327
    LogDiffusionSolveFinal(name_, ksp_, x);
×
328

329
  // Transfer petsc solution to vector
UNCOV
330
  OpenSnPETScCall(VecCopy(x, petsc_solution));
×
331

332
  // Cleanup x
UNCOV
333
  OpenSnPETScCall(VecDestroy(&x));
×
UNCOV
334
}
×
335

336
} // 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