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

Open-Sn / opensn / 20388244010

20 Dec 2025 12:27AM UTC coverage: 74.437% (+0.02%) from 74.415%
20388244010

push

github

web-flow
Merge pull request #882 from andrsd/issue/90-local-cell-idxs

Unifying integer types for local cell face/side indices

13 of 15 new or added lines in 2 files covered. (86.67%)

43 existing lines in 8 files now uncovered.

18677 of 25091 relevant lines covered (74.44%)

66914505.04 hits per line

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

79.64
/python/lib/solver.cc
1
// SPDX-FileCopyrightText: 2025 The OpenSn Authors <https://open-sn.github.io/opensn/>
2
// SPDX-License-Identifier: MIT
3

4
#include "python/lib/py_wrappers.h"
5
#include "framework/runtime.h"
6
#include "framework/field_functions/field_function_grid_based.h"
7
#include "modules/linear_boltzmann_solvers/discrete_ordinates_problem/acceleration/discrete_ordinates_keigen_acceleration.h"
8
#include "modules/linear_boltzmann_solvers/discrete_ordinates_problem/acceleration/scdsa_acceleration.h"
9
#include "modules/linear_boltzmann_solvers/discrete_ordinates_problem/acceleration/smm_acceleration.h"
10
#include "modules/linear_boltzmann_solvers/discrete_ordinates_curvilinear_problem/discrete_ordinates_curvilinear_problem.h"
11
#include "modules/linear_boltzmann_solvers/discrete_ordinates_problem/discrete_ordinates_problem.h"
12
#include "modules/linear_boltzmann_solvers/solvers/time_dependent_solver.h"
13
#include "modules/linear_boltzmann_solvers/solvers/steady_state_solver.h"
14
#include "modules/linear_boltzmann_solvers/solvers/nl_keigen_solver.h"
15
#include "modules/linear_boltzmann_solvers/solvers/pi_keigen_solver.h"
16
#include "modules/linear_boltzmann_solvers/lbs_problem/io/lbs_problem_io.h"
17
#include "modules/linear_boltzmann_solvers/lbs_problem/lbs_problem.h"
18
#include "modules/linear_boltzmann_solvers/lbs_problem/lbs_compute.h"
19
#include "modules/solver.h"
20
#include <pybind11/numpy.h>
21
#include <algorithm>
22
#include <cstddef>
23
#include <cstdint>
24
#include <map>
25
#include <memory>
26
#include <string>
27
#include <vector>
28

29
namespace opensn
30
{
31

32
// Wrap problem
33
void
34
WrapProblem(py::module& slv)
483✔
35
{
36
  // clang-format off
37
  // problem base
38
  auto problem = py::class_<Problem, std::shared_ptr<Problem>>(
483✔
39
    slv,
40
    "Problem",
41
    R"(
42
    Base class for all problems.
43

44
    Wrapper of :cpp:class:`opensn::Problem`.
45
    )"
46
  );
483✔
47
  problem.def(
483✔
48
    "GetFieldFunctions",
49
    [](Problem& self)
483✔
50
    {
51
      py::list ff_list;
×
52
      std::vector<std::shared_ptr<FieldFunctionGridBased>>& cpp_ff_list = self.GetFieldFunctions();
×
53
      for (std::shared_ptr<FieldFunctionGridBased>& ff : cpp_ff_list) {
×
54
        ff_list.append(ff);
×
55
      }
56
      return ff_list;
×
57
    },
×
58
    R"(
59
    Get the list of field functions.
60

61
    Returns
62
    -------
63
    List[pyopensn.fieldfunc.FieldFunctionGridBased]
64
        List of grid-based field functions representing solution data such as scalar fluxes.
65
    )"
66
  );
67
  // clang-format on
68
}
483✔
69

70
// Wrap solver
71
void
72
WrapSolver(py::module& slv)
483✔
73
{
74
  // clang-format off
75
  // solver base
76
  auto solver = py::class_<Solver, std::shared_ptr<Solver> >(
483✔
77
    slv,
78
    "Solver",
79
    R"(
80
    Base class for all solvers.
81

82
    Wrapper of :cpp:class:`opensn::Solver`.
83
    )"
84
  );
483✔
85
  solver.def(
483✔
86
    "Initialize",
87
    &Solver::Initialize,
483✔
88
    "Initialize the solver."
89
  );
90
  solver.def(
483✔
91
    "Execute",
92
    &Solver::Execute,
483✔
93
    "Execute the solver."
94
  );
95
  solver.def(
483✔
96
    "Advance",
97
    &Solver::Advance,
483✔
98
    "Advance time values function."
99
  );
100
  // clang-format on
101
}
483✔
102

103
// Wrap LBS solver
104
void
105
WrapLBS(py::module& slv)
483✔
106
{
107
  // clang-format off
108
  // LBS problem
109
  auto lbs_problem = py::class_<LBSProblem, std::shared_ptr<LBSProblem>, Problem>(
483✔
110
    slv,
111
    "LBSProblem",
112
    R"(
113
    Base class for all linear Boltzmann problems.
114

115
    Wrapper of :cpp:class:`opensn::LBSProblem`.
116
    )"
117
  );
483✔
118
  lbs_problem.def(
483✔
119
    "GetScalarFieldFunctionList",
120
    [](LBSProblem& self, bool only_scalar_flux)
483✔
121
    {
122
      py::list field_function_list_per_group;
259✔
123
      for (std::size_t group = 0; group < self.GetNumGroups(); group++)
16,975✔
124
      {
125
        if (only_scalar_flux)
16,716✔
126
        {
127
          std::size_t ff_index = self.MapPhiFieldFunction(group, 0);
11,048✔
128
          field_function_list_per_group.append(self.GetFieldFunctions()[ff_index]);
11,048✔
129
        }
130
        else
131
        {
132
          py::list field_function_list_per_moment;
5,668✔
133
          for (std::size_t moment = 0; moment < self.GetNumMoments(); moment++)
22,256✔
134
          {
135
            std::size_t ff_index = self.MapPhiFieldFunction(group, moment);
16,588✔
136
            field_function_list_per_moment.append(self.GetFieldFunctions()[ff_index]);
16,588✔
137
          }
138
          field_function_list_per_group.append(field_function_list_per_moment);
5,668✔
139
        }
5,668✔
140
      }
141
      return field_function_list_per_group;
259✔
142
    },
×
143
    R"(
144
    Return field functions grouped by energy group and, optionally, by moment.
145

146
    Parameters
147
    ----------
148
    only_scalar_flux : bool, default=True
149
        If True, returns only the zeroth moment (scalar flux) field function for each group.
150
        The result is a flat list of field functions, one per group.
151

152
        If False, returns all moment field functions for each group.
153
        The result is a nested list where each entry corresponds to a group and contains
154
        a list of field functions for all moments (e.g., scalar flux, higher-order moments).
155

156
    Returns
157
    -------
158
    Union[List[pyopensn.fieldfunc.FieldFunctionGridBased], List[List[pyopensn.fieldfunc.FieldFunctionGridBased]]]
159
        The structure of the returned list depends on the `only_scalar_flux` flag.
160

161
    Notes
162
    -----
163
    The moment index varies more rapidly than the group index when `only_scalar_flux` is False.
164
    )",
