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

Open-Sn / opensn / 21738357923

06 Feb 2026 02:45AM UTC coverage: 74.553% (+0.6%) from 73.983%
21738357923

push

github

web-flow
Merge pull request #921 from wdhawkins/update_quick_install_doc

Updating quick install instructions.

19363 of 25972 relevant lines covered (74.55%)

59700221.09 hits per line

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

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

4
#include "modules/linear_boltzmann_solvers/lbs_problem/lbs_problem.h"
5
#include "modules/linear_boltzmann_solvers/lbs_problem/iterative_methods/wgs_context.h"
6
#include "modules/linear_boltzmann_solvers/lbs_problem/iterative_methods/ags_linear_solver.h"
7
#include "modules/linear_boltzmann_solvers/lbs_problem/point_source/point_source.h"
8
#include "modules/linear_boltzmann_solvers/lbs_problem/groupset/lbs_groupset.h"
9
#include "framework/math/spatial_discretization/finite_element/piecewise_linear/piecewise_linear_discontinuous.h"
10
#include "framework/field_functions/field_function_grid_based.h"
11
#include "framework/materials/multi_group_xs/multi_group_xs.h"
12
#include "framework/mesh/mesh_continuum/mesh_continuum.h"
13
#include "framework/utils/hdf_utils.h"
14
#include "framework/object_factory.h"
15
#include "framework/logging/log.h"
16
#include "framework/runtime.h"
17
#include "framework/data_types/allowable_range.h"
18
#include "caliper/cali.h"
19
#include <algorithm>
20
#include <iomanip>
21
#include <fstream>
22
#include <cstring>
23
#include <cassert>
24
#include <memory>
25
#include <stdexcept>
26
#include <sys/stat.h>
27

