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

Open-Sn / opensn / 17995540625

23 Sep 2025 01:57AM UTC coverage: 74.703%. Remained the same
17995540625

push

github

web-flow
Merge pull request #766 from wdhawkins/python_doc_updates

Updating Python interface documentation

17718 of 23718 relevant lines covered (74.7%)

44376277.59 hits per line

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

77.22
/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/steady_state_solver.h"
13
#include "modules/linear_boltzmann_solvers/solvers/nl_keigen_solver.h"
14
#include "modules/linear_boltzmann_solvers/solvers/pi_keigen_solver.h"
15
#include "modules/linear_boltzmann_solvers/lbs_problem/io/lbs_problem_io.h"
16
#include "modules/linear_boltzmann_solvers/lbs_problem/lbs_problem.h"
17
#include "modules/linear_boltzmann_solvers/lbs_problem/lbs_compute.h"
18
#include "modules/solver.h"
19
#include <pybind11/numpy.h>
20
#include <algorithm>
21
#include <cstddef>
22
#include <cstdint>
23
#include <map>
24
#include <memory>
25
#include <string>
26
#include <vector>
27

28
namespace opensn
29
{
30

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

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

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

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

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

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

119
    Wrapper of :cpp:class:`opensn::LBSProblem`.
120
    )"
121
  );
388✔
122
  lbs_problem.def(
388✔
123
    "GetScalarFieldFunctionList",
124
    [](LBSProblem& self, bool only_scalar_flux)
388✔
125
    {
126
      py::list field_function_list_per_group;
164✔
127
      for (std::size_t group = 0; group < self.GetNumGroups(); group++)
11,666✔
128
      {
129
        if (only_scalar_flux)
11,502✔
130
        {
131
          std::size_t ff_index = self.MapPhiFieldFunction(group, 0);
8,246✔
132
          field_function_list_per_group.append(self.GetFieldFunctions()[ff_index]);
8,246✔
133
        }
134
        else
135
        {
136
          py::list field_function_list_per_moment;
3,256✔
137
          for (std::size_t moment = 0; moment < self.GetNumMoments(); moment++)
15,836✔
138
          {
139
            std::size_t ff_index = self.MapPhiFieldFunction(group, moment);
12,580✔
140
            field_function_list_per_moment.append(self.GetFieldFunctions()[ff_index]);
12,580✔
141
          }
142
          field_function_list_per_group.append(field_function_list_per_moment);
3,256✔
143
        }
3,256✔
144
      }
145
      return field_function_list_per_group;
164✔
146
    },
×
147
    R"(
148
    Return field functions grouped by energy group and, optionally, by moment.
149

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

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

160
    Returns
161
    -------
162
    Union[List[pyopensn.fieldfunc.FieldFunctionGridBased], List[List[pyopensn.fieldfunc.FieldFunctionGridBased]]]
163
        The structure of the returned list depends on the `only_scalar_flux` flag.
164

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

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

262
    Parameters
263
    ----------
264
    scalar_flux_iterate : {'old', 'new'}
265
        Specifies which scalar flux vector to use in the calculation.
266
            - 'old': Use the previous scalar flux iterate.
267
            - 'new': Use the current scalar flux iterate.
268

269
    Returns
270
    -------
271
    float
272
        The total fission rate.
273

274
    Raises
275
    ------
276
    ValueError
277
        If `scalar_flux_iterate` is not 'old' or 'new'.
278
    )",
279
    py::arg("scalar_flux_iterate")
388✔
280
  );
281
  lbs_problem.def(
388✔
282
    "WriteFluxMoments",
283
    [](LBSProblem& self, const std::string& file_base)
400✔
284
    {
285
      LBSSolverIO::WriteFluxMoments(self, file_base);
12✔
286
    },
287
    R"(
288
    Write flux moments to file.
289

290
    Parameters
291
    ----------
292
    file_base: str
293
        File basename.
294
    )",
295
    py::arg("file_base")
388✔
296
  );