165
    py::arg("only_scalar_flux") = true
483✔
166
  );
167
  lbs_problem.def(
483✔
168
    "GetPowerFieldFunction",
169
    &LBSProblem::GetPowerFieldFunction,
483✔
170
    R"(
171
    Returns the power generation field function, if enabled.
172
    )"
173
  );
174
  lbs_problem.def(
483✔
175
    "SetOptions",
176
    [](LBSProblem& self, py::kwargs& params)
483✔
177
    {
178
      InputParameters input = LBSProblem::GetOptionsBlock();
×
179
      input.AssignParameters(kwargs_to_param_block(params));
×
180
      self.SetOptions(input);
×
181
    },
×
182
    R"(
183
    Set problem options from a large list of parameters.
184

185
    Parameters
186
    ----------
187
    max_mpi_message_size: int default=32768
188
        The maximum MPI message size used during sweeps.
189
    restart_writes_enabled: bool, default=False
190
        Flag that controls writing of restart dumps.
191
    write_delayed_psi_to_restart: bool, default=True
192
        Flag that controls writing of delayed angular fluxes to restarts.
193
    read_restart_path: str, default=''
194
        Full path for reading restart dumps including file basename.
195
    write_restart_path: str, default=''
196
        Full path for writing restart dumps including file basename.
197
    write_restart_time_interval: int, default=0
198
        Time interval in seconds at which restart data is to be written.
199
    use_precursors: bool, default=False
200
        Flag for using delayed neutron precursors.
201
    use_source_moments: bool, default=False
202
        Flag for ignoring fixed sources and selectively using source moments obtained elsewhere.
203
    save_angular_flux: bool, default=False
204
        Flag indicating whether angular fluxes are to be stored or not.
205
    verbose_inner_iterations: bool, default=True
206
        Flag to control verbosity of inner iterations.
207
    verbose_outer_iterations: bool, default=True
208
        Flag to control verbosity of across-groupset iterations.
209
    max_ags_iterations: int, default=100
210
        Maximum number of across-groupset iterations.
211
    ags_tolerance: float, default=1.0e-6
212
        Across-groupset iterations tolerance.
213
    ags_convergence_check: {'l2', 'pointwise'}, default='l2'
214
        Type of convergence check for AGS iterations.
215
    verbose_ags_iterations: bool, default=True
216
        Flag to control verbosity of across-groupset iterations.
217
    power_field_function_on: bool, default=False
218
        Flag to control the creation of the power generation field function. If set to ``True``, a
219
        field function will be created with the general name ``<solver_name>_power_generation``.
220
    power_default_kappa: float, default=3.20435e-11
221
        Default ``kappa`` value (Energy released per fission) to use for power generation when cross
222
        sections do not have ``kappa`` values. Default corresponds to 200 MeV per fission.
223
    power_normalization: float, default=-1.0
224
        Power normalization factor to use. Supply a negative or zero number to turn this off.
225
    field_function_prefix_option: {'prefix', 'solver_name'}, default='prefix'
226
        Prefix option on field function names. If unset, flux field functions will be exported as
227
        ``phi_gXXX_mYYY``, where ``XXX`` is the zero-padded 3-digit group number and ``YYY`` is the
228
        zero-padded 3-digit moment.
229
    field_function_prefix: str, default=''
230
        Prefix to use on all field functions. By default, this is empty. If specified, flux moments
231
        are exported as ``prefix_phi_gXXX_mYYY``.
232
    )"
233
  );
234
  lbs_problem.def(
483✔
235
    "ComputeFissionRate",
236
    [](LBSProblem& self, const std::string& scalar_flux_iterate)
483✔
237
    {
238
      const std::vector<double>* phi_ptr = nullptr;
×
239
      if (scalar_flux_iterate == "old")
×
240
      {
241
        phi_ptr = &self.GetPhiOldLocal();
×
242
      }
243
      else if (scalar_flux_iterate == "new")
×
244
      {
245
        phi_ptr = &self.GetPhiNewLocal();
×
246
      }
247
      else
248
      {
249
        throw std::invalid_argument("Unknown scalar_flux_iterate value: \"" + scalar_flux_iterate + "\".");
×
250
      }
251
      return ComputeFissionRate(self, *phi_ptr);
×
252
    },
253
    R"(
254
    Computes the total fission rate.
255

256
    Parameters
257
    ----------
258
    scalar_flux_iterate : {'old', 'new'}
259
        Specifies which scalar flux vector to use in the calculation.
260
            - 'old': Use the previous scalar flux iterate.
261
            - 'new': Use the current scalar flux iterate.
262

263
    Returns
264
    -------
265
    float
266
        The total fission rate.
267

268
    Raises
269
    ------
270
    ValueError
271
        If `scalar_flux_iterate` is not 'old' or 'new'.
272
    )",
273
    py::arg("scalar_flux_iterate")
483✔
274
  );
275
  lbs_problem.def(
483✔
276
    "WriteFluxMoments",
277
    [](LBSProblem& self, const std::string& file_base)
515✔
278
    {
279
      LBSSolverIO::WriteFluxMoments(self, file_base);
32✔
280
    },
281
    R"(
282
    Write flux moments to file.
283

284
    Parameters
285
    ----------
286
    file_base: str
287
        File basename.
288
    )",
289
    py::arg("file_base")
483✔
290
  );
291
  lbs_problem.def(
483✔
292
    "CreateAndWriteSourceMoments",
293
    [](LBSProblem& self, const std::string& file_base)
487✔
294
    {
295
      std::vector<double> source_moments = self.MakeSourceMomentsFromPhi();
4✔
296
      LBSSolverIO::WriteFluxMoments(self, file_base, source_moments);
4✔
297
    },
4✔
298
    R"(
299
    Write source moments from latest flux iterate to file.
300

301
    Parameters
302
    ----------
303
    file_base: str
304
        File basename.
305
    )",
