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

Open-Sn / opensn / 16794483591

06 Aug 2025 09:06PM UTC coverage: 73.488% (+0.1%) from 73.386%
16794483591

push

github

web-flow
Merge pull request #703 from wdhawkins/documentation_updates

Documentation updates

18200 of 24766 relevant lines covered (73.49%)

43445053.18 hits per line

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

86.73
/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/event_system/physics_event_publisher.h"
7
#include "framework/field_functions/field_function_grid_based.h"
8
#include "framework/physics/solver.h"
9
#include "modules/linear_boltzmann_solvers/discrete_ordinates_problem/acceleration/discrete_ordinates_keigen_acceleration.h"
10
#include "modules/linear_boltzmann_solvers/discrete_ordinates_problem/acceleration/scdsa_acceleration.h"
11
#include "modules/linear_boltzmann_solvers/discrete_ordinates_problem/acceleration/smm_acceleration.h"
12
#include "modules/linear_boltzmann_solvers/discrete_ordinates_curvilinear_problem/discrete_ordinates_curvilinear_problem.h"
13
#include "modules/linear_boltzmann_solvers/discrete_ordinates_problem/discrete_ordinates_problem.h"
14
#include "modules/linear_boltzmann_solvers/solvers/steady_state_solver.h"
15
#include "modules/linear_boltzmann_solvers/solvers/nl_keigen_solver.h"
16
#include "modules/linear_boltzmann_solvers/solvers/pi_keigen_solver.h"
17
#include "modules/linear_boltzmann_solvers/lbs_problem/io/lbs_problem_io.h"
18
#include "modules/linear_boltzmann_solvers/lbs_problem/lbs_problem.h"
19
#include "modules/linear_boltzmann_solvers/lbs_problem/lbs_compute.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)
380✔
35
{
36
  // clang-format off
37
  // problem base
38
  auto problem = py::class_<Problem, std::shared_ptr<Problem>>(
380✔
39
    slv,
40
    "Problem",
41
    R"(
42
    Base class for all problems.
43

44
    Wrapper of :cpp:class:`opensn::Problem`.
45
    )"
46
  );
380✔
47
  problem.def(
380✔
48
    "GetFieldFunctions",
49
    [](Problem& self)
380✔
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
}
380✔
69

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

82
    Wrapper of :cpp:class:`opensn::Solver`.
83
    )"
84
  );
380✔
85
  solver.def(
380✔
86
    "Initialize",
87
    [](Solver & self)
640✔
88
    {
89
      PhysicsEventPublisher::GetInstance().SolverInitialize(self);
260✔
90
    },
91
    "Initialize the solver."
92
  );
93
  solver.def(
380✔
94
    "Execute",
95
    [](Solver & self)
648✔
96
    {
97
      PhysicsEventPublisher::GetInstance().SolverExecute(self);
268✔
98
    },
99
    "Execute the solver."
100
  );
101
  solver.def(
380✔
102
    "Step",
103
    [](Solver & self)
380✔
104
    {
105
      PhysicsEventPublisher::GetInstance().SolverStep(self);
×
106
    },
107
    "Step the solver."
108
  );
109
  solver.def(
380✔
110
    "Advance",
111
    [](Solver & self)
380✔
112
    {
113
      PhysicsEventPublisher::GetInstance().SolverAdvance(self);
×
114
    },
115
    "Advance time values function."
116
  );
117
  // clang-format on
118
}
380✔
119

120
// Wrap LBS solver
121
void
122
WrapLBS(py::module& slv)
380✔
123
{
124
  // clang-format off
125
  // LBS problem
126
  auto lbs_problem = py::class_<LBSProblem, std::shared_ptr<LBSProblem>, Problem>(
380✔
127
    slv,
128
    "LBSProblem",
129
    R"(
130
    Base class for all linear Boltzmann problems.
131

132
    Wrapper of :cpp:class:`opensn::LBSProblem`.
133
    )"
134
  );