297
  lbs_problem.def(
388✔
298
    "CreateAndWriteSourceMoments",
299
    [](LBSProblem& self, const std::string& file_base)
392✔
300
    {
301
      std::vector<double> source_moments = self.MakeSourceMomentsFromPhi();
4✔
302
      LBSSolverIO::WriteFluxMoments(self, file_base, source_moments);
4✔
303
    },
4✔
304
    R"(
305
    Write source moments from latest flux iterate to file.
306

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

328
    Parameters
329
    ----------
330
    file_base: str
331
        File basename.
332
    single_file_flag: bool
333
        True if all flux moments are in a single file.
334
    )",
335
    py::arg("file_base"),
776✔
336
    py::arg("single_file_flag")
388✔
337
  );
338
  lbs_problem.def(
388✔
339
    "ReadSourceMoments",
340
    [](LBSProblem& self, const std::string& file_base, bool single_file_flag)
392✔
341
    {
342
      LBSSolverIO::ReadFluxMoments(self, file_base, single_file_flag, self.GetExtSrcMomentsLocal());
4✔
343
    },
4✔
344
    R"(
345
    Read source moments from file.
346

347
    Parameters
348
    ----------
349
    file_base: str
350
        File basename.
351
    single_file_flag: bool
352
        True if all source moments are in a single file.
353
    )",
354
    py::arg("file_base"),
776✔
355
    py::arg("single_file_flag")
388✔
356
  );
357
  lbs_problem.def(
388✔
358
    "ReadFluxMoments",
359
    [](LBSProblem& self, const std::string& file_base, bool single_file_flag)
388✔
360
    {
361
      LBSSolverIO::ReadFluxMoments(self, file_base, single_file_flag);
×
362
    },
363
    R"(
364
    Read flux moment data.
365

366
    Parameters
367
    ----------
368
    file_base: str
369
        File basename.
370
    single_file_flag: bool
371
        True if all flux moments are in a single file.
372
    )",
373
    py::arg("file_base"),
776✔
374
    py::arg("single_file_flag")
388✔
375
  );
376
  lbs_problem.def(
388✔
377
    "WriteAngularFluxes",
378
    [](LBSProblem& self, const std::string& file_base)
392✔
379
    {
380
      LBSSolverIO::WriteAngularFluxes(self, file_base);
4✔
381
    },
382
    R"(
383
    Write angular flux data to file.
384

385
    Parameters
386
    ----------
387
    file_base: str
388
        File basename.
389
    )",
390
    py::arg("file_base")
388✔
391
  );
392
  lbs_problem.def(
388✔
393
    "ReadAngularFluxes",
394
    [](LBSProblem& self, const std::string& file_base)
392✔
395
    {
396
      LBSSolverIO::ReadAngularFluxes(self, file_base);
4✔
397
    },
398
    R"(
399
    Read angular fluxes from file.
400

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

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

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

497
  // discrete ordinate solver
498
  auto do_problem = py::class_<DiscreteOrdinatesProblem, std::shared_ptr<DiscreteOrdinatesProblem>,
388✔
499
                               LBSProblem>(
500
    slv,
501
    "DiscreteOrdinatesProblem",
502
    R"(
503
    Base class for discrete ordinates problems in Cartesian geometry.
504

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

509
    Wrapper of :cpp:class:`opensn::DiscreteOrdinatesProblem`.
510
    )"
511
  );
388✔
512
  do_problem.def(
776✔
513
    py::init(
388✔
514
      [](py::kwargs& params)
259✔
515
      {
516
        return DiscreteOrdinatesProblem::Create(kwargs_to_param_block(params));
259✔
517
      }
518
    ),
519
    R"(
520
    Construct a discrete ordinates problem with Cartesian geometry.
521

522
    Parameters
523
    ----------
524
    mesh : MeshContinuum
525
        The spatial mesh.
526
    num_groups : int
527
        The total number of energy groups.
528
    groupsets : List[Dict], default=[]
529
        A list of input parameter blocks, each block provides the iterative properties for a
530
        groupset.
531
    xs_map : List[Dict], default=[]
532
        A list of mappings from block ids to cross-section definitions.
533
    scattering_order: int, default=0
534
        The level of harmonic expansion for the scattering source.
535
    boundary_conditions: List[Dict], default=[]
536
        A list containing tables for each boundary specification.
537
    point_sources: List[pyopensn.source.PointSource], default=[]
538
        A list of point sources.
539
    volumetric_sources: List[pyopensn.source.VolumetricSource], default=[]
540
        A list of volumetric sources.
541
    options : Dict, default={}
542
        A block of optional configuration parameters. See `SetOptions` for available settings.
543
    sweep_type : str, default="AAH"
544
        The sweep type to use. Must be one of `AAH` or `CBC`. Defaults to `AAH`.
545
    use_gpus : bool, default=False
546
        A flag specifying whether GPU acceleration is used for the sweep. Currently, only ``AAH`` is
547
        supported.
548
    )"