306
    py::arg("file_base")
483✔
307
  );
308
  lbs_problem.def(
483✔
309
    "ReadFluxMomentsAndMakeSourceMoments",
310
    [](LBSProblem& self, const std::string& file_base, bool single_file_flag)
483✔
311
    {
312
      LBSSolverIO::ReadFluxMoments(self, file_base, single_file_flag, self.GetExtSrcMomentsLocal());
×
313
      log.Log() << "Making source moments from flux file.";
×
314
      std::vector<double>& temp_phi = self.GetPhiOldLocal();
×
315
      self.GetPhiOldLocal() = self.GetExtSrcMomentsLocal();
×
316
      self.GetExtSrcMomentsLocal() = self.MakeSourceMomentsFromPhi();
×
317
      self.GetPhiOldLocal() = temp_phi;
×
318
    },
×
319
    R"(
320
    Read flux moments and compute corresponding source moments.
321

322
    Parameters
323
    ----------
324
    file_base: str
325
        File basename.
326
    single_file_flag: bool
327
        True if all flux moments are in a single file.
328
    )",
329
    py::arg("file_base"),
966✔
330
    py::arg("single_file_flag")
483✔
331
  );
332
  lbs_problem.def(
483✔
333
    "ReadSourceMoments",
334
    [](LBSProblem& self, const std::string& file_base, bool single_file_flag)
487✔
335
    {
336
      LBSSolverIO::ReadFluxMoments(self, file_base, single_file_flag, self.GetExtSrcMomentsLocal());
4✔
337
    },
4✔
338
    R"(
339
    Read source moments from file.
340

341
    Parameters
342
    ----------
343
    file_base: str
344
        File basename.
345
    single_file_flag: bool
346
        True if all source moments are in a single file.
347
    )",
348
    py::arg("file_base"),
966✔
349
    py::arg("single_file_flag")
483✔
350
  );
351
  lbs_problem.def(
483✔
352
    "ReadFluxMoments",
353
    [](LBSProblem& self, const std::string& file_base, bool single_file_flag)
483✔
354
    {
355
      LBSSolverIO::ReadFluxMoments(self, file_base, single_file_flag);
×
356
    },
357
    R"(
358
    Read flux moment data.
359

360
    Parameters
361
    ----------
362
    file_base: str
363
        File basename.
364
    single_file_flag: bool
365
        True if all flux moments are in a single file.
366
    )",
367
    py::arg("file_base"),
966✔
368
    py::arg("single_file_flag")
483✔
369
  );
370
  lbs_problem.def(
483✔
371
    "WriteAngularFluxes",
372
    [](DiscreteOrdinatesProblem& self, const std::string& file_base)
487✔
373
    {
374
      LBSSolverIO::WriteAngularFluxes(self, file_base);
4✔
375
    },
376
    R"(
377
    Write angular flux data to file.
378

379
    Parameters
380
    ----------
381
    file_base: str
382
        File basename.
383
    )",
384
    py::arg("file_base")
483✔
385
  );
386
  lbs_problem.def(
483✔
387
    "ReadAngularFluxes",
388
    [](DiscreteOrdinatesProblem& self, const std::string& file_base)
487✔
389
    {
390
      LBSSolverIO::ReadAngularFluxes(self, file_base);
4✔
391
    },
392
    R"(
393
    Read angular fluxes from file.
394

395
    Parameters
396
    ----------
397
    file_base: str
398
        File basename.
399
    )",
400
    py::arg("file_base")
483✔
401
  );
402
  lbs_problem.def(
483✔
403
    "SetPointSources",
404
    [](LBSProblem& self, py::kwargs& params)
483✔
405
    {
406
      for (auto [key, value] : params)
×
407
      {
408
        auto c_key = key.cast<std::string>();
×
409
        if (c_key == "clear_point_sources")
×
410
          self.ClearPointSources();
×
411
        else if (c_key == "point_sources")
×
412
        {
413
          auto sources = value.cast<py::list>();
×
414
          for (auto source : sources)
×
415
          {
416
            self.AddPointSource(source.cast<std::shared_ptr<PointSource>>());
×
417
          }
418
        }
×
419
        else
420
          throw std::runtime_error("Invalid argument provided to SetPointSources.\n");
×
421
      }
×
422
    },
×
423
    R"(
424
    Set or clear point sources.
425

426
    Parameters
427
    ----------
428
    clear_point_sources: bool, default=False
429
        If true, all current the point sources of the problem are deleted.
430
    point_sources: List[pyopensn.source.PointSource]
431
        List of new point sources to be added to the problem.
432
    )"
433
  );
434
  lbs_problem.def(
483✔
435
    "SetVolumetricSources",
436
    [](LBSProblem& self, py::kwargs& params)
503✔
437
    {
438
      for (auto [key, value] : params)
60✔
439
      {
440
        auto c_key = key.cast<std::string>();
20✔
441
        if (c_key == "clear_volumetric_sources")
20✔
442
          self.ClearVolumetricSources();
4✔
443
        else if (c_key == "volumetric_sources")
16✔
444
        {
445
          auto sources = value.cast<py::list>();
16✔
446
          for (auto source : sources)
48✔
447
          {
448
            self.AddVolumetricSource(source.cast<std::shared_ptr<VolumetricSource>>());
32✔
449
          }
450
        }
16✔
451
        else
452
          throw std::runtime_error("Invalid argument provided to SetVolumetricSources.\n");
×
453
      }
20✔
454
    },
20✔
455
    R"(
456
    Set or clear volumetric sources.
457

458
    Parameters
459
    ----------
460
    clear_volumetric_sources: bool, default=False
461
        If true, all current the volumetric sources of the problem are deleted.
462
    volumetric_sources: List[pyopensn.source.VolumetricSource]
463
        List of new volumetric sources to be added to the problem.
464
    )"
465
  );