380✔
135
  lbs_problem.def(
380✔
136
    "GetScalarFieldFunctionList",
137
    [](LBSProblem& self, bool only_scalar_flux)
380✔
138
    {
139
      py::list field_function_list_per_group;
160✔
140
      for (std::size_t group = 0; group < self.GetNumGroups(); group++)
11,658✔
141
      {
142
        if (only_scalar_flux)
11,498✔
143
        {
144
          std::size_t ff_index = self.MapPhiFieldFunction(group, 0);
8,246✔
145
          field_function_list_per_group.append(self.GetFieldFunctions()[ff_index]);
8,246✔
146
        }
147
        else
148
        {
149
          py::list field_function_list_per_moment;
3,252✔
150
          for (std::size_t moment = 0; moment < self.GetNumMoments(); moment++)
15,828✔
151
          {
152
            std::size_t ff_index = self.MapPhiFieldFunction(group, moment);
12,576✔
153
            field_function_list_per_moment.append(self.GetFieldFunctions()[ff_index]);
12,576✔
154
          }
155
          field_function_list_per_group.append(field_function_list_per_moment);
3,252✔
156
        }
3,252✔
157
      }
158
      return field_function_list_per_group;
160✔
159
    },
×
160
    R"(
161
    Return field functions grouped by energy group and, optionally, by moment.
162

163
    Parameters
164
    ----------
165
    only_scalar_flux : bool, default=True
166
        If True, returns only the zeroth moment (scalar flux) field function for each group.
167
        The result is a flat list of field functions, one per group.
168

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

173
    Returns
174
    -------
175
    Union[List[pyopensn.fieldfunc.FieldFunctionGridBased], List[List[pyopensn.fieldfunc.FieldFunctionGridBased]]]
176
        The structure of the returned list depends on the `only_scalar_flux` flag.
177

178
    Notes
179
    -----
180
    The moment index varies more rapidly than the group index when `only_scalar_flux` is False.
181
    )",
182
    py::arg("only_scalar_flux") = true
380✔
183
  );
184
  lbs_problem.def(
380✔
185
    "GetPowerFieldFunction",
186
    &LBSProblem::GetPowerFieldFunction,
380✔
187
    R"(
188
    Returns the power generation field function, if enabled.
189
    )"
190
  );
191
  lbs_problem.def(
380✔
192
    "SetOptions",
193
    [](LBSProblem& self, py::kwargs& params)
400✔
194
    {
195
      InputParameters input = LBSProblem::GetOptionsBlock();
20✔
196
      input.AssignParameters(kwargs_to_param_block(params));
20✔
197
      self.SetOptions(input);
20✔
198
    },
20✔
199
    R"(
200
    Set problem options from a large list of parameters.
201

202
    Parameters
203
    ----------
204
    spatial_discretization: str, default='pwld'
205
        What spatial discretization to use. Currently only ``pwld`` is supported.
206
    scattering_order: int, default=1
207
        The level of harmonic expansion for the scattering source.
208
    max_mpi_message_size: int default=32768
209
        The maximum MPI message size used during sweeps.
210
    restart_writes_enabled: bool, default=False
211
        Flag that controls writing of restart dumps.
212
    write_delayed_psi_to_restart: bool, default=True
213
        Flag that controls writing of delayed angular fluxes to restarts.
214
    read_restart_path: str, default=''
215
        Full path for reading restart dumps including file basename.
216
    write_restart_path: str, default=''
217
        Full path for writing restart dumps including file basename.
218
    write_restart_time_interval: int, default=0
219
        Time interval in seconds at which restart data is to be written.
220
    use_precursors: bool, default=False
221
        Flag for using delayed neutron precursors.
222
    use_source_moments: bool, default=False
223
        Flag for ignoring fixed sources and selectively using source moments obtained elsewhere.
224
    save_angular_flux: bool, default=False
225
        Flag indicating whether angular fluxes are to be stored or not.
226
    adjoint: bool, default=False
227
        Flag for toggling whether the solver is in adjoint mode.
228
    verbose_inner_iterations: bool, default=True
229
        Flag to control verbosity of inner iterations.
230
    verbose_outer_iterations: bool, default=True
231
        Flag to control verbosity of across-groupset iterations.
232
    max_ags_iterations: int, default=100
233
        Maximum number of across-groupset iterations.
234
    ags_tolerance: float, default=1.0e-6
235
        Across-groupset iterations tolerance.
236
    ags_convergence_check: {'l2', 'pointwise'}, default='l2'
237
        Type of convergence check for AGS iterations.
238
    verbose_ags_iterations: bool, default=True
239
        Flag to control verbosity of across-groupset iterations.
240
    power_field_function_on: bool, default=False
241
        Flag to control the creation of the power generation field function. If set to ``True``, a
242
        field function will be created with the general name ``<solver_name>_power_generation``.
243
    power_default_kappa: float, default=3.20435e-11
244
        Default ``kappa`` value (Energy released per fission) to use for power generation when cross
245
        sections do not have ``kappa`` values. Default corresponds to 200 MeV per fission.
246
    power_normalization: float, default=-1.0
247
        Power normalization factor to use. Supply a negative or zero number to turn this off.
248
    field_function_prefix_option: {'prefix', 'solver_name'}, default='prefix'
249
        Prefix option on field function names. If unset, flux field functions will be exported as
250
        ``phi_gXXX_mYYY``, where ``XXX`` is the zero-padded 3-digit group number and ``YYY`` is the
251
        zero-padded 3-digit moment.
252
    field_function_prefix: str, default=''
253
        Prefix to use on all field functions. By default, this is empty. If specified, flux moments
254
        are exported as ``prefix_phi_gXXX_mYYY``.
255
    boundary_conditions: List[Dict], default=[]
256
        A list containing tables for each boundary specification.
257
    clear_boundary_conditions: bool, default=False
258
        Clears all boundary conditions. If no additional boundary conditions are supplied, all
259
        boundaries become vacuum.
260
    point_sources: List[pyopensn.source.PointSource], default=[]
261
        A list of point sources.
262
    clear_point_sources: bool, default=False
263
        Clear all point sources.
264
    volumetric_sources: List[pyopensn.source.VolumetricSource], default=[]
265
        A list of volumetric sources.
266
    clear_volumetric_sources: bool, default=False
267
        Clear all volumetric sources.
268
    )"