549
  );
550
  do_problem.def(
388✔
551
    "ComputeBalance",
552
    [](DiscreteOrdinatesProblem& self)
407✔
553
    {
554
      ComputeBalance(self);
19✔
555
    },
556
    R"(
557
    Compute and print particle balance for the problem.
558
    )"
559
  );
560
  do_problem.def(
388✔
561
    "ComputeLeakage",
562
    [](DiscreteOrdinatesProblem& self, py::list bnd_names)
407✔
563
    {
564
      // get the supported boundaries
565
      std::map<std::string, std::uint64_t> allowed_bd_names = LBSProblem::supported_boundary_names;
19✔
566
      std::map<std::uint64_t, std::string> allowed_bd_ids = LBSProblem::supported_boundary_ids;
19✔
567
      // get the boundaries to parse
568
      std::vector<std::uint64_t> bndry_ids;
19✔
569
      if (bnd_names.size() > 1)
19✔
570
      {
571
        for (py::handle name : bnd_names)
×
572
        {
573
          bndry_ids.push_back(allowed_bd_names.at(name.cast<std::string>()));
×
574
        }
575
      }
576
      else
577
      {
578
        bndry_ids = self.GetGrid()->GetUniqueBoundaryIDs();
57✔
579
      }
580
      // compute the leakage
581
      std::map<std::uint64_t, std::vector<double>> leakage = ComputeLeakage(self, bndry_ids);
19✔
582
      // convert result to native Python
583
      py::dict result;
19✔
584
      for (const auto& [bndry_id, gr_wise_leakage] : leakage)
57✔
585
      {
586
        py::array_t<double> np_vector = py::array_t<double>(gr_wise_leakage.size());
38✔
587
        py::buffer_info buffer = np_vector.request();
38✔
588
        auto np_vector_data = static_cast<double*>(buffer.ptr);
38✔
589
        std::copy(gr_wise_leakage.begin(), gr_wise_leakage.end(), np_vector_data);
38✔
590
        result[allowed_bd_ids.at(bndry_id).data()] = np_vector;
38✔
591
      }
38✔
592
      return result;
19✔
593
    },
19✔
594
    R"(
595
    Compute leakage for the problem.
596

597
    Parameters
598
    ----------
599
    bnd_names : List[str]
600
        A list of boundary names for which leakage should be computed.
601

602
    Returns
603
    -------
604
    Dict[str, numpy.ndarray]
605
        A dictionary mapping boundary names to group-wise leakage vectors.
606
        Each array contains the outgoing angular flux (per group) integrated over
607
        the corresponding boundary surface.
608

609
    Raises
610
    ------
611
    RuntimeError
612
        If `save_angular_flux` option was not enabled during problem setup.
613

614
    ValueError
615
        If one or more boundary ids are not present on the current mesh.
616
    )",
617
    py::arg("bnd_names")
388✔
618
  );
619

620
  // discrete ordinates curvilinear problem
621
  auto do_curvilinear_problem = py::class_<DiscreteOrdinatesCurvilinearProblem,
388✔
622
                                           std::shared_ptr<DiscreteOrdinatesCurvilinearProblem>,
623
                                           DiscreteOrdinatesProblem>(
624
    slv,
625
    "DiscreteOrdinatesCurvilinearProblem",
626
    R"(
627
    Base class for discrete ordinates problems in curvilinear geometry.
628

629
    Wrapper of :cpp:class:`opensn::DiscreteOrdinatesCurvilinearProblem`.
630
    )"
631
  );