466
  lbs_problem.def(
483✔
467
    "SetBoundaryOptions",
468
    [](LBSProblem& self, py::kwargs& params)
483✔
469
    {
470
      for (auto [key, value] : params)
×
471
      {
472
        auto c_key = key.cast<std::string>();
×
473
        if (c_key == "clear_boundary_conditions")
×
474
          self.ClearBoundaries();
×
475
        else if (c_key == "boundary_conditions")
×
476
        {
477
          auto boundaries = value.cast<py::list>();
×
478
          for (auto boundary : boundaries)
×
479
          {
480
            InputParameters input = LBSProblem::GetBoundaryOptionsBlock();
×
481
            input.AssignParameters(pyobj_to_param_block("", boundary.cast<py::dict>()));
×
482
            self.SetBoundaryOptions(input);
×
483
          }
×
484
        }
×
485
        else
486
          throw std::runtime_error("Invalid argument provided to SetBoundaryOptions.\n");
×
487
      }
×
488
    },
×
489
    R"(
490
    Set or clear boundary conditions.
491

492
    Parameters
493
    ----------
494
    clear_boundary_conditions: bool, default=False
495
        If true, all current boundary conditions are deleted.
496
    boundary_conditions: List[Dict]
497
        A list of boundary condition dictionaries. Each dictionary supports:
498
          - name: str (required)
499
              Boundary name that identifies the specific boundary.
500
          - type: {'vacuum', 'isotropic', 'reflecting'} (required)
501
              Boundary type specification.
502
          - group_strength: List[float], optional
503
              Required when ``type='isotropic'``. Isotropic strength per group.
504
          - function_name: str, optional, default=''
505
              Name of a registered function to evaluate on the boundary.
506
    )"
507
  );
508
  lbs_problem.def(
483✔
509
    "SetAdjoint",
UNCOV
510
    [](LBSProblem& self, bool adjoint)
×
511
    {
512
      self.SetAdjoint(adjoint);
12✔
513
    }
514
  );
515

516
  // discrete ordinate solver
517
  auto do_problem = py::class_<DiscreteOrdinatesProblem, std::shared_ptr<DiscreteOrdinatesProblem>,
483✔
518
                               LBSProblem>(
519
    slv,
520
    "DiscreteOrdinatesProblem",
521
    R"(
522
    Base class for discrete ordinates problems in Cartesian geometry.
523

524
    This class implements the algorithms necessary to solve a problem using the discrete ordinates method.
525
    When paired with a solver base, the result is a solver instance configured for a specific problem type
526
    (steady-state, transient, adjoint, k-eigenvalue, etc.).
527

528
    Wrapper of :cpp:class:`opensn::DiscreteOrdinatesProblem`.
529
    )"
530
  );
483✔
531
  do_problem.def(
966✔
532
    py::init(
483✔
533
      [](py::kwargs& params)
350✔
534
      {
535
        return DiscreteOrdinatesProblem::Create(kwargs_to_param_block(params));
350✔
536
      }
537
    ),
538
    R"(
539
    Construct a discrete ordinates problem with Cartesian geometry.
540

541
    Parameters
542
    ----------
543
    mesh : MeshContinuum
544
        The spatial mesh.
545
    num_groups : int
546
        The total number of energy groups.
547
    groupsets : List[Dict], default=[]
548
        A list of input parameter blocks, each block provides the iterative properties for a
549
        groupset. Each dictionary supports:
550
          - groups_from_to: List[int] (required)
551
              Two-entry list with the first and last group id for the groupset, e.g. ``[0, 3]``.
552
          - angular_quadrature: pyopensn.aquad.AngularQuadrature, optional
553
              Handle to an angular quadrature.
554
          - angle_aggregation_type: {'polar', 'single', 'azimuthal'}, default='polar'
555
              Angle aggregation method to use during sweeping.
556
          - angle_aggregation_num_subsets: int, default=1
557
              Number of angle subsets used for aggregation.
558
          - inner_linear_method: {'classic_richardson', 'petsc_richardson',
559
            'petsc_gmres', 'petsc_bicgstab'}, default='petsc_richardson'
560
              Iterative method used for inner linear solves.
561
          - l_abs_tol: float, default=1.0e-6
562
              Inner linear solver absolute residual tolerance.
563
          - l_max_its: int, default=200
564
              Inner linear solver maximum iterations.
565
          - gmres_restart_interval: int, default=30
566
              GMRES restart interval, if GMRES is used.
567
          - allow_cycles: bool, default=True
568
              Whether cyclic dependencies are allowed in sweeps.
569
          - apply_wgdsa: bool, default=False
570
              Enable within-group DSA for this groupset.
571
          - wgdsa_l_abs_tol: float, default=1.0e-4
572
              WGDSA linear absolute tolerance.
573
          - wgdsa_l_max_its: int, default=30
574
              WGDSA maximum iterations.
575
          - wgdsa_verbose: bool, default=False
576
              Verbose WGDSA output.
577
          - wgdsa_petsc_options: str, default=''
578
              PETSc options string for the WGDSA solver.
579
          - apply_tgdsa: bool, default=False
580
              Enable two-grid DSA for this groupset.
581
          - tgdsa_l_abs_tol: float, default=1.0e-4
582
              TGDSA linear absolute tolerance.
583
          - tgdsa_l_max_its: int, default=30
584
              TGDSA maximum iterations.
585
          - tgdsa_verbose: bool, default=False
586
              Verbose TGDSA output.
587
          - tgdsa_petsc_options: str, default=''
588
              PETSc options string for the TGDSA solver.
589
    xs_map : List[Dict], default=[]
590
        A list of mappings from block ids to cross-section definitions. Each dictionary supports:
591
          - block_ids: List[int] (required)
592
              Mesh block IDs to associate with the cross section.
593
          - xs: pyopensn.xs.MultiGroupXS (required)
594
              Cross-section object to assign to the specified blocks.
595
    boundary_conditions: List[Dict], default=[]
596
        A list containing tables for each boundary specification. Each dictionary supports:
597
          - name: str (required)
598
              Boundary name that identifies the specific boundary.
599
          - type: {'vacuum', 'isotropic', 'reflecting'} (required)
600
              Boundary type specification.
601
          - group_strength: List[float], optional
602
              Required when ``type='isotropic'``. Isotropic strength per group.
603
          - function_name: str, optional, default=''
604
              Name of a registered function to evaluate on the boundary.
605
    point_sources: List[pyopensn.source.PointSource], default=[]
606
        A list of point sources.
607
    volumetric_sources: List[pyopensn.source.VolumetricSource], default=[]
608
        A list of volumetric sources.
609
    options : Dict, default={}
610
        A block of optional configuration parameters. Each dictionary supports the same keys as
611
        :meth:`LBSProblem.SetOptions`, including:
612
          - max_mpi_message_size: int, default=32768
613
          - restart_writes_enabled: bool, default=False
614
          - write_delayed_psi_to_restart: bool, default=True
615
          - read_restart_path: str, default=''
616
          - write_restart_path: str, default=''
617
          - write_restart_time_interval: int, default=0
618
          - use_precursors: bool, default=False
619
          - use_source_moments: bool, default=False
620
          - save_angular_flux: bool, default=False
621
          - verbose_inner_iterations: bool, default=True
622
          - verbose_outer_iterations: bool, default=True
623
          - max_ags_iterations: int, default=100
624
          - ags_tolerance: float, default=1.0e-6
625
          - ags_convergence_check: {'l2', 'pointwise'}, default='l2'
626
          - verbose_ags_iterations: bool, default=True
627
          - power_field_function_on: bool, default=False
628
          - power_default_kappa: float, default=3.20435e-11
629
          - power_normalization: float, default=-1.0
630
          - field_function_prefix_option: {'prefix', 'solver_name'}, default='prefix'
631
          - field_function_prefix: str, default=''
632
    sweep_type : str, default="AAH"
633
        The sweep type to use. Must be one of `AAH` or `CBC`. Defaults to `AAH`.
634
    use_gpus : bool, default=False
635
        A flag specifying whether GPU acceleration is used for the sweep. Currently, only ``AAH`` is
636
        supported.
637
    time_dependent : bool, default=False
638
        Enable time-dependent sweeps. Currently only supported with ``sweep_type="AAH"``.
639
    )"