269
  );
270
  lbs_problem.def(
380✔
271
    "ComputeFissionRate",
272
    [](LBSProblem& self, const std::string& scalar_flux_iterate)
380✔
273
    {
274
      const std::vector<double>* phi_ptr;
×
275
      if (scalar_flux_iterate == "old")
×
276
      {
277
        phi_ptr = &self.GetPhiOldLocal();
×
278
      }
279
      else if (scalar_flux_iterate == "new")
×
280
      {
281
        phi_ptr = &self.GetPhiNewLocal();
×
282
      }
283
      else
284
      {
285
        throw std::invalid_argument("Unknown scalar_flux_iterate value: \"" + scalar_flux_iterate + "\".");
×
286
      }
287
      return ComputeFissionRate(self, *phi_ptr);
×
288
    },
289
    R"(
290
    Computes the total fission rate.
291

292
    Parameters
293
    ----------
294
    scalar_flux_iterate : {'old', 'new'}
295
        Specifies which scalar flux vector to use in the calculation.
296
            - 'old': Use the previous scalar flux iterate.
297
            - 'new': Use the current scalar flux iterate.
298

299
    Returns
300
    -------
301
    float
302
        The total fission rate.
303

304
    Raises
305
    ------
306
    ValueError
307
        If `scalar_flux_iterate` is not 'old' or 'new'.
308
    )",
309
    py::arg("scalar_flux_iterate")
380✔
310
  );
311
  lbs_problem.def(
380✔
312
    "WriteFluxMoments",
313
    [](LBSProblem& self, const std::string& file_base)
392✔
314
    {
315
      LBSSolverIO::WriteFluxMoments(self, file_base);
12✔
316
    },
317
    R"(
318
    Write flux moments to file.
319

320
    Parameters
321
    ----------
322
    file_base: str
323
        File basename.
324
    )",
325
    py::arg("file_base")
380✔
326
  );
327
  lbs_problem.def(
380✔
328
    "CreateAndWriteSourceMoments",
329
    [](LBSProblem& self, const std::string& file_base)
384✔
330
    {
331
      std::vector<double> source_moments = self.MakeSourceMomentsFromPhi();
4✔
332
      LBSSolverIO::WriteFluxMoments(self, file_base, source_moments);
4✔
333
    },
4✔
334
    R"(
335
    Write source moments from latest flux iterate to file.
336

337
    Parameters
338
    ----------
339
    file_base: str
340
        File basename.
341
    )",