388✔
632
  do_curvilinear_problem.def(
776✔
633
    py::init(
388✔
634
      [](py::kwargs& params)
8✔
635
      {
636
        return DiscreteOrdinatesCurvilinearProblem::Create(kwargs_to_param_block(params));
8✔
637
      }
638
    ),
639
    R"(
640
    Construct a discrete ordinates problem for curvilinear geometry.
641

642
    Warnings
643
    --------
644
       DiscreteOrdinatesCurvilinearProblem is **experimental** and should be used with caution!
645

646
    Parameters
647
    ----------
648
    mesh : MeshContinuum
649
        The spatial mesh.
650
    coord_system : int
651
        Coordinate system to use. Must be set to 2 (cylindrical coordinates).
652
    num_groups : int
653
        The total number of energy groups.
654
    groupsets : list of dict
655
        A list of input parameter blocks, each block provides the iterative properties for a
656
        groupset.
657
    xs_map : list of dict
658
        A list of mappings from block ids to cross-section definitions.
659
    scattering_order: int, default=0
660
        The level of harmonic expansion for the scattering source.
661
    boundary_conditions: List[Dict], default=[]
662
        A list containing tables for each boundary specification.
663
    point_sources: List[pyopensn.source.PointSource], default=[]
664
        A list of point sources.
665
    volumetric_sources: List[pyopensn.source.VolumetricSource], default=[]
666
        A list of volumetric sources.
667
    options : dict, optional
668
        A block of optional configuration parameters. See `SetOptions` for available settings.
669
    sweep_type : str, optional
670
        The sweep type to use. Must be one of `AAH` or `CBC`. Defaults to `AAH`.
671
    )"
672
  );
673
}
388✔
674

675
// Wrap steady-state solver
676
void
677
WrapSteadyState(py::module& slv)
388✔
678
{
679
  // clang-format off
680
  // steady state solver
681
  auto steady_state_solver = py::class_<SteadyStateSourceSolver, std::shared_ptr<SteadyStateSourceSolver>,
388✔
682
                                        Solver>(
683
    slv,
684
    "SteadyStateSourceSolver",
685
    R"(
686
    Steady state solver.
687

688
    Wrapper of :cpp:class:`opensn::SteadyStateSourceSolver`.
689
    )"
690
  );
388✔
691
  steady_state_solver.def(
776✔
692
    py::init(
388✔
693
      [](py::kwargs& params)
202✔
694
      {
695
        return SteadyStateSourceSolver::Create(kwargs_to_param_block(params));
202✔
696
      }
697
    ),
698
    R"(
699
    Construct a steady state solver.
700

701
    Parameters
702
    ----------
703
    pyopensn.solver.LBSProblem : LBSProblem
704
        Existing LBSProblem instance.
705
    )"
706
  );
707
  // clang-format on
708
}
388✔
709

710
// Wrap non-linear k-eigen solver
711
void
712
WrapNLKEigen(py::module& slv)
388✔
713
{
714
  // clang-format off
715
  // non-linear k-eigen solver
716
  auto non_linear_k_eigen_solver = py::class_<NonLinearKEigenSolver, std::shared_ptr<NonLinearKEigenSolver>,
388✔
717
                                              Solver>(
718
    slv,
719
    "NonLinearKEigenSolver",
720
    R"(
721
    Non-linear k-eigenvalue solver.
722

723
    Wrapper of :cpp:class:`opensn::NonLinearKEigenSolver`.
724
    )"
725
  );
388✔
726
  non_linear_k_eigen_solver.def(
776✔
727
    py::init(
388✔
728
      [](py::kwargs& params)
28✔
729
      {
730
        return NonLinearKEigenSolver::Create(kwargs_to_param_block(params));
28✔
731
      }
732
        ),
733
    R"(
734
    Construct a non-linear k-eigenvalue solver.
735

736
    Parameters
737
    ----------
738
    lbs_problem: pyopensn.solver.LBSProblem
739
        Existing LBSProblem instance.
740
    nl_abs_tol: float, default=1.0e-8
741
        Non-linear absolute tolerance.
742
    nl_rel_tol: float, default=1.0e-8
743
        Non-linear relative tolerance.
744
    nl_sol_tol: float, default=1.0e-50
745
        Non-linear solution tolerance.
746
    nl_max_its: int, default=50
747
        Non-linear algorithm maximum iterations.
748
    l_abs_tol: float, default=1.0e-8
749
        Linear absolute tolerance.
750
    l_rel_tol: float, default=1.0e-8
751
        Linear relative tolerance.
752
    l_div_tol: float, default=1.0e6
753
        Linear divergence tolerance.
754
    l_max_its: int, default=50
755
        Linear algorithm maximum iterations.
756
    l_gmres_restart_intvl: int, default=30
757
        GMRES restart interval.
758
    l_gmres_breakdown_tol: float, default=1.0e6
759
        GMRES breakdown tolerance.
760
    reset_phi0: bool, default=True
761
        If true, reinitializes scalar fluxes to 1.0.
762
    num_initial_power_iterations: int, default=0
763
        Number of initial power iterations before the non-linear solve.
764
    )"