640
  );
641
  do_problem.def(
483✔
642
    "SetOptions",
643
    [](DiscreteOrdinatesProblem& self, py::kwargs& params)
495✔
644
    {
645
      InputParameters input = DiscreteOrdinatesProblem::GetOptionsBlock();
12✔
646
      input.AssignParameters(kwargs_to_param_block(params));
12✔
647
      self.SetOptions(input);
12✔
648
    },
12✔
649
    R"(
650
    Set problem options from a large list of parameters.
651

652
    Parameters
653
    ----------
654
    adjoint: bool, default=False
655
        Flag for toggling whether the solver is in adjoint mode.
656
    )"
657
  );
658
  do_problem.def(
483✔
659
    "GetPsi",
660
    [](DiscreteOrdinatesProblem& self)
491✔
661
    {
662
      const auto& psi = self.GetPsiNewLocal();
8✔
663
      py::list psi_list;
8✔
664
      for (const auto& vec : psi)
16✔
665
      {
666
        auto array = py::array_t<double>(static_cast<py::ssize_t>(vec.size()),
8✔
667
                                         vec.data(),
668
                                         py::cast(self));
8✔
669
        psi_list.append(array);
8✔
670
      }
8✔
671
      return psi_list;
8✔
UNCOV
672
    },
×
673
    R"(
674
    Return psi as a list of NumPy arrays (float64), using zero-copy views into the
675
    underlying data.
676
    )"
677
  );
678
  do_problem.def(
483✔
679
    "ComputeBalance",
680
    [](DiscreteOrdinatesProblem& self)
510✔
681
    {
682
      ComputeBalance(self);
27✔
683
    },
684
    R"(
685
    Compute and print particle balance for the problem.
686
    )"
687
  );
688
  do_problem.def(
483✔
689
    "ComputeLeakage",
690
    [](DiscreteOrdinatesProblem& self, py::list bnd_names)
510✔
691
    {
692
      auto grid = self.GetGrid();
27✔
693
      // get the supported boundaries
694
      std::map<std::string, std::uint64_t> allowed_bd_names = grid->GetBoundaryNameMap();
27✔
695
      std::map<std::uint64_t, std::string> allowed_bd_ids = grid->GetBoundaryIDMap();
27✔
696
      // get the boundaries to parse, preserving user order
697
      std::vector<std::uint64_t> bndry_ids;
27✔
698
      if (bnd_names.size() > 1)
27✔
699
      {
700
        for (py::handle name : bnd_names)
120✔
701
        {
702
          auto sname = name.cast<std::string>();
72✔
703
          bndry_ids.push_back(allowed_bd_names.at(sname));
72✔
704
        }
72✔
705
      }
706
      else
707
      {
708
        bndry_ids = self.GetGrid()->GetUniqueBoundaryIDs();
9✔
709
      }
710
      // compute the leakage
711
      std::map<std::uint64_t, std::vector<double>> leakage = ComputeLeakage(self, bndry_ids);
27✔
712
      // convert result to native Python
713
      py::dict result;
27✔
714
      for (const auto& bndry_id : bndry_ids)
105✔
715
      {
716
        const auto it = leakage.find(bndry_id);
78✔
717
        if (it == leakage.end())
78✔
UNCOV
718
          continue;
×
719
        // construct numpy array and copy contents
720
        const auto& grp_wise_leakage = it->second;
78✔
721
        py::array_t<double> np_vector(py::ssize_t(grp_wise_leakage.size()));
78✔
722
        auto buffer = np_vector.request();
78✔
723
        auto *np_vector_data = static_cast<double*>(buffer.ptr);
78✔
724
        std::copy(grp_wise_leakage.begin(), grp_wise_leakage.end(), np_vector_data);
78✔
725
        const std::string& name = allowed_bd_ids.at(bndry_id);
78✔
726
        result[py::str(name)] = std::move(np_vector);
156✔
727
      }
78✔
728

729
      return result;
27✔
730
    },
54✔
731
    R"(
732
    Compute leakage for the problem.
733

734
    Parameters
735
    ----------
736
    bnd_names : List[str]
737
        A list of boundary names for which leakage should be computed.
738

739
    Returns
740
    -------
741
    Dict[str, numpy.ndarray]
742
        A dictionary mapping boundary names to group-wise leakage vectors.
743
        Each array contains the outgoing angular flux (per group) integrated over
744
        the corresponding boundary surface.
745

746
    Raises
747
    ------
748
    RuntimeError
749
        If `save_angular_flux` option was not enabled during problem setup.
750

751
    ValueError
752
        If one or more boundary ids are not present on the current mesh.
753
    )",
754
    py::arg("bnd_names")
483✔
755
  );
756

757
  // discrete ordinates curvilinear problem
758
  auto do_curvilinear_problem = py::class_<DiscreteOrdinatesCurvilinearProblem,
483✔
759
                                           std::shared_ptr<DiscreteOrdinatesCurvilinearProblem>,
760
                                           DiscreteOrdinatesProblem>(
761
    slv,
762
    "DiscreteOrdinatesCurvilinearProblem",
763
    R"(
764
    Base class for discrete ordinates problems in curvilinear geometry.
765

766
    Wrapper of :cpp:class:`opensn::DiscreteOrdinatesCurvilinearProblem`.
767
    )"
768
  );