342
    py::arg("file_base")
380✔
343
  );
344
  lbs_problem.def(
380✔
345
    "ReadFluxMomentsAndMakeSourceMoments",
346
    [](LBSProblem& self, const std::string& file_base, bool single_file_flag)
380✔
347
    {
348
      LBSSolverIO::ReadFluxMoments(self, file_base, single_file_flag, self.GetExtSrcMomentsLocal());
×
349
      log.Log() << "Making source moments from flux file.";
×
350
      std::vector<double>& temp_phi = self.GetPhiOldLocal();
×
351
      self.GetPhiOldLocal() = self.GetExtSrcMomentsLocal();
×
352
      self.GetExtSrcMomentsLocal() = self.MakeSourceMomentsFromPhi();
×
353
      self.GetPhiOldLocal() = temp_phi;
×
354
    },
×
355
    R"(
356
    Read flux moments and compute corresponding source moments.
357

358
    Parameters
359
    ----------
360
    file_base: str
361
        File basename.
362
    single_file_flag: bool
363
        True if all flux moments are in a single file.
364
    )",
365
    py::arg("file_base"),
760✔
366
    py::arg("single_file_flag")
380✔
367
  );
368
  lbs_problem.def(
380✔
369
    "ReadSourceMoments",
370
    [](LBSProblem& self, const std::string& file_base, bool single_file_flag)
384✔
371
    {
372
      LBSSolverIO::ReadFluxMoments(self, file_base, single_file_flag, self.GetExtSrcMomentsLocal());
4✔
373
    },
4✔
374
    R"(
375
    Read source moments from file.
376

377
    Parameters
378
    ----------
379
    file_base: str
380
        File basename.
381
    single_file_flag: bool
382
        True if all source moments are in a single file.
383
    )",
384
    py::arg("file_base"),
760✔
385
    py::arg("single_file_flag")
380✔
386
  );
387
  lbs_problem.def(
380✔
388
    "ReadFluxMoments",
389
    [](LBSProblem& self, const std::string& file_base, bool single_file_flag)
380✔
390
    {
391
      LBSSolverIO::ReadFluxMoments(self, file_base, single_file_flag);
×
392
    },
393
    R"(
394
    Read flux moment data.
395

396
    Parameters
397
    ----------
398
    file_base: str
399
        File basename.
400
    single_file_flag: bool
401
        True if all flux moments are in a single file.
402
    )",
403
    py::arg("file_base"),
760✔
404
    py::arg("single_file_flag")
380✔
405
  );
406
  lbs_problem.def(
380✔
407
    "WriteAngularFluxes",
408
    [](LBSProblem& self, const std::string& file_base)
384✔
409
    {
410
      LBSSolverIO::WriteAngularFluxes(self, file_base);
4✔
411
    },
412
    R"(
413
    Write angular flux data to file.
414

415
    Parameters
416
    ----------
417
    file_base: str
418
        File basename.
419
    )",
420
    py::arg("file_base")
380✔
421
  );
422
  lbs_problem.def(
380✔
423
    "ReadAngularFluxes",
424
    [](LBSProblem& self, const std::string& file_base)
384✔
425
    {
426
      LBSSolverIO::ReadAngularFluxes(self, file_base);
4✔
427
    },
428
    R"(
429
    Read angular fluxes from file.
430

431
    Parameters
432
    ----------
433
    file_base: str
434
        File basename.
435
    )",
436
    py::arg("file_base")
380✔
437
  );
438

439
  // discrete ordinate solver
440
  auto do_problem = py::class_<DiscreteOrdinatesProblem, std::shared_ptr<DiscreteOrdinatesProblem>,
380✔
441
                               LBSProblem>(
442
    slv,
443
    "DiscreteOrdinatesProblem",
444
    R"(
445
    Base class for discrete ordinates problems in Cartesian geometry.
446

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

451
    Wrapper of :cpp:class:`opensn::DiscreteOrdinatesProblem`.
452
    )"