765
  );
766
  non_linear_k_eigen_solver.def(
388✔
767
    "GetEigenvalue",
768
    &NonLinearKEigenSolver::GetEigenvalue,
388✔
769
    R"(
770
    Return the current k‑eigenvalue.
771
    )"
772
  );
773
  // clang-format on
774
}
388✔
775

776
// Wrap power iteration solvers
777
void
778
WrapPIteration(py::module& slv)
388✔
779
{
780
  // clang-format off
781
  // power iteration k-eigen solver
782
  auto pi_k_eigen_solver = py::class_<PowerIterationKEigenSolver, std::shared_ptr<PowerIterationKEigenSolver>,
388✔
783
                                      Solver>(
784
    slv,
785
    "PowerIterationKEigenSolver",
786
    R"(
787
    Power iteration k-eigenvalue solver.
788

789
    Wrapper of :cpp:class:`opensn::PowerIterationKEigenSolver`.
790
    )"
791
  );
388✔
792
  pi_k_eigen_solver.def(
776✔
793
    py::init(
388✔
794
      [](py::kwargs& params)
35✔
795
      {
796
        return PowerIterationKEigenSolver::Create(kwargs_to_param_block(params));
35✔
797
      }
798
    ),
799
    R"(
800
    Construct a power iteration k-eigen solver.
801

802
    Parameters
803
    ----------
804
    problem: pyopensn.solver.LBSProblem
805
        Existing DiscreteOrdinatesProblem instance.
806
    acceleration: pyopensn.solver.DiscreteOrdinatesKEigenAcceleration
807
        Optional DiscreteOrdinatesKEigenAcceleration instance for acceleration.
808
    max_iters: int, default = 1000
809
        Maximum power iterations allowed.
810
    k_tol: float, default = 1.0e-10
811
        Tolerance on the k-eigenvalue.
812
    reset_solution: bool, default=True
813
        If true, initialize flux moments to 1.0.
814
    reset_phi0: bool, default=True
815
        If true, reinitializes scalar fluxes to 1.0.
816
    )"
817
  );
818
  pi_k_eigen_solver.def(
388✔
819
    "GetEigenvalue",
820
    &PowerIterationKEigenSolver::GetEigenvalue,
388✔
821
    R"(
822
    Return the current k‑eigenvalue.
823
    )"
824
  );
825
  // clang-format on
826
}
388✔
827

828
// Wrap LBS solver
829
void
830
WrapDiscreteOrdinatesKEigenAcceleration(py::module& slv)
388✔
831
{
832
  // clang-format off
833
  // discrete ordinates k-eigen acceleration base
834
  auto acceleration = py::class_<DiscreteOrdinatesKEigenAcceleration,
388✔
835
                                 std::shared_ptr<DiscreteOrdinatesKEigenAcceleration>>(
836
    slv,
837
    "DiscreteOrdinatesKEigenAcceleration",
838
    R"(
839
    Base class for discrete ordinates k-eigenvalue acceleration methods.
840

841
    Wrapper of :cpp:class:`opensn::DiscreteOrdinatesKEigenAcceleration`.
842
    )"
843
  );
388✔
844
  // SCDSA acceleration
845
  auto scdsa_acceleration = py::class_<SCDSAAcceleration,
388✔
846
                                       std::shared_ptr<SCDSAAcceleration>,