483✔
769
  do_curvilinear_problem.def(
966✔
770
    py::init(
483✔
771
      [](py::kwargs& params)
8✔
772
      {
773
        return DiscreteOrdinatesCurvilinearProblem::Create(kwargs_to_param_block(params));
8✔
774
      }
775
    ),
776
    R"(
777
    Construct a discrete ordinates problem for curvilinear geometry.
778

779
    Warnings
780
    --------
781
       DiscreteOrdinatesCurvilinearProblem is **experimental** and should be used with caution!
782

783
    Parameters
784
    ----------
785
    mesh : MeshContinuum
786
        The spatial mesh.
787
    coord_system : int
788
        Coordinate system to use. Must be set to 2 (cylindrical coordinates).
789
    num_groups : int
790
        The total number of energy groups.
791
    groupsets : list of dict
792
        A list of input parameter blocks, each block provides the iterative properties for a
793
        groupset. Each dictionary supports:
794
          - groups_from_to: List[int] (required)
795
              Two-entry list with the first and last group id for the groupset, e.g. ``[0, 3]``.
796
          - angular_quadrature: pyopensn.aquad.AngularQuadrature, optional
797
              Handle to an angular quadrature.
798
          - angle_aggregation_type: {'polar', 'single', 'azimuthal'}, default='polar'
799
              Angle aggregation method to use during sweeping.
800
          - angle_aggregation_num_subsets: int, default=1
801
              Number of angle subsets used for aggregation.
802
          - inner_linear_method: {'classic_richardson', 'petsc_richardson',
803
            'petsc_gmres', 'petsc_bicgstab'}, default='petsc_richardson'
804
              Iterative method used for inner linear solves.
805
          - l_abs_tol: float, default=1.0e-6
806
              Inner linear solver absolute residual tolerance.
807
          - l_max_its: int, default=200
808
              Inner linear solver maximum iterations.
809
          - gmres_restart_interval: int, default=30
810
              GMRES restart interval, if GMRES is used.
811
          - allow_cycles: bool, default=True
812
              Whether cyclic dependencies are allowed in sweeps.
813
          - apply_wgdsa: bool, default=False
814
              Enable within-group DSA for this groupset.
815
          - wgdsa_l_abs_tol: float, default=1.0e-4
816
              WGDSA linear absolute tolerance.
817
          - wgdsa_l_max_its: int, default=30
818
              WGDSA maximum iterations.
819
          - wgdsa_verbose: bool, default=False
820
              Verbose WGDSA output.
821
          - wgdsa_petsc_options: str, default=''
822
              PETSc options string for the WGDSA solver.
823
          - apply_tgdsa: bool, default=False
824
              Enable two-grid DSA for this groupset.
825
          - tgdsa_l_abs_tol: float, default=1.0e-4
826
              TGDSA linear absolute tolerance.
827
          - tgdsa_l_max_its: int, default=30
828
              TGDSA maximum iterations.
829
          - tgdsa_verbose: bool, default=False
830
              Verbose TGDSA output.
831
          - tgdsa_petsc_options: str, default=''
832
    xs_map : list of dict
833
        A list of mappings from block ids to cross-section definitions. Each dictionary supports:
834
          - block_ids: List[int] (required)
835
              Mesh block IDs to associate with the cross section.
836
          - xs: pyopensn.xs.MultiGroupXS (required)
837
              Cross-section object to assign to the specified blocks.
838
    boundary_conditions: List[Dict], default=[]
839
        A list containing tables for each boundary specification. Each dictionary supports:
840
          - name: str (required)
841
              Boundary name that identifies the specific boundary.
842
          - type: {'vacuum', 'isotropic', 'reflecting'} (required)
843
              Boundary type specification.
844
          - group_strength: List[float], optional
845
              Required when ``type='isotropic'``. Isotropic strength per group.
846
          - function_name: str, optional, default=''
847
              Name of a registered function to evaluate on the boundary.
848
    point_sources: List[pyopensn.source.PointSource], default=[]
849
        A list of point sources.
850
    volumetric_sources: List[pyopensn.source.VolumetricSource], default=[]
851
        A list of volumetric sources.
852
    options : dict, optional
853
        A block of optional configuration parameters. Each dictionary supports the same keys as
854
        :meth:`LBSProblem.SetOptions`, including:
855
          - max_mpi_message_size: int, default=32768
856
          - restart_writes_enabled: bool, default=False
857
          - write_delayed_psi_to_restart: bool, default=True
858
          - read_restart_path: str, default=''
859
          - write_restart_path: str, default=''
860
          - write_restart_time_interval: int, default=0
861
          - use_precursors: bool, default=False
862
          - use_source_moments: bool, default=False
863
          - save_angular_flux: bool, default=False
864
          - verbose_inner_iterations: bool, default=True
865
          - verbose_outer_iterations: bool, default=True
866
          - max_ags_iterations: int, default=100
867
          - ags_tolerance: float, default=1.0e-6
868
          - ags_convergence_check: {'l2', 'pointwise'}, default='l2'
869
          - verbose_ags_iterations: bool, default=True
870
          - power_field_function_on: bool, default=False
871
          - power_default_kappa: float, default=3.20435e-11
872
          - power_normalization: float, default=-1.0
873
          - field_function_prefix_option: {'prefix', 'solver_name'}, default='prefix'
874
          - field_function_prefix: str, default=''
875
    sweep_type : str, optional
876
        The sweep type to use. Must be one of `AAH` or `CBC`. Defaults to `AAH`.
877
    time_dependent : bool, default=False
878
        Enable time-dependent sweeps. Currently only supported with ``sweep_type="AAH"``.
879
    )"
880
  );
881
}
483✔
882

883
// Wrap steady-state solver
884
void
885
WrapSteadyState(py::module& slv)
483✔
886
{
887
  // clang-format off
888
  // steady state solver
889
  auto steady_state_solver = py::class_<SteadyStateSourceSolver, std::shared_ptr<SteadyStateSourceSolver>,
483✔
890
                                        Solver>(
891
    slv,
892
    "SteadyStateSourceSolver",
893
    R"(
894
    Steady state solver.
895

896
    Wrapper of :cpp:class:`opensn::SteadyStateSourceSolver`.
897
    )"
898
  );
483✔
899
  steady_state_solver.def(
966✔
900
    py::init(
483✔
901
      [](py::kwargs& params)
261✔
902
      {
903
        return SteadyStateSourceSolver::Create(kwargs_to_param_block(params));
261✔
904
      }
905
    ),
906
    R"(
907
    Construct a steady state solver.
908

909
    Parameters
910
    ----------
911
    pyopensn.solver.LBSProblem : LBSProblem
912
        Existing LBSProblem instance.
913
    )"