453
  );
380✔
454
  do_problem.def(
760✔
455
    py::init(
380✔
456
      [](py::kwargs& params)
252✔
457
      {
458
        return DiscreteOrdinatesProblem::Create(kwargs_to_param_block(params));
252✔
459
      }
460
    ),
461
    R"(
462
    Construct a discrete ordinates problem with Cartesian geometry.
463

464
    Parameters
465
    ----------
466
    mesh : MeshContinuum
467
        The spatial mesh.
468
    num_groups : int
469
        The total number of energy groups.
470
    groupsets : List[Dict], default=[]
471
        A list of input parameter blocks, each block provides the iterative properties for a
472
        groupset.
473
    xs_map : List[Dict], default=[]
474
        A list of mappings from block ids to cross-section definitions.
475
    options : Dict, default={}
476
        A block of optional configuration parameters. See `SetOptions` for available settings.
477
    sweep_type : str, default="AAH"
478
        The sweep type to use. Must be one of `AAH` or `CBC`. Defaults to `AAH`.
479
    use_gpus : bool, default=False
480
        A flag specifying whether GPU acceleration is used for the sweep. Currently, only ``AAH`` is
481
        supported.
482
    )"
483
  );
484
  do_problem.def(
380✔
485
    "ComputeBalance",
486
    [](DiscreteOrdinatesProblem& self)
399✔
487
    {
488
      ComputeBalance(self);
19✔
489
    },
490
    R"(
491
    Compute and print particle balance for the problem.
492
    )"
493
  );
494
  do_problem.def(
380✔
495
    "ComputeLeakage",
496
    [](DiscreteOrdinatesProblem& self, py::list bnd_names)
399✔
497
    {
498
      // get the supported boundaries
499
      std::map<std::string, std::uint64_t> allowed_bd_names = LBSProblem::supported_boundary_names;
19✔
500
      std::map<std::uint64_t, std::string> allowed_bd_ids = LBSProblem::supported_boundary_ids;
19✔
501
      // get the boundaries to parse
502
      std::vector<std::uint64_t> bndry_ids;
19✔
503
      if (bnd_names.size() > 1)
19✔
504
      {
505
        for (py::handle name : bnd_names)
×
506
        {
507
          bndry_ids.push_back(allowed_bd_names.at(name.cast<std::string>()));
×
508
        }
509
      }
510
      else
511
      {
512
        bndry_ids = self.GetGrid()->GetUniqueBoundaryIDs();
57✔
513
      }
514
      // compute the leakage
515
      std::map<std::uint64_t, std::vector<double>> leakage = ComputeLeakage(self, bndry_ids);
19✔
516
      // convert result to native Python
517
      py::dict result;
19✔
518
      for (const auto& [bndry_id, gr_wise_leakage] : leakage)
57✔
519
      {
520
        py::array_t<double> np_vector = py::array_t<double>(gr_wise_leakage.size());
38✔
521
        py::buffer_info buffer = np_vector.request();
38✔
522
        double* np_vector_data = static_cast<double*>(buffer.ptr);
38✔
523
        std::copy(gr_wise_leakage.begin(), gr_wise_leakage.end(), np_vector_data);
38✔
524
        result[allowed_bd_ids.at(bndry_id).data()] = np_vector;
38✔
525
      }
38✔
526
      return result;
19✔
527
    },
19✔
528
    R"(
529
    Compute leakage for the problem.
530

531
    Parameters
532
    ----------
533
    bnd_names : List[str]
534
        A list of boundary names for which leakage should be computed.
535

536
    Returns
537
    -------
538
    Dict[str, numpy.ndarray]
539
        A dictionary mapping boundary names to group-wise leakage vectors.
540
        Each array contains the outgoing angular flux (per group) integrated over
541
        the corresponding boundary surface.
542

543
    Raises
544
    ------
545
    RuntimeError
546
        If `save_angular_flux` option was not enabled during problem setup.
547

548
    ValueError
549
        If one or more boundary ids are not present on the current mesh.
550
    )",
551
    py::arg("bnd_names")
380✔
552
  );
553

554
  // discrete ordinates curvilinear problem