847
                                       DiscreteOrdinatesKEigenAcceleration>(
848
    slv,
849
    "SCDSAAcceleration",
850
    R"(
851
    Construct an SCDSA accelerator for the power iteration k-eigenvalue solver.
852

853
    Wrapper of :cpp:class:`opensn::SCDSAAcceleration`.
854
    )"
855
  );
388✔
856
  scdsa_acceleration.def(
776✔
857
    py::init(
388✔
858
      [](py::kwargs& params)
8✔
859
      {
860
        return SCDSAAcceleration::Create(kwargs_to_param_block(params));
8✔
861
      }
862
    ),
863
    R"(
864
    SCDSA acceleration for the power iteration k-eigenvalue solver.
865

866
    Parameters
867
    ----------
868
    problem: pyopensn.solver.LBSProblem
869
        Existing DiscreteOrdinatesProblem instance.
870
    l_abs_tol: float, defauilt=1.0e-10
871
        Absolute residual tolerance.
872
    max_iters: int, default=100
873
        Maximum allowable iterations.
874
    verbose: bool, default=False
875
        If true, enables verbose output.
876
    petsc_options: str, default="ssss"
877
        Additional PETSc options.
878
    pi_max_its: int, default=50
879
        Maximum allowable iterations for inner power iterations.
880
    pi_k_tol: float, default=1.0e-10
881
        k-eigenvalue tolerance for the inner power iterations.
882
    sdm: str, default="pwld"
883
        Spatial discretization method to use for the diffusion solver. Valid choices are:
884
            - 'pwld' : Piecewise Linear Discontinuous
885
            - 'pwlc' : Piecewise Linear Continuous
886
    )"
887
  );
888
  // SMM acceleration
889
  auto smm_acceleration = py::class_<SMMAcceleration,
388✔
890
                                     std::shared_ptr<SMMAcceleration>,
891
                                     DiscreteOrdinatesKEigenAcceleration>(
892
    slv,
893
    "SMMAcceleration",
894
    R"(
895
    Construct an SMM accelerator for the power iteration k-eigenvalue solver.
896

897
    Wrapper of :cpp:class:`opensn::SMMAcceleration`.
898
    )"
899
  );
388✔
900
  smm_acceleration.def(
776✔
901
    py::init(
388✔
902
      [](py::kwargs& params)
4✔
903
      {
904
        return SMMAcceleration::Create(kwargs_to_param_block(params));
4✔
905
      }
906
    ),
907
    R"(
908
    SMM acceleration for the power iteration k-eigenvalue solver.
909

910
    Warnings
911
    --------
912
       SMM acceleration is **experimental** and should be used with caution!
913
       SMM accleration only supports problems with isotropic scattering.
914

915
    Parameters
916
    ----------
917
    problem: pyopensn.solver.LBSProblem
918
        Existing DiscreteOrdinatesProblem instance.
919
    l_abs_tol: float, defauilt=1.0e-10
920
        Absolute residual tolerance.
921
    max_iters: int, default=100
922
        Maximum allowable iterations.
923
    verbose: bool, default=False
924
        If true, enables verbose output.
925
    petsc_options: str, default="ssss"
926
        Additional PETSc options.
927
    pi_max_its: int, default=50
928
        Maximum allowable iterations for inner power iterations.
929
    pi_k_tol: float, default=1.0e-10
930
        k-eigenvalue tolerance for the inner power iterations.
931
    sdm: str, default="pwld"
932
        Spatial discretization method to use for the diffusion solver. Valid choices are:
933
            - 'pwld' : Piecewise Linear Discontinuous
934
            - 'pwlc' : Piecewise Linear Continuous
935
    )"
936
  );
937
  // clang-format on
938
}
388✔
939

940
// Wrap the solver components of OpenSn
941
void
942
py_solver(py::module& pyopensn)
34✔
943
{
944
  py::module slv = pyopensn.def_submodule("solver", "Solver module.");
34✔
945
  WrapProblem(slv);
34✔
946
  WrapSolver(slv);
34✔
947
  WrapLBS(slv);
34✔
948
  WrapSteadyState(slv);
34✔
949
  WrapNLKEigen(slv);
34✔
950
  WrapDiscreteOrdinatesKEigenAcceleration(slv);
34✔
951
  WrapPIteration(slv);
34✔
952
}
34✔
953

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