914
  );
915
  // clang-format on
916
}
483✔
917

918
// Wrap time-dependent solver
919
void
920
WrapTimeDependent(py::module& slv)
483✔
921
{
922
  // clang-format off
923
  auto time_dependent_solver =
483✔
924
    py::class_<TimeDependentSourceSolver, std::shared_ptr<TimeDependentSourceSolver>, Solver>(
925
      slv,
926
      "TimeDependentSourceSolver",
927
      R"(
928
      Time dependent solver.
929

930
      Wrapper of :cpp:class:`opensn::TimeDependentSourceSolver`.
931
      )"
932
    );
483✔
933
  time_dependent_solver.def(
966✔
934
    py::init(
483✔
935
      [](py::kwargs& params)
32✔
936
      {
937
        return TimeDependentSourceSolver::Create(kwargs_to_param_block(params));
32✔
938
      }
939
    ),
940
    R"(
941
    Construct a time dependent solver.
942

943
    Parameters
944
    ----------
945
    pyopensn.solver.LBSProblem : LBSProblem
946
        Existing LBSProblem instance.
947
    dt : float, optional, default=1.0
948
        Time step size used during the simulation.
949
    stop_time : float, optional, default=1.0
950
        Simulation end time.
951
    )"
952
  );
953
  time_dependent_solver.def(
483✔
954
    "Advance",
955
    &TimeDependentSourceSolver::Advance,
483✔
956
    R"(
957
    Advance the solver by a single timestep.
958

959
    This method uses the configured `dt` and `theta` values and will return
960
    immediately if the stop time has already been reached. Calling it
961
    repeatedly allows users to write custom python time loops.
962
    )");
963
  time_dependent_solver.def(
483✔
964
    "SetTimeStep",
965
    &TimeDependentSourceSolver::SetTimeStep,
483✔
966
    R"(
967
    Set the timestep size used by :meth:`Advance`.
968

969
    Parameters
970
    ----------
971
    dt : float
972
        New timestep size.
973
    )");
974
  time_dependent_solver.def(
483✔
975
    "SetTheta",
976
    &TimeDependentSourceSolver::SetTheta,
483✔
977
    R"(
978
    Set the theta parameter used by :meth:`Advance`.
979

980
    Parameters
981
    ----------
982
    theta : float
983
        Theta value between 0 and 1.
984
    )");
985
  slv.attr("BackwardEuler") = 1.0;
483✔
986
  slv.attr("CrankNicolson") = 0.5;
966✔
987
  // clang-format on
988
}
483✔
989

990
// Wrap non-linear k-eigen solver
991
void
992
WrapNLKEigen(py::module& slv)
483✔
993
{
994
  // clang-format off
995
  // non-linear k-eigen solver
996
  auto non_linear_k_eigen_solver = py::class_<NonLinearKEigenSolver, std::shared_ptr<NonLinearKEigenSolver>,
483✔
997
                                              Solver>(
998
    slv,
999
    "NonLinearKEigenSolver",
1000
    R"(
1001
    Non-linear k-eigenvalue solver.
1002

1003
    Wrapper of :cpp:class:`opensn::NonLinearKEigenSolver`.
1004
    )"
1005
  );
483✔
1006
  non_linear_k_eigen_solver.def(
966✔
1007
    py::init(
483✔
1008
      [](py::kwargs& params)
28✔
1009
      {
1010
        return NonLinearKEigenSolver::Create(kwargs_to_param_block(params));
28✔
1011
      }
1012
        ),
1013
    R"(
1014
    Construct a non-linear k-eigenvalue solver.
1015

1016
    Parameters
1017
    ----------
1018
    lbs_problem: pyopensn.solver.LBSProblem
1019
        Existing LBSProblem instance.
1020
    nl_abs_tol: float, default=1.0e-8
1021
        Non-linear absolute tolerance.
1022
    nl_rel_tol: float, default=1.0e-8
1023
        Non-linear relative tolerance.
1024
    nl_sol_tol: float, default=1.0e-50
1025
        Non-linear solution tolerance.
1026
    nl_max_its: int, default=50
1027
        Non-linear algorithm maximum iterations.
1028
    l_abs_tol: float, default=1.0e-8
1029
        Linear absolute tolerance.
1030
    l_rel_tol: float, default=1.0e-8
1031
        Linear relative tolerance.
1032
    l_div_tol: float, default=1.0e6
1033
        Linear divergence tolerance.
1034
    l_max_its: int, default=50
1035
        Linear algorithm maximum iterations.
1036
    l_gmres_restart_intvl: int, default=30
1037
        GMRES restart interval.
1038
    l_gmres_breakdown_tol: float, default=1.0e6
1039
        GMRES breakdown tolerance.
1040
    reset_phi0: bool, default=True
1041
        If true, reinitializes scalar fluxes to 1.0.
1042
    num_initial_power_iterations: int, default=0
1043
        Number of initial power iterations before the non-linear solve.
1044
    )"
1045
  );
1046
  non_linear_k_eigen_solver.def(
483✔
1047
    "GetEigenvalue",
1048
    &NonLinearKEigenSolver::GetEigenvalue,
483✔
1049
    R"(
1050
    Return the current k‑eigenvalue.
1051
    )"
1052
  );
1053
  // clang-format on
1054
}
483✔
1055

1056
// Wrap power iteration solvers
1057
void
1058
WrapPIteration(py::module& slv)
483✔
1059
{
1060
  // clang-format off
1061
  // power iteration k-eigen solver
1062
  auto pi_k_eigen_solver = py::class_<PowerIterationKEigenSolver, std::shared_ptr<PowerIterationKEigenSolver>,
483✔
1063
                                      Solver>(
1064
    slv,
1065
    "PowerIterationKEigenSolver",
1066
    R"(
1067
    Power iteration k-eigenvalue solver.
1068

1069
    Wrapper of :cpp:class:`opensn::PowerIterationKEigenSolver`.
1070
    )"
1071
  );