555
  auto do_curvilinear_problem = py::class_<DiscreteOrdinatesCurvilinearProblem,
380✔
556
                                           std::shared_ptr<DiscreteOrdinatesCurvilinearProblem>,
557
                                           DiscreteOrdinatesProblem>(
558
    slv,
559
    "DiscreteOrdinatesCurvilinearProblem",
560
    R"(
561
    Base class for discrete ordinates problems in curvilinear geometry.
562

563
    Wrapper of :cpp:class:`opensn::DiscreteOrdinatesCurvilinearProblem`.
564
    )"
565
  );
380✔
566
  do_curvilinear_problem.def(
760✔
567
    py::init(
380✔
568
      [](py::kwargs& params)
8✔
569
      {
570
        return DiscreteOrdinatesCurvilinearProblem::Create(kwargs_to_param_block(params));
8✔
571
      }
572
        ),
573
    R"(
574
    Construct a discrete ordinates problem for curvilinear geometry.
575

576
    Parameters
577
    ----------
578
    mesh : MeshContinuum
579
        The spatial mesh.
580
    coord_system : int
581
        Coordinate system to use. Must be set to 2 (cylindrical coordinates).
582
    num_groups : int
583
        The total number of energy groups.
584
    groupsets : list of dict
585
        A list of input parameter blocks, each block provides the iterative properties for a
586
        groupset.
587
    xs_map : list of dict
588
        A list of mappings from block ids to cross-section definitions.
589
    options : dict, optional
590
        A block of optional configuration parameters. See `SetOptions` for available settings.
591
    sweep_type : str, optional
592
        The sweep type to use. Must be one of `AAH` or `CBC`. Defaults to `AAH`.
593
    )"
594
  );
595
}
380✔
596

597
// Wrap steady-state solver
598
void
599
WrapSteadyState(py::module& slv)
380✔
600
{
601
  // clang-format off
602
  // steady state solver
603
  auto steady_state_solver = py::class_<SteadyStateSolver, std::shared_ptr<SteadyStateSolver>,
380✔
604
                                        Solver>(
605
    slv,
606
    "SteadyStateSolver",
607
    R"(
608
    Steady state solver.
609

610
    Wrapper of :cpp:class:`opensn::SteadyStateSolver`.
611
    )"
612
  );
380✔
613
  steady_state_solver.def(
760✔
614
    py::init(
380✔
615
      [](py::kwargs& params)
198✔
616
      {
617
        return SteadyStateSolver::Create(kwargs_to_param_block(params));
198✔
618
      }
619
    ),
620
    R"(
621
    Construct a steady state solver.
622

623
    Parameters
624
    ----------
625
    pyopensn.solver.LBSProblem : LBSProblem
626
        Existing LBSProblem instance.
627
    )"
628
  );
629
  // clang-format on
630
}
380✔
631

632
// Wrap non-linear k-eigen solver
633
void
634
WrapNLKEigen(py::module& slv)
380✔
635
{
636
  // clang-format off
637
  // non-linear k-eigen solver
638
  auto non_linear_k_eigen_solver = py::class_<NonLinearKEigenSolver, std::shared_ptr<NonLinearKEigenSolver>,
380✔
639
                                              Solver>(
640
    slv,
641
    "NonLinearKEigenSolver",
642
    R"(
643
    Non-linear k-eigenvalue solver.
644

645
    Wrapper of :cpp:class:`opensn::NonLinearKEigenSolver`.
646
    )"
647
  );