28
namespace opensn
29
{
30

31
LBSProblem::LBSProblem(std::string name, std::shared_ptr<MeshContinuum> grid)
×
32
  : Problem(std::move(name)), grid_(std::move(grid)), use_gpus_(false)
×
33
{
34
}
×
35

36
InputParameters
37
LBSProblem::GetInputParameters()
494✔
38
{
39
  InputParameters params = Problem::GetInputParameters();
494✔
40

41
  params.ChangeExistingParamToOptional("name", "LBSProblem");
988✔
42

43
  params.AddRequiredParameter<std::shared_ptr<MeshContinuum>>("mesh", "Mesh");
988✔
44

45
  params.AddRequiredParameter<size_t>("num_groups", "The total number of groups within the solver");
988✔
46

47
  params.AddRequiredParameterArray("groupsets",
988✔
48
                                   "An array of blocks each specifying the input parameters for a "
49
                                   "<TT>LBSGroupset</TT>.");
50
  params.LinkParameterToBlock("groupsets", "LBSGroupset");
988✔
51

52
  params.AddRequiredParameterArray("xs_map",
988✔
53
                                   "Cross-section map from block IDs to cross-section objects.");
54

55
  params.AddOptionalParameterArray<std::shared_ptr<VolumetricSource>>(
988✔
56
    "volumetric_sources", {}, "An array of handles to volumetric sources.");
57

58
  params.AddOptionalParameterArray<std::shared_ptr<PointSource>>(
988✔
59
    "point_sources", {}, "An array of point sources.");
60

61
  params.AddOptionalParameterBlock(
988✔
62
    "options", ParameterBlock(), "Block of options. See <TT>OptionsBlock</TT>.");
988✔
63
  params.LinkParameterToBlock("options", "OptionsBlock");
988✔
64

65
  params.AddOptionalParameter("use_gpus", false, "Offload the sweep computation to GPUs.");
988✔
66

67
  return params;
494✔
68
}
×
69

70
LBSProblem::LBSProblem(const InputParameters& params)
494✔
71
  : Problem(params),
72
    num_groups_(params.GetParamValue<size_t>("num_groups")),
494✔
73
    grid_(params.GetSharedPtrParam<MeshContinuum>("mesh")),
494✔
74
    use_gpus_(params.GetParamValue<bool>("use_gpus"))
1,482✔
75
{
76
  // Check system for GPU acceleration
77
  if (use_gpus_)
494✔
78
  {
79
#ifdef __OPENSN_WITH_GPU__
80
    CheckCapableDevices();
81
#else
82
    throw std::invalid_argument(
×
83
      GetName() + ": GPU support was requested, but OpenSn was built without CUDA enabled.");
×
84
#endif // __OPENSN_WITH_GPU__
85
  }
86

87
  // Initialize options
88
  if (params.IsParameterValid("options"))
494✔
89
  {
90
    auto options_params = LBSProblem::GetOptionsBlock();
314✔
91
    options_params.AssignParameters(params.GetParam("options"));
316✔
92
    SetOptions(options_params);
312✔
93
  }
314✔
94

95
  // Set geometry type
96
  geometry_type_ = grid_->GetGeometryType();
492✔
97
  if (geometry_type_ == GeometryType::INVALID)
492✔
98
    throw std::runtime_error(GetName() + ": Invalid geometry type.");
×
99

100
  InitializeGroupsets(params);
492✔
101
  InitializeSources(params);
492✔
102
  InitializeXSmapAndDensities(params);
492✔
103
  InitializeMaterials();
492✔
104
}
512✔
105

106
LBSOptions&
107
LBSProblem::GetOptions()
162,847✔
108
{
109
  return options_;
162,847✔
110
}
111

112
const LBSOptions&
113
LBSProblem::GetOptions() const
675,161,350✔
114
{
115
  return options_;
675,161,350✔
116
}
117

118
double
119
LBSProblem::GetTime() const
418,312✔
120
{
121
  return time_;
418,312✔
122
}
123

124
void
125
LBSProblem::SetTime(double time)
4,716✔
126
{
127
  time_ = time;
4,716✔
128
}
4,716✔
129

130
void
131
LBSProblem::SetTimeStep(double dt)
1,780✔
132
{
133
  if (dt <= 0.0)
1,780✔
134
    throw std::runtime_error(GetName() + " dt must be greater than zero.");
×
135
  dt_ = dt;
1,780✔
136
}
1,780✔
137

138
double
139
LBSProblem::GetTimeStep() const
2,147,483,647✔
140
{
141
  return dt_;
2,147,483,647✔
142
}
143

144
void
145
LBSProblem::SetTheta(double theta)
268✔
146
{
147
  if (theta <= 0.0 or theta > 1.0)
268✔
148
    throw std::runtime_error(GetName() + " theta must be in (0.0, 1.0].");
×
149
  theta_ = theta;
268✔
150
}
268✔
151

152
double
153
LBSProblem::GetTheta() const
2,147,483,647✔
154
{
155
  return theta_;
2,147,483,647✔
156
}
157

158
GeometryType
159
LBSProblem::GetGeometryType() const
4✔
160
{
161
  return geometry_type_;
4✔
162
}
163

164
size_t
165
LBSProblem::GetNumMoments() const
519,165✔
166
{
167
  return num_moments_;
519,165✔
168
}
169

170
unsigned int
171
LBSProblem::GetMaxCellDOFCount() const
671✔
172
{
173
  return max_cell_dof_count_;
671✔
174
}
175

176
unsigned int
177
LBSProblem::GetMinCellDOFCount() const
671✔
178
{
179
  return min_cell_dof_count_;
671✔
180
}
181

182
bool
183
LBSProblem::UseGPUs() const
999✔
184
{
185
  return use_gpus_;
999✔
186
}
187

188
size_t
189
LBSProblem::GetNumGroups() const
367,901✔
190
{
191
  return num_groups_;
367,901✔
192
}
193

194
unsigned int
195
LBSProblem::GetScatteringOrder() const
4✔
196
{
197
  return scattering_order_;
4✔
198
}
199

200
size_t
201
LBSProblem::GetNumPrecursors() const
×
202
{
203
  return num_precursors_;
×
204
}
205

206
size_t
207
LBSProblem::GetMaxPrecursorsPerMaterial() const
9,550✔
208
{
209
  return max_precursors_per_material_;
9,550✔
210
}
211

212
const std::vector<LBSGroup>&
213
LBSProblem::GetGroups() const
656,141✔
214
{
215
  return groups_;
656,141✔
216
}
217

218
std::vector<LBSGroupset>&
219
LBSProblem::GetGroupsets()
21,037,207✔
220
{
221
  return groupsets_;
21,037,207✔
222
}
223

224
const std::vector<LBSGroupset>&
225
LBSProblem::GetGroupsets() const
×
226
{
227
  return groupsets_;
×
228
}
229

230
void
231
LBSProblem::AddPointSource(std::shared_ptr<PointSource> point_source)
×
232
{
233
  point_sources_.push_back(point_source);
×
234
  if (discretization_)
×
235
    point_sources_.back()->Initialize(*this);
×
236
}
×
237

238
void
239
LBSProblem::ClearPointSources()
×
240
{
241
  point_sources_.clear();
×
242
}
×
243

244
const std::vector<std::shared_ptr<PointSource>>&
245
LBSProblem::GetPointSources() const
144,920✔
246
{
247
  return point_sources_;
144,920✔
248
}
249

250
void
251
LBSProblem::AddVolumetricSource(std::shared_ptr<VolumetricSource> volumetric_source)
16✔
252
{
253
  volumetric_sources_.push_back(volumetric_source);
16✔
254
  if (discretization_)
16✔
255
    volumetric_sources_.back()->Initialize(*this);
16✔
256
}
16✔
257

258
void
259
LBSProblem::ClearVolumetricSources()
12✔
260
{
261
  volumetric_sources_.clear();
12✔
262
}
12✔
263

264
const std::vector<std::shared_ptr<VolumetricSource>>&
265
LBSProblem::GetVolumetricSources() const
144,920✔
266
{
267
  return volumetric_sources_;
144,920✔
268
}
269

270
const BlockID2XSMap&
271
LBSProblem::GetBlockID2XSMap() const
18,050✔
272
{
273
  return block_id_to_xs_map_;
18,050✔
274
}
275

276
void
277
LBSProblem::SetBlockID2XSMap(const BlockID2XSMap& xs_map)
168✔
278
{
279
  block_id_to_xs_map_ = xs_map;
168✔
280
  InitializeMaterials();
168✔
281
  ResetGPUCarriers();
168✔
282
  InitializeGPUExtras();
168✔
283
}
168✔
284

285
std::shared_ptr<MeshContinuum>
286
LBSProblem::GetGrid() const
742,167✔
287
{
288
  return grid_;
742,167✔
289
}
290

291
const SpatialDiscretization&
292
LBSProblem::GetSpatialDiscretization() const
236,444✔
293
{
294
  return *discretization_;
236,444✔
295
}
296

297
const std::vector<UnitCellMatrices>&
298
LBSProblem::GetUnitCellMatrices() const
19,424✔
299
{
300
  return unit_cell_matrices_;
19,424✔
301
}
302

303
const std::map<uint64_t, UnitCellMatrices>&
304
LBSProblem::GetUnitGhostCellMatrices() const
17✔
305
{
306
  return unit_ghost_cell_matrices_;
17✔
307
}
308

309
std::vector<CellLBSView>&
310
LBSProblem::GetCellTransportViews()
297,522✔
311
{
312
  return cell_transport_views_;
297,522✔
313
}
314

315
const std::vector<CellLBSView>&
316
LBSProblem::GetCellTransportViews() const
625,644✔
317
{
318
  return cell_transport_views_;
625,644✔
319
}
320

321
const UnknownManager&
322
LBSProblem::GetUnknownManager() const
27,019✔
323
{
324
  return flux_moments_uk_man_;
27,019✔
325
}
326

327
size_t
328
LBSProblem::GetLocalNodeCount() const
3,212✔
329
{
330
  return local_node_count_;
3,212✔
331
}
332

333
size_t
334
LBSProblem::GetGlobalNodeCount() const
2,900✔
335
{
336
  return global_node_count_;
2,900✔
337
}
338

339
std::vector<double>&
340
LBSProblem::GetQMomentsLocal()
353,513✔
341
{
342
  return q_moments_local_;
353,513✔
343
}
344

345
const std::vector<double>&
346
LBSProblem::GetQMomentsLocal() const
×
347
{
348
  return q_moments_local_;
×
349
}
350

351
std::vector<double>&
352
LBSProblem::GetExtSrcMomentsLocal()
4✔
353
{
354
  return ext_src_moments_local_;
4✔
355
}
356

357
const std::vector<double>&
358
LBSProblem::GetExtSrcMomentsLocal() const
208,548✔
359
{
360
  return ext_src_moments_local_;
208,548✔
361
}
362

363
std::vector<double>&
364
LBSProblem::GetPhiOldLocal()
273,771✔
365
{
366
  return phi_old_local_;
273,771✔
367
}
368

369
const std::vector<double>&
370
LBSProblem::GetPhiOldLocal() const
×
371
{
372
  return phi_old_local_;
×
373
}
374

375
std::vector<double>&
376
LBSProblem::GetPhiNewLocal()
245,310✔
377
{
378
  return phi_new_local_;
245,310✔
379
}
380

381
const std::vector<double>&
382
LBSProblem::GetPhiNewLocal() const
×
383
{
384
  return phi_new_local_;
×
385
}
386

387
std::vector<double>&
388
LBSProblem::GetPrecursorsNewLocal()
372✔
389
{
390
  return precursor_new_local_;
372✔
391
}
392

393
const std::vector<double>&
394
LBSProblem::GetPrecursorsNewLocal() const
×
395
{
396
  return precursor_new_local_;
×
397
}
398

399
std::vector<double>&
400
LBSProblem::GetDensitiesLocal()
921✔
401
{
402
  return densities_local_;
921✔
403
}
404

405
const std::vector<double>&
406
LBSProblem::GetDensitiesLocal() const
208,548✔
407
{
408
  return densities_local_;
208,548✔
409
}
410

411
SetSourceFunction
412
LBSProblem::GetActiveSetSourceFunction() const
4,254✔
413
{
414
  return active_set_source_function_;
4,254✔
415
}
416

417
void
418
LBSProblem::SetActiveSetSourceFunction(SetSourceFunction source_function)
120✔
419
{
420
  active_set_source_function_ = std::move(source_function);
120✔
421
}
120✔
422

423
std::shared_ptr<AGSLinearSolver>
424
LBSProblem::GetAGSSolver()
1,728✔
425
{
426
  return ags_solver_;
1,728✔
427
}
428

429
std::vector<std::shared_ptr<LinearSolver>>&
430
LBSProblem::GetWGSSolvers()
1,657✔
431
{
432
  return wgs_solvers_;
1,657✔
433
}
434

435
WGSContext&
436
LBSProblem::GetWGSContext(int groupset_id)
11,544✔
437
{
438
  auto& wgs_solver = wgs_solvers_[groupset_id];
11,544✔
439
  auto raw_context = wgs_solver->GetContext();
11,544✔
440
  auto wgs_context_ptr = std::dynamic_pointer_cast<WGSContext>(raw_context);
11,544✔
441
  OpenSnLogicalErrorIf(not wgs_context_ptr, "Failed to cast WGSContext");
11,544✔
442
  return *wgs_context_ptr;
11,544✔
443
}
23,088✔
444

445
std::pair<size_t, size_t>
446
LBSProblem::GetNumPhiIterativeUnknowns()
×
447
{
448
  const auto& sdm = *discretization_;
×
449
  const size_t num_local_phi_dofs = sdm.GetNumLocalDOFs(flux_moments_uk_man_);
×
450
  const size_t num_global_phi_dofs = sdm.GetNumGlobalDOFs(flux_moments_uk_man_);
×
451

452
  return {num_local_phi_dofs, num_global_phi_dofs};
×
453
}
454

455
size_t
456
LBSProblem::MapPhiFieldFunction(size_t g, size_t m) const
27,696✔
457
{
458
  OpenSnLogicalErrorIf(phi_field_functions_local_map_.count({g, m}) == 0,
27,696✔
459
                       std::string("Failure to map phi field function g") + std::to_string(g) +
460
                         " m" + std::to_string(m));
461

462
  return phi_field_functions_local_map_.at({g, m});
27,696✔
463
}
464

465
std::shared_ptr<FieldFunctionGridBased>
466
LBSProblem::GetPowerFieldFunction() const
×
467
{
468
  OpenSnLogicalErrorIf(not options_.power_field_function_on,
×
469
                       "Called when options_.power_field_function_on == false");
470

471
  return field_functions_[power_gen_fieldfunc_local_handle_];
×
472
}
473

474
InputParameters
475
LBSProblem::GetOptionsBlock()
650✔
476
{
477
  InputParameters params;
650✔
478

479
  params.SetGeneralDescription("Set options from a large list of parameters");
1,300✔
480
  params.AddOptionalParameter("max_mpi_message_size",
1,300✔
481
                              32768,
482
                              "The maximum MPI message size used during sweep initialization.");
483
  params.AddOptionalParameter(
1,300✔
484
    "restart_writes_enabled", false, "Flag that controls writing of restart dumps");
485
  params.AddOptionalParameter("write_delayed_psi_to_restart",
1,300✔
486
                              true,
487
                              "Flag that controls writing of delayed angular fluxes to restarts.");
488
  params.AddOptionalParameter(
1,300✔
489
    "read_restart_path", "", "Full path for reading restart dumps including file stem.");
490
  params.AddOptionalParameter(
1,300✔
491
    "write_restart_path", "", "Full path for writing restart dumps including file stem.");
492
  params.AddOptionalParameter("write_restart_time_interval",
1,300✔
493
                              0,
494
                              "Time interval in seconds at which restart data is to be written.");
495
  params.AddOptionalParameter(
1,300✔
496
    "use_precursors", false, "Flag for using delayed neutron precursors.");
497
  params.AddOptionalParameter("use_source_moments",
1,300✔
498
                              false,
499
                              "Flag for ignoring fixed sources and selectively using source "
500
                              "moments obtained elsewhere.");
501
  params.AddOptionalParameter(
1,300✔
502
    "save_angular_flux", false, "Flag indicating whether angular fluxes are to be stored or not.");
503
  params.AddOptionalParameter(
1,300✔
504
    "adjoint", false, "Flag for toggling whether the solver is in adjoint mode.");
505
  params.AddOptionalParameter(
1,300✔
506
    "verbose_inner_iterations", true, "Flag to control verbosity of inner iterations.");
507
  params.AddOptionalParameter(
1,300✔
508
    "verbose_outer_iterations", true, "Flag to control verbosity of across-groupset iterations.");
509
  params.AddOptionalParameter(
1,300✔
510
    "max_ags_iterations", 100, "Maximum number of across-groupset iterations.");
511
  params.AddOptionalParameter("ags_tolerance", 1.0e-6, "Across-groupset iterations tolerance.");
1,300✔
512
  params.AddOptionalParameter("ags_convergence_check",
1,300✔
513
                              "l2",
514
                              "Type of convergence check for AGS iterations. Valid values are "
515
                              "`\"l2\"` and '\"pointwise\"'");
516
  params.AddOptionalParameter(
1,300✔
517
    "verbose_ags_iterations", true, "Flag to control verbosity of across-groupset iterations.");
518
  params.AddOptionalParameter("power_field_function_on",
1,300✔
519
                              false,
520
                              "Flag to control the creation of the power generation field "
521
                              "function. If set to `true` then a field function will be created "
522
                              "with the general name <solver_name>_power_generation`.");
523
  params.AddOptionalParameter("power_default_kappa",
1,300✔
524
                              3.20435e-11,
525
                              "Default `kappa` value (Energy released per fission) to use for "
526
                              "power generation when cross sections do not have `kappa` values. "
527
                              "Default: 3.20435e-11 Joule (corresponding to 200 MeV per fission).");
528
  params.AddOptionalParameter("power_normalization",
1,300✔
529
                              -1.0,
530
                              "Power normalization factor to use. Supply a negative or zero number "
531
                              "to turn this off.");
532
  params.AddOptionalParameter("field_function_prefix_option",
1,300✔
533
                              "prefix",
534
                              "Prefix option on field function names. Default: `\"prefix\"`. Can "
535
                              "be `\"prefix\"` or `\"solver_name\"`. By default this option uses "
536
                              "the value of the `field_function_prefix` parameter. If this "
537
                              "parameter is not set, flux field functions will be exported as "
538
                              "`phi_gXXX_mYYY` where `XXX` is the zero padded 3 digit group number "
539
                              "and `YYY` is the zero padded 3 digit moment.");
540
  params.AddOptionalParameter("field_function_prefix",
1,300✔
541
                              "",
542
                              "Prefix to use on all field functions. Default: `\"\"`. By default "
543
                              "this option is empty. Ff specified, flux moments will be exported "
544
                              "as `prefix_phi_gXXX_mYYY` where `XXX` is the zero padded 3 digit "
545
                              "group number and `YYY` is the zero padded 3 digit moment. The "
546
                              "underscore after \"prefix\" is added automatically.");
547
  params.ConstrainParameterRange("ags_convergence_check",
1,950✔
548
                                 AllowableRangeList::New({"l2", "pointwise"}));
650✔
549
  params.ConstrainParameterRange("field_function_prefix_option",
1,950✔
550
                                 AllowableRangeList::New({"prefix", "solver_name"}));
650✔
551

552
  return params;
650✔
553
}
×
554

555
InputParameters
556
LBSProblem::GetXSMapEntryBlock()
917✔
557
{
558
  InputParameters params;
917✔
559
  params.SetGeneralDescription("Set the cross-section map for the solver.");
1,834✔
560
  params.AddRequiredParameterArray("block_ids", "Mesh block IDs");
1,834✔
561
  params.AddRequiredParameter<std::shared_ptr<MultiGroupXS>>("xs", "Cross-section object");
1,834✔
562
  return params;
917✔
563
}
×
564

565
void
566
LBSProblem::SetOptions(const InputParameters& input)
324✔
567
{
568
  auto params = LBSProblem::GetOptionsBlock();
324✔
569
  params.AssignParameters(input);
324✔
570

571
  // Handle order insensitive options
572
  for (size_t p = 0; p < params.GetNumParameters(); ++p)
7,128✔
573
  {
574
    const auto& spec = params.GetParam(p);
6,804✔
575

576
    if (spec.GetName() == "max_mpi_message_size")
6,804✔
577
      options_.max_mpi_message_size = spec.GetValue<int>();
324✔
578

579
    else if (spec.GetName() == "restart_writes_enabled")
6,480✔
580
      options_.restart_writes_enabled = spec.GetValue<bool>();
324✔
581

582
    else if (spec.GetName() == "write_delayed_psi_to_restart")
6,156✔
583
      options_.write_delayed_psi_to_restart = spec.GetValue<bool>();
324✔
584

585
    else if (spec.GetName() == "read_restart_path")
5,832✔
586
    {
587
      options_.read_restart_path = spec.GetValue<std::string>();
324✔
588
      if (not options_.read_restart_path.empty())
324✔
589
        options_.read_restart_path += std::to_string(opensn::mpi_comm.rank()) + ".restart.h5";
36✔
590
    }
591

592
    else if (spec.GetName() == "write_restart_path")
5,508✔
593
    {
594
      options_.write_restart_path = spec.GetValue<std::string>();
324✔
595
      if (not options_.write_restart_path.empty())
324✔
596
        options_.write_restart_path += std::to_string(opensn::mpi_comm.rank()) + ".restart.h5";
×
597
    }
598

599
    else if (spec.GetName() == "write_restart_time_interval")
5,184✔
600
      options_.write_restart_time_interval = std::chrono::seconds(spec.GetValue<int>());
324✔
601

602
    else if (spec.GetName() == "use_precursors")
4,860✔
603
      options_.use_precursors = spec.GetValue<bool>();
324✔
604

605
    else if (spec.GetName() == "use_source_moments")
4,536✔
606
      options_.use_src_moments = spec.GetValue<bool>();
324✔
607

608
    else if (spec.GetName() == "save_angular_flux")
4,212✔
609
      options_.save_angular_flux = spec.GetValue<bool>();
324✔
610

611
    else if (spec.GetName() == "verbose_inner_iterations")
3,888✔
612
      options_.verbose_inner_iterations = spec.GetValue<bool>();
324✔
613

614
    else if (spec.GetName() == "max_ags_iterations")
3,564✔
615
      options_.max_ags_iterations = spec.GetValue<int>();
324✔
616

617
    else if (spec.GetName() == "ags_tolerance")
3,240✔
618
      options_.ags_tolerance = spec.GetValue<double>();
324✔
619

620
    else if (spec.GetName() == "ags_convergence_check")
2,916✔
621
    {
622
      auto check = spec.GetValue<std::string>();
324✔
623
      if (check == "pointwise")
324✔
624
        options_.ags_pointwise_convergence = true;
×
625
    }
324✔
626

627
    else if (spec.GetName() == "verbose_ags_iterations")
2,592✔
628
      options_.verbose_ags_iterations = spec.GetValue<bool>();
324✔
629

630
    else if (spec.GetName() == "verbose_outer_iterations")
2,268✔
631
      options_.verbose_outer_iterations = spec.GetValue<bool>();
324✔
632

633
    else if (spec.GetName() == "power_field_function_on")
1,944✔
634
      options_.power_field_function_on = spec.GetValue<bool>();
324✔
635

636
    else if (spec.GetName() == "power_default_kappa")
1,620✔
637
      options_.power_default_kappa = spec.GetValue<double>();
324✔
638

639
    else if (spec.GetName() == "power_normalization")
1,296✔
640
      options_.power_normalization = spec.GetValue<double>();
324✔
641

642
    else if (spec.GetName() == "field_function_prefix_option")
972✔
643
    {
644
      options_.field_function_prefix_option = spec.GetValue<std::string>();
324✔
645
    }
646

647
    else if (spec.GetName() == "field_function_prefix")
648✔
648
      options_.field_function_prefix = spec.GetValue<std::string>();
324✔
649

650
    else if (spec.GetName() == "adjoint")
324✔
651
      options_.adjoint = spec.GetValue<bool>();
324✔
652

653
  } // for p
654

655
  if (options_.restart_writes_enabled)
324✔
656
  {
657
    // Create restart directory if necessary
658
    auto dir = options_.write_restart_path.parent_path();
×
659
    if (opensn::mpi_comm.rank() == 0)
×
660
    {
661
      if (not std::filesystem::exists(dir))
×
662
      {
663
        if (not std::filesystem::create_directories(dir))
×
664
          throw std::runtime_error(GetName() + ": Failed to create restart directory " +
×
665
                                   dir.string());
×
666
      }
667
      else if (not std::filesystem::is_directory(dir))
×
668
        throw std::runtime_error(GetName() + ": Restart path exists but is not a directory " +
×
669
                                 dir.string());
×
670
    }
671
    opensn::mpi_comm.barrier();
×
672
    UpdateRestartWriteTime();
×
673
  }
×
674
}
324✔
675

676
void
677
LBSProblem::Initialize()
492✔
678
{
679
  CALI_CXX_MARK_SCOPE("LBSProblem::Initialize");
492✔
680

681
  PrintSimHeader();
492✔
682
  mpi_comm.barrier();
492✔
683

684
  InitializeSpatialDiscretization();
492✔
685
  InitializeParrays();
492✔
686
  InitializeBoundaries();
492✔
687
  InitializeGPUExtras();
492✔
688
  SetAdjoint(options_.adjoint);
492✔
689

690
  // Initialize point sources
691
  for (auto& point_source : point_sources_)
501✔
692
    point_source->Initialize(*this);
9✔
693

694
  // Initialize volumetric sources
695
  for (auto& volumetric_source : volumetric_sources_)
919✔
696
    volumetric_source->Initialize(*this);
427✔
697
}
492✔
698

699
void
700
LBSProblem::PrintSimHeader()
×
701
{
702
  if (opensn::mpi_comm.rank() == 0)
×
703
  {
704
    std::stringstream outstr;
×
705
    outstr << "\n"
×
706
           << "Initializing " << GetName() << "\n\n"
×
707
           << "Scattering order    : " << scattering_order_ << "\n"
×
708
           << "Number of moments   : " << num_moments_ << "\n"
×
709
           << "Number of groups    : " << groups_.size() << "\n"
×
710
           << "Number of groupsets : " << groupsets_.size() << "\n\n";
×
711

712
    for (const auto& groupset : groupsets_)
×
713
    {
714
      outstr << "***** Groupset " << groupset.id << " *****\n"
×
715
             << "Groups:\n";
×
716
      const auto& groups = groupset.groups;
717
      constexpr int groups_per_line = 12;
718
      for (size_t i = 0; i < groups.size(); ++i)
×
719
      {
720
        outstr << std::setw(5) << groups[i].id << ' ';
×
721
        if ((i + 1) % groups_per_line == 0)
×
722
          outstr << '\n';
×
723
      }
724
      if (!groups.empty() && groups.size() % groups_per_line != 0)
×
725
        outstr << '\n';
×
726
    }
727

728
    log.Log() << outstr.str() << '\n';
×
729
  }
×
730
}
×
731

732
void
733
LBSProblem::InitializeSources(const InputParameters& params)
492✔
734
{
735
  if (params.Has("volumetric_sources"))
492✔
736
  {
737
    const auto& vol_srcs = params.GetParam("volumetric_sources");
492✔
738
    vol_srcs.RequireBlockTypeIs(ParameterBlockType::ARRAY);
492✔
739
    for (const auto& src : vol_srcs)
919✔
740
      volumetric_sources_.push_back(src.GetValue<std::shared_ptr<VolumetricSource>>());
854✔
741
  }
742

743
  if (params.Has("point_sources"))
492✔
744
  {
745
    const auto& pt_srcs = params.GetParam("point_sources");
492✔
746
    pt_srcs.RequireBlockTypeIs(ParameterBlockType::ARRAY);
492✔
747
    for (const auto& src : pt_srcs)
501✔
748
      point_sources_.push_back(src.GetValue<std::shared_ptr<PointSource>>());
18✔
749
  }
750
}
492✔
751

752
void
753
LBSProblem::InitializeGroupsets(const InputParameters& params)
492✔
754
{
755
  // Initialize groups
756
  if (num_groups_ == 0)
492✔
757
    throw std::invalid_argument(GetName() + ": Number of groups must be > 0");
×
758
  for (size_t g = 0; g < num_groups_; ++g)
21,857✔
759
    groups_.emplace_back(static_cast<int>(g));
21,365✔
760

761
  // Initialize groupsets
762
  const auto& groupsets_array = params.GetParam("groupsets");
492✔
763
  const size_t num_gs = groupsets_array.GetNumParameters();
492✔
764
  if (num_gs == 0)
492✔
765
    throw std::invalid_argument(GetName() + ": At least one groupset must be specified");
×
766
  for (size_t gs = 0; gs < num_gs; ++gs)
1,043✔
767
  {
768
    const auto& groupset_params = groupsets_array.GetParam(gs);
551✔
769
    InputParameters gs_input_params = LBSGroupset::GetInputParameters();
551✔
770
    gs_input_params.SetObjectType("LBSProblem:LBSGroupset");
551✔
771
    gs_input_params.AssignParameters(groupset_params);
551✔
772
    groupsets_.emplace_back(gs_input_params, gs, *this);
551✔
773
    if (groupsets_.back().groups.empty())
551✔
774
    {
775
      std::stringstream oss;
×
776
      oss << GetName() << ": No groups added to groupset " << groupsets_.back().id;
×
777
      throw std::runtime_error(oss.str());
×
778
    }
×
779
  }
551✔
780
}
492✔
781

782
void
783
LBSProblem::InitializeXSmapAndDensities(const InputParameters& params)
492✔
784
{
785
  // Build XS map
786
  const auto& xs_array = params.GetParam("xs_map");
492✔
787
  const size_t num_xs = xs_array.GetNumParameters();
492✔
788
  for (size_t i = 0; i < num_xs; ++i)
1,241✔
789
  {
790
    const auto& item_params = xs_array.GetParam(i);
749✔
791
    InputParameters xs_entry_pars = GetXSMapEntryBlock();
749✔
792
    xs_entry_pars.AssignParameters(item_params);
749✔
793

794
    const auto& block_ids_param = xs_entry_pars.GetParam("block_ids");
749✔
795
    block_ids_param.RequireBlockTypeIs(ParameterBlockType::ARRAY);
749✔
796
    const auto& block_ids = block_ids_param.GetVectorValue<unsigned int>();
749✔
797
    auto xs = xs_entry_pars.GetSharedPtrParam<MultiGroupXS>("xs");
749✔
798
    for (const auto& block_id : block_ids)
1,594✔
799
      block_id_to_xs_map_[block_id] = xs;
845✔
800
  }
749✔
801

802
  // Assign placeholder unit densities
803
  densities_local_.assign(grid_->local_cells.size(), 1.0);
492✔
804
}
492✔
805

806
void
807
LBSProblem::InitializeMaterials()
672✔
808
{
809
  CALI_CXX_MARK_SCOPE("LBSProblem::InitializeMaterials");
672✔
810

811
  log.Log0Verbose1() << "Initializing Materials";
1,344✔
812

813
  // Create set of material ids locally relevant
814
  int invalid_mat_cell_count = 0;
672✔
815
  std::set<unsigned int> unique_block_ids;
672✔
816
  for (auto& cell : grid_->local_cells)
440,164✔
817
  {
818
    unique_block_ids.insert(cell.block_id);
439,492✔
819
    if (cell.block_id == std::numeric_limits<unsigned int>::max() or
439,492✔
820
        (block_id_to_xs_map_.find(cell.block_id) == block_id_to_xs_map_.end()))
439,492✔
821
      ++invalid_mat_cell_count;
×
822
  }
823
  const auto& ghost_cell_ids = grid_->cells.GetGhostGlobalIDs();
672✔
824
  for (uint64_t cell_id : ghost_cell_ids)
93,520✔
825
  {
826
    const auto& cell = grid_->cells[cell_id];
92,848✔
827
    unique_block_ids.insert(cell.block_id);
92,848✔
828
    if (cell.block_id == std::numeric_limits<unsigned int>::max() or
92,848✔
829
        (block_id_to_xs_map_.find(cell.block_id) == block_id_to_xs_map_.end()))
92,848✔
830
      ++invalid_mat_cell_count;
×
831
  }
832
  OpenSnLogicalErrorIf(invalid_mat_cell_count > 0,
672✔
833
                       std::to_string(invalid_mat_cell_count) +
834
                         " cells encountered with an invalid material id.");
835

836
  // Get ready for processing
837
  for (const auto& [blk_id, mat] : block_id_to_xs_map_)
1,713✔
838
  {
839
    mat->SetAdjointMode(options_.adjoint);
1,041✔
840

841
    OpenSnLogicalErrorIf(mat->GetNumGroups() < groups_.size(),
1,041✔
842
                         "Cross-sections for block \"" + std::to_string(blk_id) +
843
                           "\" have fewer groups (" + std::to_string(mat->GetNumGroups()) +
844
                           ") than the simulation (" + std::to_string(groups_.size()) + "). " +
845
                           "Cross-sections must have at least as many groups as the simulation.");
846
  }
847

848
  // Initialize precursor properties
849
  num_precursors_ = 0;
672✔
850
  max_precursors_per_material_ = 0;
672✔
851
  for (const auto& mat_id_xs : block_id_to_xs_map_)
1,713✔
852
  {
853
    const auto& xs = mat_id_xs.second;
1,041✔
854
    num_precursors_ += xs->GetNumPrecursors();
1,041✔
855
    max_precursors_per_material_ = std::max(xs->GetNumPrecursors(), max_precursors_per_material_);
1,041✔
856
  }
857

858
  // if no precursors, turn off precursors
859
  if (num_precursors_ == 0)
672✔
860
    options_.use_precursors = false;
484✔
861

862
  // check compatibility when precursors are on
863
  if (options_.use_precursors)
672✔
864
  {
865
    for (const auto& [mat_id, xs] : block_id_to_xs_map_)
352✔
866
    {
867
      OpenSnLogicalErrorIf(xs->IsFissionable() and num_precursors_ == 0,
176✔
868
                           "Incompatible cross-section data encountered for material id " +
869
                             std::to_string(mat_id) + ". When delayed neutron data is present " +
870
                             "for one fissionable matrial, it must be present for all fissionable "
871
                             "materials.");
872
    }
873
  }
874

875
  // Update transport views if available
876
  if (grid_->local_cells.size() == cell_transport_views_.size())
672✔
877
    for (const auto& cell : grid_->local_cells)
25,168✔
878
    {
879
      const auto& xs_ptr = block_id_to_xs_map_[cell.block_id];
24,988✔
880
      auto& transport_view = cell_transport_views_[cell.local_id];
24,988✔
881
      transport_view.ReassignXS(*xs_ptr);
24,988✔
882
    }
883

884
  mpi_comm.barrier();
672✔
885
}
672✔
886

887
void
888
LBSProblem::InitializeSpatialDiscretization()
484✔
889
{
890
  CALI_CXX_MARK_SCOPE("LBSProblem::InitializeSpatialDiscretization");
484✔
891

892
  log.Log() << "Initializing spatial discretization.\n";
968✔
893
  discretization_ = PieceWiseLinearDiscontinuous::New(grid_);
484✔
894

895
  ComputeUnitIntegrals();
484✔
896
}
484✔
897

898
void
899
LBSProblem::ComputeUnitIntegrals()
492✔
900
{
901
  CALI_CXX_MARK_SCOPE("LBSProblem::ComputeUnitIntegrals");
492✔
902

903
  log.Log() << "Computing unit integrals.\n";
984✔
904
  const auto& sdm = *discretization_;
492✔
905

906
  const size_t num_local_cells = grid_->local_cells.size();
492✔
907
  unit_cell_matrices_.resize(num_local_cells);
492✔
908

909
  for (const auto& cell : grid_->local_cells)
414,996✔
910
    unit_cell_matrices_[cell.local_id] =
414,504✔
911
      ComputeUnitCellIntegrals(sdm, cell, grid_->GetCoordinateSystem());
414,504✔
912

913
  const auto ghost_ids = grid_->cells.GetGhostGlobalIDs();
492✔
914
  for (auto ghost_id : ghost_ids)
84,856✔
915
    unit_ghost_cell_matrices_[ghost_id] =
84,364✔
916
      ComputeUnitCellIntegrals(sdm, grid_->cells[ghost_id], grid_->GetCoordinateSystem());
168,728✔
917

918
  // Assessing global unit cell matrix storage
919
  std::array<size_t, 2> num_local_ucms = {unit_cell_matrices_.size(),
492✔
920
                                          unit_ghost_cell_matrices_.size()};
492✔
921
  std::array<size_t, 2> num_global_ucms = {0, 0};
492✔
922

923
  mpi_comm.all_reduce(num_local_ucms.data(), 2, num_global_ucms.data(), mpi::op::sum<size_t>());
492✔
924

925
  opensn::mpi_comm.barrier();
492✔
926
  log.Log() << "Ghost cell unit cell-matrix ratio: "
492✔
927
            << (double)num_global_ucms[1] * 100 / (double)num_global_ucms[0] << "%";
984✔
928
  log.Log() << "Cell matrices computed.";
984✔
929
}
492✔
930

931
void
932
LBSProblem::InitializeParrays()
492✔
933
{
934
  CALI_CXX_MARK_SCOPE("LBSProblem::InitializeParrays");
492✔
935

936
  log.Log() << "Initializing parallel arrays."
984✔
937
            << " G=" << num_groups_ << " M=" << num_moments_ << std::endl;
492✔
938

939
  // Initialize unknown
940
  // structure
941
  flux_moments_uk_man_.unknowns.clear();
492✔
942
  for (size_t m = 0; m < num_moments_; ++m)
1,558✔
943
  {
944
    flux_moments_uk_man_.AddUnknown(UnknownType::VECTOR_N, groups_.size());
1,066✔
945
    flux_moments_uk_man_.unknowns.back().name = "m" + std::to_string(m);
1,066✔
946
  }
947

948
  // Compute local # of dof
949
  local_node_count_ = discretization_->GetNumLocalNodes();
492✔
950
  global_node_count_ = discretization_->GetNumGlobalNodes();
492✔
951

952
  // Compute num of unknowns
953
  size_t num_grps = groups_.size();
492✔
954
  size_t local_unknown_count = local_node_count_ * num_grps * num_moments_;
492✔
955

956
  log.LogAllVerbose1() << "LBS Number of phi unknowns: " << local_unknown_count;
984✔
957

958
  // Size local vectors
959
  q_moments_local_.assign(local_unknown_count, 0.0);
492✔
960
  phi_old_local_.assign(local_unknown_count, 0.0);
492✔
961
  phi_new_local_.assign(local_unknown_count, 0.0);
492✔
962

963
  // Setup precursor vector
964
  if (options_.use_precursors)
492✔
965
  {
966
    size_t num_precursor_dofs = grid_->local_cells.size() * max_precursors_per_material_;
60✔
967
    precursor_new_local_.assign(num_precursor_dofs, 0.0);
60✔
968
  }
969

970
  // Initialize transport views
971
  // Transport views act as a data structure to store information
972
  // related to the transport simulation. The most prominent function
973
  // here is that it holds the means to know where a given cell's
974
  // transport quantities are located in the unknown vectors (i.e. phi)
975
  //
976
  // Also, for a given cell, within a given sweep chunk,
977
  // we need to solve a matrix which square size is the
978
  // amount of nodes on the cell. max_cell_dof_count is
979
  // initialized here.
980
  //
981
  size_t block_MG_counter = 0; // Counts the strides of moment and group
492✔
982

983
  const Vector3 ihat(1.0, 0.0, 0.0);
492✔
984
  const Vector3 jhat(0.0, 1.0, 0.0);
492✔
985
  const Vector3 khat(0.0, 0.0, 1.0);
492✔
986

987
  min_cell_dof_count_ = std::numeric_limits<unsigned int>::max();
492✔
988
  max_cell_dof_count_ = 0;
492✔
989
  cell_transport_views_.clear();
492✔
990
  cell_transport_views_.reserve(grid_->local_cells.size());
492✔
991
  for (auto& cell : grid_->local_cells)
414,996✔
992
  {
993
    size_t num_nodes = discretization_->GetCellNumNodes(cell);
414,504✔
994

995
    // compute cell volumes
996
    double cell_volume = 0.0;
414,504✔
997
    const auto& IntV_shapeI = unit_cell_matrices_[cell.local_id].intV_shapeI;
414,504✔
998
    for (size_t i = 0; i < num_nodes; ++i)
3,189,176✔
999
      cell_volume += IntV_shapeI(i);
2,774,672✔
1000

1001
    size_t cell_phi_address = block_MG_counter;
414,504✔
1002

1003
    const size_t num_faces = cell.faces.size();
414,504✔
1004
    std::vector<bool> face_local_flags(num_faces, true);
414,504✔
1005
    std::vector<int> face_locality(num_faces, opensn::mpi_comm.rank());
414,504✔
1006
    std::vector<const Cell*> neighbor_cell_ptrs(num_faces, nullptr);
414,504✔
1007
    bool cell_on_boundary = false;
414,504✔
1008
    int f = 0;
414,504✔
1009
    for (auto& face : cell.faces)
2,621,696✔
1010
    {
1011
      if (not face.has_neighbor)
2,207,192✔
1012
      {
1013
        cell_on_boundary = true;
87,072✔
1014
        face_local_flags[f] = false;
87,072✔
1015
        face_locality[f] = -1;
87,072✔
1016
      } // if bndry
1017
      else
1018
      {
1019
        const int neighbor_partition = face.GetNeighborPartitionID(grid_.get());
2,120,120✔
1020
        face_local_flags[f] = (neighbor_partition == opensn::mpi_comm.rank());
2,120,120✔
1021
        face_locality[f] = neighbor_partition;
2,120,120✔
1022
        neighbor_cell_ptrs[f] = &grid_->cells[face.neighbor_id];
2,120,120✔
1023
      }
1024

1025
      ++f;
2,207,192✔
1026
    } // for f
1027

1028
    max_cell_dof_count_ = std::max(max_cell_dof_count_, static_cast<unsigned int>(num_nodes));
414,504✔
1029
    min_cell_dof_count_ = std::min(min_cell_dof_count_, static_cast<unsigned int>(num_nodes));
414,504✔
1030
    cell_transport_views_.emplace_back(cell_phi_address,
829,008✔
1031
                                       num_nodes,
1032
                                       num_grps,
1033
                                       num_moments_,
414,504✔
1034
                                       num_faces,
1035
                                       *block_id_to_xs_map_[cell.block_id],
414,504✔
1036
                                       cell_volume,
1037
                                       face_local_flags,
1038
                                       face_locality,
1039
                                       neighbor_cell_ptrs,
1040
                                       cell_on_boundary);
1041
    block_MG_counter += num_nodes * num_grps * num_moments_;
414,504✔
1042
  } // for local cell
414,504✔
1043

1044
  // Populate grid nodal mappings
1045
  // This is used in the Flux Data Structures (FLUDS)
1046
  grid_nodal_mappings_.clear();
492✔
1047
  grid_nodal_mappings_.reserve(grid_->local_cells.size());
492✔
1048
  for (auto& cell : grid_->local_cells)
414,996✔
1049
  {
1050
    CellFaceNodalMapping cell_nodal_mapping;
414,504✔
1051
    cell_nodal_mapping.reserve(cell.faces.size());
414,504✔
1052

1053
    for (auto& face : cell.faces)
2,621,696✔
1054
    {
1055
      std::vector<short> face_node_mapping;
2,207,192✔
1056
      std::vector<short> cell_node_mapping;
2,207,192✔
1057
      int adj_face_idx = -1;
2,207,192✔
1058

1059
      if (face.has_neighbor)
2,207,192✔
1060
      {
1061
        grid_->FindAssociatedVertices(face, face_node_mapping);
2,120,120✔
1062
        grid_->FindAssociatedCellVertices(face, cell_node_mapping);
2,120,120✔
1063
        adj_face_idx = face.GetNeighborAdjacentFaceIndex(grid_.get());
2,120,120✔
1064
      }
1065

1066
      cell_nodal_mapping.emplace_back(adj_face_idx, face_node_mapping, cell_node_mapping);
2,207,192✔
1067
    } // for f
2,207,192✔
1068

1069
    grid_nodal_mappings_.push_back(cell_nodal_mapping);
414,504✔
1070
  } // for local cell
414,504✔
1071

1072
  // Get grid localized communicator set
1073
  grid_local_comm_set_ = grid_->MakeMPILocalCommunicatorSet();
492✔
1074

1075
  // Initialize Field Functions
1076
  InitializeFieldFunctions();
492✔
1077

1078
  opensn::mpi_comm.barrier();
492✔
1079
  log.Log() << "Done with parallel arrays." << std::endl;
984✔
1080
}
492✔
1081

1082
void
1083
LBSProblem::InitializeFieldFunctions()
492✔
1084
{
1085
  CALI_CXX_MARK_SCOPE("LBSProblem::InitializeFieldFunctions");
492✔
1086

1087
  if (not field_functions_.empty())
492✔
1088
    return;
×
1089

1090
  // Initialize Field Functions for flux moments
1091
  phi_field_functions_local_map_.clear();
492✔
1092

1093
  for (size_t g = 0; g < groups_.size(); ++g)
21,857✔
1094
  {
1095
    for (size_t m = 0; m < num_moments_; ++m)
98,048✔
1096
    {
1097
      std::string prefix;
76,683✔
1098
      if (options_.field_function_prefix_option == "prefix")
76,683✔
1099
      {
1100
        prefix = options_.field_function_prefix;
76,683✔
1101
        if (not prefix.empty())
76,683✔
1102
          prefix += "_";
1✔
1103
      }
1104
      if (options_.field_function_prefix_option == "solver_name")
76,683✔
1105
        prefix = GetName() + "_";
×
1106

1107
      std::ostringstream oss;
76,683✔
1108
      oss << prefix << "phi_g" << std::setw(3) << std::setfill('0') << static_cast<int>(g) << "_m"
76,683✔
1109
          << std::setw(2) << std::setfill('0') << static_cast<int>(m);
76,683✔
1110
      const std::string name = oss.str();
76,683✔
1111

1112
      auto group_ff = std::make_shared<FieldFunctionGridBased>(
76,683✔
1113
        name, discretization_, Unknown(UnknownType::SCALAR));
76,683✔
1114

1115
      field_function_stack.push_back(group_ff);
153,366✔
1116
      field_functions_.push_back(group_ff);
76,683✔
1117

1118
      phi_field_functions_local_map_[{g, m}] = field_functions_.size() - 1;
76,683✔
1119
    } // for m
76,683✔
1120
  } // for g
1121

1122
  // Initialize power generation field function
1123
  if (options_.power_field_function_on)
492✔
1124
  {
1125
    std::string prefix;
4✔
1126
    if (options_.field_function_prefix_option == "prefix")
4✔
1127
    {
1128
      prefix = options_.field_function_prefix;
4✔
1129
      if (not prefix.empty())
4✔
1130
        prefix += "_";
×
1131
    }
1132
    if (options_.field_function_prefix_option == "solver_name")
4✔
1133
      prefix = GetName() + "_";
×
1134

1135
    auto power_ff = std::make_shared<FieldFunctionGridBased>(
4✔
1136
      prefix + "power_generation", discretization_, Unknown(UnknownType::SCALAR));
8✔
1137

1138
    field_function_stack.push_back(power_ff);
8✔
1139
    field_functions_.push_back(power_ff);
4✔
1140

1141
    power_gen_fieldfunc_local_handle_ = field_functions_.size() - 1;
4✔
1142
  }
4✔
1143
}
492✔
1144

1145
void
1146
LBSProblem::InitializeSolverSchemes()
612✔
1147
{
1148
  CALI_CXX_MARK_SCOPE("LBSProblem::InitializeSolverSchemes");
612✔
1149

1150
  log.Log() << "Initializing WGS and AGS solvers";
1,224✔
1151

1152
  InitializeWGSSolvers();
612✔
1153

1154
  ags_solver_ = std::make_shared<AGSLinearSolver>(*this, wgs_solvers_);
612✔
1155
  if (groupsets_.size() == 1)
612✔
1156
  {
1157
    ags_solver_->SetMaxIterations(1);
557✔
1158
    ags_solver_->SetVerbosity(false);
557✔
1159
  }
1160
  else
1161
  {
1162
    ags_solver_->SetMaxIterations(options_.max_ags_iterations);
55✔
1163
    ags_solver_->SetVerbosity(options_.verbose_ags_iterations);
55✔
1164
  }
1165
  ags_solver_->SetTolerance(options_.ags_tolerance);
612✔
1166
}
612✔
1167

1168
#ifndef __OPENSN_WITH_GPU__
1169
void
1170
LBSProblem::InitializeGPUExtras()
660✔
1171
{
1172
}
660✔
1173

1174
void
1175
LBSProblem::ResetGPUCarriers()
652✔
1176
{
1177
}
652✔
1178

1179
void
1180
LBSProblem::CheckCapableDevices()
×
1181
{
1182
}
×
1183
#endif // __OPENSN_WITH_GPU__
1184

1185
std::vector<double>
1186
LBSProblem::MakeSourceMomentsFromPhi()
4✔
1187
{
1188
  CALI_CXX_MARK_SCOPE("LBSProblem::MakeSourceMomentsFromPhi");
4✔
1189

1190
  size_t num_local_dofs = discretization_->GetNumLocalDOFs(flux_moments_uk_man_);
4✔
1191

1192
  std::vector<double> source_moments(num_local_dofs, 0.0);
4✔
1193
  for (auto& groupset : groupsets_)
8✔
1194
  {
1195
    active_set_source_function_(groupset,
4✔
1196
                                source_moments,
1197
                                phi_new_local_,
4✔
1198
                                APPLY_AGS_SCATTER_SOURCES | APPLY_WGS_SCATTER_SOURCES |
1199
                                  APPLY_AGS_FISSION_SOURCES | APPLY_WGS_FISSION_SOURCES);
4✔
1200
  }
1201

1202
  return source_moments;
4✔
1203
}
4✔
1204

1205
void
1206
LBSProblem::UpdateFieldFunctions()
2,616✔
1207
{
1208
  CALI_CXX_MARK_SCOPE("LBSProblem::UpdateFieldFunctions");
2,616✔
1209

1210
  const auto& sdm = *discretization_;
2,616✔
1211
  const auto& phi_uk_man = flux_moments_uk_man_;
2,616✔
1212

1213
  // Update flux moments
1214
  for (const auto& [g_and_m, ff_index] : phi_field_functions_local_map_)
86,403✔
1215
  {
1216
    const size_t g = g_and_m.first;
83,787✔
1217
    const size_t m = g_and_m.second;
83,787✔
1218

1219
    std::vector<double> data_vector_local(local_node_count_, 0.0);
83,787✔
1220

1221
    for (const auto& cell : grid_->local_cells)
22,748,304✔
1222
    {
1223
      const auto& cell_mapping = sdm.GetCellMapping(cell);
22,664,517✔
1224
      const size_t num_nodes = cell_mapping.GetNumNodes();
22,664,517✔
1225

1226
      for (size_t i = 0; i < num_nodes; ++i)
156,200,325✔
1227
      {
1228
        const auto imapA = sdm.MapDOFLocal(cell, i, phi_uk_man, m, g);
133,535,808✔
1229
        const auto imapB = sdm.MapDOFLocal(cell, i);
133,535,808✔
1230

1231
        data_vector_local[imapB] = phi_new_local_[imapA];
133,535,808✔
1232
      } // for node
1233
    } // for cell
1234

1235
    auto& ff_ptr = field_functions_.at(ff_index);
83,787✔
1236
    ff_ptr->UpdateFieldVector(data_vector_local);
83,787✔
1237
  }
83,787✔
1238

1239
  // Update power generation and scalar flux
1240
  if (options_.power_field_function_on)
2,616✔
1241
  {
1242
    std::vector<double> data_vector_power_local(local_node_count_, 0.0);
4✔
1243

1244
    double local_total_power = 0.0;
4✔
1245
    for (const auto& cell : grid_->local_cells)
83,268✔
1246
    {
1247
      const auto& cell_mapping = sdm.GetCellMapping(cell);
83,264✔
1248
      const size_t num_nodes = cell_mapping.GetNumNodes();
83,264✔
1249

1250
      const auto& Vi = unit_cell_matrices_[cell.local_id].intV_shapeI;
83,264✔
1251

1252
      const auto& xs = block_id_to_xs_map_.at(cell.block_id);
83,264✔
1253

1254
      if (not xs->IsFissionable())
83,264✔
1255
        continue;
56,360✔
1256

1257
      for (size_t i = 0; i < num_nodes; ++i)
134,520✔
1258
      {
1259
        const auto imapA = sdm.MapDOFLocal(cell, i);
107,616✔
1260
        const auto imapB = sdm.MapDOFLocal(cell, i, phi_uk_man, 0, 0);
107,616✔
1261

1262
        double nodal_power = 0.0;
1263
        for (size_t g = 0; g < groups_.size(); ++g)
860,928✔
1264
        {
1265
          const double sigma_fg = xs->GetSigmaFission()[g];
753,312✔
1266
          // const double kappa_g = xs->Kappa()[g];
1267
          const double kappa_g = options_.power_default_kappa;
753,312✔
1268

1269
          nodal_power += kappa_g * sigma_fg * phi_new_local_[imapB + g];
753,312✔
1270
        } // for g
1271

1272
        data_vector_power_local[imapA] = nodal_power;
107,616✔
1273
        local_total_power += nodal_power * Vi(i);
107,616✔
1274
      } // for node
1275
    } // for cell
1276

1277
    double scale_factor = 1.0;
4✔
1278
    if (options_.power_normalization > 0.0)
4✔
1279
    {
1280
      double global_total_power = 0.0;
4✔
1281
      mpi_comm.all_reduce(local_total_power, global_total_power, mpi::op::sum<double>());
4✔
1282
      scale_factor = options_.power_normalization / global_total_power;
4✔
1283
      Scale(data_vector_power_local, scale_factor);
4✔
1284
    }
1285

1286
    const size_t ff_index = power_gen_fieldfunc_local_handle_;
4✔
1287

1288
    auto& ff_ptr = field_functions_.at(ff_index);
4✔
1289
    ff_ptr->UpdateFieldVector(data_vector_power_local);
4✔
1290

1291
    // scale scalar flux if neccessary
1292
    if (scale_factor != 1.0)
4✔
1293
    {
1294
      for (size_t g = 0; g < groups_.size(); ++g)
32✔
1295
      {
1296
        const size_t phi_ff_index = phi_field_functions_local_map_.at({g, size_t{0}});
28✔
1297
        auto& phi_ff_ptr = field_functions_.at(phi_ff_index);
28✔
1298
        const auto& phi_vec = phi_ff_ptr->GetLocalFieldVector();
28✔
1299
        std::vector<double> phi_scaled(phi_vec.begin(), phi_vec.end());
28✔
1300
        Scale(phi_scaled, scale_factor);
28✔
1301
        phi_ff_ptr->UpdateFieldVector(phi_scaled);
28✔
1302
      }
28✔
1303
    }
1304
  } // if power enabled
4✔
1305
}
2,616✔
1306

1307
void
1308
LBSProblem::SetPhiFromFieldFunctions(PhiSTLOption which_phi,
×
1309
                                     const std::vector<size_t>& m_indices,
1310
                                     const std::vector<size_t>& g_indices)
1311
{
1312
  CALI_CXX_MARK_SCOPE("LBSProblem::SetPhiFromFieldFunctions");
×
1313

1314
  std::vector<size_t> m_ids_to_copy = m_indices;
×
1315
  std::vector<size_t> g_ids_to_copy = g_indices;
×
1316
  if (m_indices.empty())
×
1317
    for (size_t m = 0; m < num_moments_; ++m)
×
1318
      m_ids_to_copy.push_back(m);
×
1319
  if (g_ids_to_copy.empty())
×
1320
    for (size_t g = 0; g < num_groups_; ++g)
×
1321
      g_ids_to_copy.push_back(g);
×
1322

1323
  const auto& sdm = *discretization_;
×
1324
  const auto& phi_uk_man = flux_moments_uk_man_;
×
1325

1326
  for (const size_t m : m_ids_to_copy)
×
1327
  {
1328
    for (const size_t g : g_ids_to_copy)
×
1329
    {
1330
      const size_t ff_index = phi_field_functions_local_map_.at({g, m});
×
1331
      const auto& ff_ptr = field_functions_.at(ff_index);
×
1332
      const auto& ff_data = ff_ptr->GetLocalFieldVector();
×
1333

1334
      for (const auto& cell : grid_->local_cells)
×
1335
      {
1336
        const auto& cell_mapping = sdm.GetCellMapping(cell);
×
1337
        const size_t num_nodes = cell_mapping.GetNumNodes();
×
1338

1339
        for (size_t i = 0; i < num_nodes; ++i)
×
1340
        {
1341
          const auto imapA = sdm.MapDOFLocal(cell, i);
×
1342
          const auto imapB = sdm.MapDOFLocal(cell, i, phi_uk_man, m, g);
×
1343

1344
          if (which_phi == PhiSTLOption::PHI_OLD)
×
1345
            phi_old_local_[imapB] = ff_data[imapA];
×
1346
          else if (which_phi == PhiSTLOption::PHI_NEW)
×
1347
            phi_new_local_[imapB] = ff_data[imapA];
×
1348
        } // for node
1349
      } // for cell
1350
    } // for g
1351
  } // for m
1352
}
×
1353

1354
LBSProblem::~LBSProblem()
484✔
1355
{
1356
  ResetGPUCarriers();
1357
}
2,420✔
1358

484✔
1359
void
1360
LBSProblem::SetAdjoint(bool adjoint)
504✔
1361
{
1362
  if (adjoint != options_.adjoint)
504✔
1363
  {
1364
    options_.adjoint = adjoint;
12✔
1365

1366
    // If a discretization exists, the solver has already been initialized.
1367
    // Reinitialize the materials to obtain the appropriate xs and clear the
1368
    // sources to prepare for defining the adjoint problem
1369
    if (discretization_)
12✔
1370
    {
1371
      // The materials are reinitialized here to ensure that the proper cross sections
1372
      // are available to the solver. Because an adjoint solve requires volumetric or
1373
      // point sources, the material-based sources are not set within the initialize routine.
1374
      InitializeMaterials();
12✔
1375

1376
      // Forward and adjoint sources are fundamentally different, so any existing sources
1377
      // should be cleared and reset through options upon changing modes.
1378
      point_sources_.clear();
12✔
1379
      volumetric_sources_.clear();
12✔
1380
      ClearBoundaries();
12✔
1381

1382
      // Set all solutions to zero.
1383
      phi_old_local_.assign(phi_old_local_.size(), 0.0);
12✔
1384
      phi_new_local_.assign(phi_new_local_.size(), 0.0);
12✔
1385
      ZeroSolutions();
12✔
1386
      precursor_new_local_.assign(precursor_new_local_.size(), 0.0);
12✔
1387
    }
1388
  }
1389
}
504✔
1390

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