483✔
1072
  pi_k_eigen_solver.def(
966✔
1073
    py::init(
483✔
1074
      [](py::kwargs& params)
35✔
1075
      {
1076
        return PowerIterationKEigenSolver::Create(kwargs_to_param_block(params));
35✔
1077
      }
1078
    ),
1079
    R"(
1080
    Construct a power iteration k-eigen solver.
1081

1082
    Parameters
1083
    ----------
1084
    problem: pyopensn.solver.LBSProblem
1085
        Existing DiscreteOrdinatesProblem instance.
1086
    acceleration: pyopensn.solver.DiscreteOrdinatesKEigenAcceleration
1087
        Optional DiscreteOrdinatesKEigenAcceleration instance for acceleration.
1088
    max_iters: int, default = 1000
1089
        Maximum power iterations allowed.
1090
    k_tol: float, default = 1.0e-10
1091
        Tolerance on the k-eigenvalue.
1092
    reset_solution: bool, default=True
1093
        If true, initialize flux moments to 1.0.
1094
    reset_phi0: bool, default=True
1095
        If true, reinitializes scalar fluxes to 1.0.
1096
    )"
1097
  );
1098
  pi_k_eigen_solver.def(
483✔
1099
    "GetEigenvalue",
1100
    &PowerIterationKEigenSolver::GetEigenvalue,
483✔
1101
    R"(
1102
    Return the current k‑eigenvalue.
1103
    )"
1104
  );
1105
  // clang-format on
1106
}
483✔
1107

1108
// Wrap LBS solver
1109
void
1110
WrapDiscreteOrdinatesKEigenAcceleration(py::module& slv)
483✔
1111
{
1112
  // clang-format off
1113
  // discrete ordinates k-eigen acceleration base
1114
  auto acceleration = py::class_<DiscreteOrdinatesKEigenAcceleration,
483✔
1115
                                 std::shared_ptr<DiscreteOrdinatesKEigenAcceleration>>(
1116
    slv,
1117
    "DiscreteOrdinatesKEigenAcceleration",
1118
    R"(
1119
    Base class for discrete ordinates k-eigenvalue acceleration methods.
1120

1121
    Wrapper of :cpp:class:`opensn::DiscreteOrdinatesKEigenAcceleration`.
1122
    )"
1123
  );
483✔
1124
  // SCDSA acceleration
1125
  auto scdsa_acceleration = py::class_<SCDSAAcceleration,
483✔
1126
                                       std::shared_ptr<SCDSAAcceleration>,
1127
                                       DiscreteOrdinatesKEigenAcceleration>(
1128
    slv,
1129
    "SCDSAAcceleration",
1130
    R"(
1131
    Construct an SCDSA accelerator for the power iteration k-eigenvalue solver.
1132

1133
    Wrapper of :cpp:class:`opensn::SCDSAAcceleration`.
1134
    )"
1135
  );
483✔
1136
  scdsa_acceleration.def(
966✔
1137
    py::init(
483✔
1138
      [](py::kwargs& params)
8✔
1139
      {
1140
        return SCDSAAcceleration::Create(kwargs_to_param_block(params));
8✔
1141
      }
1142
    ),
1143
    R"(
1144
    SCDSA acceleration for the power iteration k-eigenvalue solver.
1145

1146
    Parameters
1147
    ----------
1148
    problem: pyopensn.solver.LBSProblem
1149
        Existing DiscreteOrdinatesProblem instance.
1150
    l_abs_tol: float, defauilt=1.0e-10
1151
        Absolute residual tolerance.
1152
    max_iters: int, default=100
1153
        Maximum allowable iterations.
1154
    verbose: bool, default=False
1155
        If true, enables verbose output.
1156
    petsc_options: str, default="ssss"
1157
        Additional PETSc options.
1158
    pi_max_its: int, default=50
1159
        Maximum allowable iterations for inner power iterations.
1160
    pi_k_tol: float, default=1.0e-10
1161
        k-eigenvalue tolerance for the inner power iterations.
1162
    sdm: str, default="pwld"
1163
        Spatial discretization method to use for the diffusion solver. Valid choices are:
1164
            - 'pwld' : Piecewise Linear Discontinuous
1165
            - 'pwlc' : Piecewise Linear Continuous
1166
    )"
1167
  );
1168
  // SMM acceleration
1169
  auto smm_acceleration = py::class_<SMMAcceleration,
483✔
1170
                                     std::shared_ptr<SMMAcceleration>,
1171
                                     DiscreteOrdinatesKEigenAcceleration>(
1172
    slv,
1173
    "SMMAcceleration",
1174
    R"(
1175
    Construct an SMM accelerator for the power iteration k-eigenvalue solver.
1176

1177
    Wrapper of :cpp:class:`opensn::SMMAcceleration`.
1178
    )"
1179
  );
483✔
1180
  smm_acceleration.def(
966✔
1181
    py::init(
483✔
1182
      [](py::kwargs& params)
4✔
1183
      {
1184
        return SMMAcceleration::Create(kwargs_to_param_block(params));
4✔
1185
      }
1186
    ),
1187
    R"(
1188
    SMM acceleration for the power iteration k-eigenvalue solver.
1189

1190
    Warnings
1191
    --------
1192
       SMM acceleration is **experimental** and should be used with caution!
1193
       SMM accleration only supports problems with isotropic scattering.
1194

1195
    Parameters
1196
    ----------
1197
    problem: pyopensn.solver.LBSProblem
1198
        Existing DiscreteOrdinatesProblem instance.
1199
    l_abs_tol: float, defauilt=1.0e-10
1200
        Absolute residual tolerance.
1201
    max_iters: int, default=100
1202
        Maximum allowable iterations.
1203
    verbose: bool, default=False
1204
        If true, enables verbose output.
1205
    petsc_options: str, default="ssss"
1206
        Additional PETSc options.
1207
    pi_max_its: int, default=50
1208
        Maximum allowable iterations for inner power iterations.
1209
    pi_k_tol: float, default=1.0e-10
1210
        k-eigenvalue tolerance for the inner power iterations.
1211
    sdm: str, default="pwld"
1212
        Spatial discretization method to use for the diffusion solver. Valid choices are:
1213
            - 'pwld' : Piecewise Linear Discontinuous
1214
            - 'pwlc' : Piecewise Linear Continuous
1215
    )"
1216
  );
1217
  // clang-format on
1218
}
483✔
1219

1220
// Wrap the solver components of OpenSn
1221
void
1222
py_solver(py::module& pyopensn)
62✔
1223
{
1224
  py::module slv = pyopensn.def_submodule("solver", "Solver module.");
62✔
1225
  WrapProblem(slv);
62✔
1226
  WrapSolver(slv);
62✔
1227
  WrapLBS(slv);
62✔
1228
  WrapSteadyState(slv);
62✔
1229
  WrapTimeDependent(slv);
62✔
1230
  WrapNLKEigen(slv);
62✔
1231
  WrapDiscreteOrdinatesKEigenAcceleration(slv);
62✔
1232
  WrapPIteration(slv);
62✔
1233
}
62✔
1234

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