380✔
648
  non_linear_k_eigen_solver.def(
760✔
649
    py::init(
380✔
650
      [](py::kwargs& params)
28✔
651
      {
652
        return NonLinearKEigenSolver::Create(kwargs_to_param_block(params));
28✔
653
      }
654
        ),
655
    R"(
656
    Construct a non-linear k-eigenvalue solver.
657

658
    Parameters
659
    ----------
660
    lbs_problem: pyopensn.solver.LBSProblem
661
        Existing LBSProblem instance.
662
    nl_abs_tol: float, default=1.0e-8
663
        Non-linear absolute tolerance.
664
    nl_rel_tol: float, default=1.0e-8
665
        Non-linear relative tolerance.
666
    nl_sol_tol: float, default=1.0e-50
667
        Non-linear solution tolerance.
668
    nl_max_its: int, default=50
669
        Non-linear algorithm maximum iterations.
670
    l_abs_tol: float, default=1.0e-8
671
        Linear absolute tolerance.
672
    l_rel_tol: float, default=1.0e-8
673
        Linear relative tolerance.
674
    l_div_tol: float, default=1.0e6
675
        Linear divergence tolerance.
676
    l_max_its: int, default=50
677
        Linear algorithm maximum iterations.
678
    l_gmres_restart_intvl: int, default=30
679
        GMRES restart interval.
680
    l_gmres_breakdown_tol: float, default=1.0e6
681
        GMRES breakdown tolerance.
682
    reset_phi0: bool, default=True
683
        If true, reinitializes scalar fluxes to 1.0.
684
    num_initial_power_iterations: int, default=0
685
        Number of initial power iterations before the non-linear solve.
686
    )"
687
  );
688
  non_linear_k_eigen_solver.def(
380✔
689
    "GetEigenvalue",
690
    &NonLinearKEigenSolver::GetEigenvalue,
380✔
691
    R"(
692
    Return the current k‑eigenvalue.
693
    )"
694
  );
695
  // clang-format on
696
}
380✔
697

698
// Wrap power iteration solvers
699
void
700
WrapPIteration(py::module& slv)
380✔
701
{
702
  // clang-format off
703
  // power iteration k-eigen solver
704
  auto pi_k_eigen_solver = py::class_<PowerIterationKEigenSolver, std::shared_ptr<PowerIterationKEigenSolver>,
380✔
705
                                      Solver>(
706
    slv,
707
    "PowerIterationKEigenSolver",
708
    R"(
709
    Power iteration k-eigenvalue solver.
710

711
    Wrapper of :cpp:class:`opensn::PowerIterationKEigenSolver`.
712
    )"
713
  );
380✔
714
  pi_k_eigen_solver.def(
760✔
715
    py::init(
380✔
716
      [](py::kwargs& params)
34✔
717
      {
718
        return PowerIterationKEigenSolver::Create(kwargs_to_param_block(params));
34✔
719
      }
720
    ),
721
    R"(
722
    Construct a power iteration k-eigen solver.
723

724
    Parameters
725
    ----------
726
    problem: pyopensn.solver.LBSProblem
727
        Existing DiscreteOrdinatesProblem instance.
728
    acceleration: pyopensn.solver.DiscreteOrdinatesKEigenAcceleration
729
        Optional DiscreteOrdinatesKEigenAcceleration instance for acceleration.
730
    max_iters: int, default = 1000
731
        Maximum power iterations allowed.
732
    k_tol: float, default = 1.0e-10
733
        Tolerance on the k-eigenvalue.
734
    reset_solution: bool, default=True
735
        If true, initialize flux moments to 1.0.
736
    reset_phi0: bool, default=True
737
        If true, reinitializes scalar fluxes to 1.0.
738
    )"
739
  );
740
  pi_k_eigen_solver.def(
380✔
741
    "GetEigenvalue",
742
    &PowerIterationKEigenSolver::GetEigenvalue,
380✔
743
    R"(
744
    Return the current k‑eigenvalue.
745
    )"
746
  );
747
  // clang-format on
748
}
380✔
749

750
// Wrap LBS solver
751
void
752
WrapDiscreteOrdinatesKEigenAcceleration(py::module& slv)
380✔
753
{
754
  // clang-format off
755
  // DiscreteOrdinatesKEigenAcceleration base
756
  auto acceleration = py::class_<DiscreteOrdinatesKEigenAcceleration,
380✔
757
                                     std::shared_ptr<DiscreteOrdinatesKEigenAcceleration>>(
758
    slv,
759
    "LBSAccelertion",
760
    R"(
761
    Base class for LBS acceleration methods.
762

763
    Wrapper of :cpp:class:`opensn::DiscreteOrdinatesKEigenAcceleration`.
764
    )"
765
  );
380✔
766

767
  // SCDSA acceleration
768
  auto scdsa_acceleration = py::class_<SCDSAAcceleration,
380✔
769
                                       std::shared_ptr<SCDSAAcceleration>,
770
                                       DiscreteOrdinatesKEigenAcceleration>(
771
    slv,
772
    "SCDSAAcceleration",
773
    R"(
774
    SCDSA acceleration scheme to be used the power iteration k-eigenvalue solver.
775

776
    Wrapper of :cpp:class:`opensn::SCDSAAcceleration`.
777
    )"
778
  );
380✔
779
  scdsa_acceleration.def(
760✔
780
    py::init(
380✔
781
      [](py::kwargs& params)
8✔
782
      {
783
        return SCDSAAcceleration::Create(kwargs_to_param_block(params));
8✔
784
      }
785
    ),
786
    R"(
787
    Construct a SCDSA acceleration solver for LBS.
788

789
    Parameters
790
    ----------
791
    problem: pyopensn.solver.LBSProblem
792
        Existing DiscreteOrdinatesProblem instance.
793
    l_abs_tol: float, defauilt=1.0e-10
794
        Absolute residual tolerance.
795
    max_iters: int, default=100
796
        Maximum allowable iterations.
797
    verbose: bool, default=False
798
        If true, enables verbose output.
799
    petsc_options: str, default="ssss"
800
        Additional PETSc options.
801
    pi_max_its: int, default=50
802
        Maximum allowable iterations for inner power iterations.
803
    pi_k_tol: float, default=1.0e-10
804
        k-eigenvalue tolerance for the inner power iterations.
805
    sdm: str, default="pwld"
806
        Spatial discretization method to use for the diffusion solver. Valid choices are:
807
            - 'pwld' : Piecewise Linear Discontinuous
808
            - 'pwlc' : Piecewise Linear Continuous
809
    )"
810
  );
811

812
  // SMM acceleration
813
  auto smm_acceleration = py::class_<SMMAcceleration,
380✔
814
                                     std::shared_ptr<SMMAcceleration>,
815
                                     DiscreteOrdinatesKEigenAcceleration>(
816
    slv,
817
    "SMMAcceleration",
818
    R"(
819
    SCDSA acceleration scheme to be used the power iteration k-eigenvalue solver.
820

821
    Wrapper of :cpp:class:`opensn::SMMAcceleration`.
822
    )"
823
  );
380✔
824
  smm_acceleration.def(
760✔
825
    py::init(
380✔
826
      [](py::kwargs& params)
4✔
827
      {
828
        return SMMAcceleration::Create(kwargs_to_param_block(params));
4✔
829
      }
830
    ),
831
    R"(
832
    Construct a SMM acceleration solver for LBS.
833

834
    Parameters
835
    ----------
836
    problem: pyopensn.solver.LBSProblem
837
        Existing DiscreteOrdinatesProblem instance.
838
    l_abs_tol: float, defauilt=1.0e-10
839
        Absolute residual tolerance.
840
    max_iters: int, default=100
841
        Maximum allowable iterations.
842
    verbose: bool, default=False
843
        If true, enables verbose output.
844
    petsc_options: str, default="ssss"
845
        Additional PETSc options.
846
    pi_max_its: int, default=50
847
        Maximum allowable iterations for inner power iterations.
848
    pi_k_tol: float, default=1.0e-10
849
        k-eigenvalue tolerance for the inner power iterations.
850
    sdm: str, default="pwld"
851
        Spatial discretization method to use for the diffusion solver. Valid choices are:
852
            - 'pwld' : Piecewise Linear Discontinuous
853
            - 'pwlc' : Piecewise Linear Continuous
854
    )"
855
  );
856
  // clang-format on
857
}
380✔
858

859
// Wrap the solver components of OpenSn
860
void
861
py_solver(py::module& pyopensn)
34✔
862
{
863
  py::module slv = pyopensn.def_submodule("solver", "Solver module.");
34✔
864
  WrapProblem(slv);
34✔
865
  WrapSolver(slv);
34✔
866
  WrapLBS(slv);
34✔
867
  WrapSteadyState(slv);
34✔
868
  WrapNLKEigen(slv);
34✔
869
  WrapPIteration(slv);
34✔
870
  WrapDiscreteOrdinatesKEigenAcceleration(slv);
34✔
871
}
34✔
872

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