diff --git a/docs/source/io_formats/materials.rst b/docs/source/io_formats/materials.rst index 92b0165a43c..d0049b30312 100644 --- a/docs/source/io_formats/materials.rst +++ b/docs/source/io_formats/materials.rst @@ -49,8 +49,9 @@ Each ``material`` element can have the following attributes or sub-elements: ` is used. :density: - An element with attributes/sub-elements called ``value`` and ``units``. The - ``value`` attribute is the numeric value of the density while the ``units`` + An element with attributes/sub-elements called ``value``, ``units``, and + (optionally) ``value_timeseries``. The ``value`` attribute is the numeric + value of the density while the ``units`` can be "g/cm3", "kg/m3", "atom/b-cm", "atom/cm3", or "sum". The "sum" unit indicates that values appearing in ``ao`` or ``wo`` attributes for ```` and ```` sub-elements are to be interpreted as absolute nuclide/element @@ -59,14 +60,21 @@ Each ``material`` element can have the following attributes or sub-elements: a ``macroscopic`` quantity to indicate that the density is already included in the library and thus not needed here. However, if a value is provided for the ``value``, then this is treated as a number density multiplier on - the macroscopic cross sections in the multi-group data. This can be used, - for example, when perturbing the density slightly. + the macroscopic cross sections in the multi-group data. This can be used, + for example, when perturbing the density slightly. The ``value_timeseries`` + element indicates a material density changing in time. This can be used to + simulate various benchmark problems in kinetic simulations. The + ``value_timeseries`` attribute is assumed to use the same units specified + in the ``units`` attribute. *Default*: None .. note:: A ``macroscopic`` quantity can not be used in conjunction with a ``nuclide``, ``element``, or ``sab`` quantity. + .. note:: The ``value_timeseries`` attribute cannot be used with ``sum`` + densities. + :nuclide: An element with attributes/sub-elements called ``name``, and ``ao`` or ``wo``. The ``name`` attribute is the name of the cross-section for a diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index a50922b041e..0cf893b6287 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -343,6 +343,18 @@ the estimated eigenvalue. It has the following attributes/sub-elements: .. note:: See section on the :ref:`trigger` for more information. +------------------------------------- +```` Element +------------------------------------- + +The ```` element indicates whether to run a static or +time-dependent simulation (with no feedbacks). If this element is set to +"true", a kinetic simulation will be run; otherwise a static simulation is run. + + *Default*: false + +.. _kinetic_simulation: + --------------------------- ```` Element --------------------------- @@ -637,6 +649,25 @@ found in the :ref:`random ray user guide `. *Default*: 1.0 +If a kinetic simulation is active, two additional settings are available: + + :bd_order: + Order for backwards difference approximation used in numerically estimating + time derivatives. The backwards difference approximation is stable for + orders less than or equal to 6, and is relatively straightforward to + implement. The default value of 3 balances higher precision with speed. + + *Default*: 3 + + :time_derivative_method: + The method used to resolve the angular flux time-derivative in the time-dependent + characteristic equation. The ``isotropic`` method utilizes an isotropic approximation + to resolve the angular flux time derivative. The ``propagation`` method + uses a technique called Time Derivative Propagation to resolve the angular + flux time derivative. + + *Default*: ``isotropic`` + ---------------------------------- ```` Element ---------------------------------- @@ -1393,6 +1424,32 @@ despite not being bounded on both sides. .. _trace: +--------------------------------- +```` Element +--------------------------------- + +The ```` element can be used specify for how long and +at what resolution to run a kinetic simulation. This element has the +following attributes/sub-elements: + + :n_timesteps: + The number of timesteps. Must be specified by the user. + + :dt: + Time step size. Must be specified by the user. + + :timestep_units: + Time step units. Available options are ``ms`` for milliseconds, ``s`` for seconds, + and ``min`` for minutes. + + *Default*: ``s`` + + .. note:: This element is required if the :ref:`kinetic_simulation` is ``true``. + + *Default*: None + +.. _timestep_parameters: + ------------------- ```` Element ------------------- diff --git a/docs/source/io_formats/statepoint.rst b/docs/source/io_formats/statepoint.rst index 7d7765b849d..9573895e056 100644 --- a/docs/source/io_formats/statepoint.rst +++ b/docs/source/io_formats/statepoint.rst @@ -28,6 +28,10 @@ The current version of the statepoint file format is 18.2. 'continuous-energy' or 'multi-group'. - **run_mode** (*char[]*) -- Run mode used, either 'eigenvalue' or 'fixed source'. + - **solver_type** (*char[]*) -- Solver used, either 'monte carlo' or + 'random ray'. + - **kinetic_simulation** (*int*) -- Flag indicating whether a kinetic (1) + or static (0) simulation was run. - **n_particles** (*int8_t*) -- Number of particles used per generation. - **n_batches** (*int*) -- Number of batches to simulate. - **current_batch** (*int*) -- The number of batches already simulated. @@ -49,6 +53,10 @@ The current version of the statepoint file format is 18.2. combined estimate of k-effective. - **n_realizations** (*int*) -- Number of realizations for global tallies. + - **n_energy_groups** (*int*) -- Number of energy groups used if + **energy_mode** is 'multi-group'. + - **n_delay_groups** (*int*) -- Number of delay groups used if + **energy_mode** is 'multi-group'. - **global_tallies** (*double[][2]*) -- Accumulated sum and sum-of-squares for each global tally. - **source_bank** (Compound type) -- Source bank information for each @@ -197,3 +205,16 @@ All values are given in seconds and are measured on the master process. tally results and evaluating their statistics. - **writing statepoints** (*double*) -- Time spent writing statepoint files + + If random ray mode is used, the following times are also recorded: + - **source_update** (*double*) -- Time spent updating the neutron source. + - **precursor_update** (*double*) -- Time spent updating the precursors + (only written for kinetic simulations). + +**/timestep_data/** + +Time step information for kinetic simulation. All values are given in seconds. + +:Datasets: - **dt** (*double*) -- Length of the time step. + - **current_timestep** (*int*) -- Numbered time step. + - **current_time** (*double*) -- Simulated elapsed time. diff --git a/docs/source/methods/random_ray.rst b/docs/source/methods/random_ray.rst index 5e17316aa1c..318f9aa0484 100644 --- a/docs/source/methods/random_ray.rst +++ b/docs/source/methods/random_ray.rst @@ -87,6 +87,8 @@ derivations are reproduced here verbatim. Several extensions are also made to add clarity, particularly on the topic of OpenMC's treatment of cell volumes in the random ray solver. +.. _usersguide_moc: + ~~~~~~~~~~~~~~~~~~~~~~~~~ Method of Characteristics ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1059,6 +1061,315 @@ random ray and Monte Carlo, however. develop the scattering source by way of inactive batches before beginning active batches. +.. _usersguide_kinetic_random_ray: + +------------------ +Kinetic Random Ray +------------------ + +One can derive equations for time-dependent cases by following similar steps +shown in the derivation for the :ref:`Method of +Charactersistics`. This process stars with the time-dependent +form of neutron transport equation, + +.. math:: + :label: transport-td + + \frac{1}{v(E)} \frac{\partial}{\partial t} + \psi(\mathbf{r},\mathbf{\Omega},E,t) = -\mathbf{\Omega}\cdot + \nabla\psi(\mathbf{r},\mathbf{\Omega},E,t) - \Sigma_{t}(\mathbf{r}, E, + t)\psi(\mathbf{r},\mathbf{\Omega},E,t) + Q(\mathbf{r},\mathbf{\Omega},E,t) + +The neutron source, :math:`Q(\mathbf{r},\mathbf{\Omega},E,t)`, is composed of several +neutron producing sources: + +.. math:: + :label: source-td + + Q(\mathbf{r},\mathbf{\Omega},E,t) = F_{p}(\mathbf{r},E,t) + + S(\mathbf{r},\mathbf{\Omega},E,t) +D(\mathbf{r},E,t) + +where the prompt neutron source (:math:`F_{p}(\mathbf{r},E,t)`), the scattering +neutron source (:math:`S(\mathbf{r},\mathbf{\Omega},E,t)`), and the delayed +neutron source (:math:`D(\mathbf{r},E,t)`) are given by: + + +.. math:: + + \begin{align} + F_{p}(\mathbf{r},E,t) &= \frac{\chi^p(E)}{4\pi} \int_0^\infty \int_{4\pi} + \nu_{p}\Sigma_{f}(\mathbf{r},E^\prime,t) + \psi(\mathbf{r},\mathbf{\Omega}^\prime,E^\prime,t) \: + d\mathbf{\Omega}^\prime \: dE^\prime\nonumber\\ + S(\mathbf{r},\mathbf{\Omega},E,t) &= \int_0^\infty\int_{4\pi} + \Sigma_{s}(\mathbf{r},\mathbf{\Omega}^\prime\rightarrow\mathbf{\Omega},E^\prime\rightarrow + E,t) + \psi(\mathbf{r},\mathbf{\Omega}^\prime,E^\prime,t) \: + d\mathbf{\Omega}^\prime \: dE^\prime\nonumber\\ + D(\mathbf{r},E,t) &= \sum_m \frac{\chi^d_m(E)}{4\pi} \lambda_m + C_m(\mathbf{r}, t)\nonumber + \end{align} + +The :math:`C_m(\mathbf{r}, t)` term is the number of delayed neutron precursors of delayed +group :math:`m`, governed by + + +.. math:: + :label: dnp-eq + + \frac{\partial}{\partial t} C_m(\mathbf{r}, t) = \int_0^\infty\int_{4\pi} + \nu_{d,m} \Sigma_{f}(\mathbf{r},E^\prime,t) + \psi(\mathbf{r},\mathbf{\Omega}^\prime,E^\prime,t) \: + d\mathbf{\Omega}^\prime \: dE^\prime - \lambda_m C_m(\mathbf{r},t) + +The new variables in Equations :eq:`transport-td`, :eq:`source-td`, and +:eq:`dnp-eq` are + + +.. math:: + + \begin{align*} + v(E) &= \text{ neutron speed at energy $E$ [cm s$^{-1}$]},\\ + t &= \text{ time [s]},\\ + \beta &= \sum_m \beta_m = \text{ total delayed neutron fraction [-]},\\ + \nu_{p} &= (1-\beta) \nu = \text{ prompt neutron yield [-]}, \\ + \nu_{d,m} &= \beta_{m} \nu = \text{ delayed neutron yield [-]}, \\ + \chi^p(E) &= \text{ prompt fission neutron spectrum [-]},\\ + \chi^d_m(E) &= \text{ delayed fission neutron spectrum [-],},\\ + \lambda_m &= \text{ decay constant for precursor group $m$ [s$^{-1}$]},\\ + \beta_m &= \text{ delayed neutron fraction for precursor group $m$ [-]} + .\end{align*} + +Applying the characteristic transform to Equation :eq:`transport-td` yields the +time-dependent characteristic equation: + + +.. math:: + :label: char-td + + \frac{1}{v(E)} \frac{\partial}{\partial t} \psi(s,\mathbf{\Omega},E,t) = + -\frac{\partial}{\partial s} \psi(s,\mathbf{\Omega},E,t) - + \Sigma_{t}(s,E,t)\psi(s,\mathbf{\Omega},E,t) + Q(s,\mathbf{\Omega},E,t) + +A similar integrating factor is used to obtain a partial solution of the +time-dependent characteristic equation: + + +.. math:: + :label: moc_td_final_sub + + \psi(s,\mathbf{\Omega},E,t) = \psi(\mathbf{r}_0,\mathbf{\Omega},E,t) + e^{-\int_0^s ds^{\prime} \Sigma_t(s^{\prime},E,t)} + \mathcal{I}_{Q} - + \mathcal{I}_{\frac{\partial}{\partial t} \psi} + +where + + +.. math:: + + \begin{aligned} + \mathcal{I}_{Q} &= \int_0^{s} ds^{\prime\prime} + Q(s^{\prime\prime},\mathbf{\Omega},E,t) e^{\int_{s^{\prime\prime}}^{s'} + ds^{\prime} \Sigma_{t}(s^{\prime},E,t)}\\ + \mathcal{I}_{\frac{\partial}{\partial t} \psi} &= -\frac{1}{v(E)} + \int_0^{s} ds^{\prime\prime} \frac{\partial}{\partial t} + \psi(s^{\prime\prime},\mathbf{\Omega},E,t) e^{\int_{s^{\prime\prime}}^{s} + ds^{\prime} \Sigma_{t}(s^{\prime},E,t)} + \end{aligned} + +Rearranging terms in Equation :eq:`moc_td_final_sub` to solve in terms of +:math:`\Delta \psi_{r,g}(t) = \psi_{r,g}(0,t) - \psi_{r,g}(\ell_r, t)`, and +applying the same energy and spatial discretizations used for the static +case, results in a characteristic equation of the form yields + + +.. math:: + :label: moc_td_final + + \Delta \psi_{r,g}(t) = \psi_{r,g}(0,t) e^{-\Sigma_{t,i,g}(t) \ell_r} - \mathcal{I}_{Q} + \mathcal{I}_{\frac{\partial}{\partial t} \psi} + +The :math:`\mathcal{I}_Q` term is resolved by the analytic shape assumed for +the source term (flat, linear, quadratic, etc.). Applying a flat source approximation +to Equation :eq:`moc_td_final` results in a characteristic equation of the form: + + +.. math:: + :label: moc_td_final_flat + + \Delta \psi_{r,g}(t) = \left(\psi_{r,g}(0,t) - + \frac{Q_{i,g}(t)}{\Sigma_{t,i,g}(t)} \right) (1 - e^{-\Sigma_{t,i,g}(t) + \ell_r}) + \mathcal{I}_{\frac{\partial}{\partial t} \psi} + +The corresponding flat source term is + + +.. math:: + :label: source_td_final_flat + + Q_{i,g}(t) = \frac{1}{4\pi} \left(F_{p,i,g}(t) + S_{i,g}(t) + + D_{i,g}(t)\right) + + +where + +.. math:: + + \begin{align} + F_{p,i,g}(t) &= \chi_g^p\sum_{g'=1}^G + \nu_{p}\Sigma_{f,g',w}(t)\phi_{i,g'}(t)\nonumber\\ + S_{i,g}(t) &= \sum_{g'=1}^G + \Sigma_{s,i,g'\rightarrow g}(t) \phi_{i,g'}(t)\nonumber\\ + D_{i,g}(t) &= \sum_m \chi^d_{g,m} \lambda_m C_{i,m}(t)\nonumber + \end{align} + + +The same approximations applied to the precursor equation (Eq. :eq:`dnp-eq`) yields + + +.. math:: + :label: dnp-eq-final-flat + + \frac{\partial}{\partial t} C_{i,m}(t) = \sum_{g=1}^G + \nu_{d,m}\Sigma_{f,i,g}(t)\phi_{i,g}(t) - \lambda_m C_{i,m}(t) + +The most straightforward approach to resolve the +:math:`\mathcal{I}_{\frac{\partial}{\partial t} \psi}` term would be to +numerically estimate the derivative using a finite difference approximation. +This approach works for the Method of Characteristics and has been shown to +yield accurate answers (see `Hoffman `_), but requires storage of +angular fluxes which can be very expensive. Additionally, the stochastic +quadrature used in the random ray method ensures each time step has a different +spatial domain, making direct finite differencing of angular fluxes impossible. +Two alternative methods have been implemented into OpenMC: + +~~~~~~~~~~~~~~~~~~~~~~~ +Isotropic Approximation +~~~~~~~~~~~~~~~~~~~~~~~ +A commonly used approach to avoid storage of angular fluxes is to make an +isotropic approximation on the angular flux time derivative: + + +.. math:: + + \frac{\partial}{\partial t} \psi_{r,g}(s,t) \approx \frac{1}{4\pi} \frac{d}{d t} \phi_{g}(s,t) + + +This approximation incurs inaccuracies close to strongly absorbing regions due +to the strong flux anisotropies introduced by neutron absorption (i.e. no +neutrons will be emitted from the absorber). This will result in scalar flux +values higher than they should be near absorbing regions. The benefit is that +it is fast, requiring minimal new computation to numerically compute the scalar +flux time derivative. The isotropic approximation resolves the +:math:`\mathcal{I}_{\frac{\partial}{\partial t} \psi}` term to + + +.. math:: + :label: ical-isotropic + + \mathcal{I}_{\frac{\partial}{\partial t} \psi} \approx \frac{1}{4\pi v_g} + \frac{d}{d t} \phi_{i,g}(t) \frac{1 - e^{-\Sigma_{t,i,g}(t) \ell_r}}{\Sigma_{t,i,g}(t)} + +Applying Equation :eq:`ical-isotropic` to Equation :eq:`moc_td_final_flat` yields + + +.. math:: + :label: char-td-isotropic + + \Delta \psi(t) = \left(\psi_{r,g}(0, t) - \frac{Q_{i,g}(t)}{\Sigma_{t,i,g}(t)} + \frac{1}{4\pi v_g \Sigma_{t,i,g}(t)} \frac{d}{d t} \phi_{i,g}(t)\right) (1 - e^{-\Sigma_{t,i,g}(t) \ell_r}) + + +This approach was first applied to the random ray method by `Kraus +`_. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Source Derivative Propagation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Source Derivative Propagation is a method developed by `Hoffman +`_ to solve the angular flux storage issue while also providing +a higher fidelity approximation storing angular fluxes. This method works by +forming a characteristic equation for the *angular flux time-derivative* which +is solved in the same way as the standard characteristic equation. The +time-derivative characteristic equation provides an analytical solution for +:math:`\frac{d}{d t} \psi_{r,i}(s,t)`. The time derivative characteristic +equation is formed by taking the time derivative of Equation +:eq:`moc_td_final_flat` and assuming :math:`\frac{d}{d t} \Sigma_{t,i,g}(t) +\approx 0`. The resulting :math:`\frac{\partial^2}{\partial t^2} \psi_{r,g}(s,t)` term +can be resolved with a second-order isotropic approximation, yielding: + + +.. math:: + :label: char-td-derivative-sdp + + \Delta \frac{\partial}{\partial t} \psi_{r,g}(s,t) = + \left(\frac{\partial}{\partial t} \psi_{r,g}(0, t) - \frac{T^{1}_{i,g}(t)}{\Sigma_{t,i,g}(t)} \right) + (1 - e^{-\Sigma_{t,i,g}(t)s}) + +where + + +.. math:: + :label: operator-T1 + + T^{1}_{i,g}(t) \equiv \frac{d Q_{i,g}}{d t} + -\frac{1}{4\pi v_g}\frac{d^2 \phi_{i,g}}{d t^2} + +Equation :eq:`char-td-derivative-sdp` can be rearranged to solve for +:math:`\frac{\partial}{\partial t} \psi_{r,g}(s,t)` and used to solve +:math:`\mathcal{I}_{\frac{\partial}{\partial t} \psi}` as: + + +.. math:: + + \mathcal{I}_{\frac{\partial}{\partial t} \psi} = + \frac{1}{v_g}\frac{T^{1}_{i,g}(t)}{(\Sigma_{t,i,g}(t))^{2}} (1 - + e^{-\Sigma_{t,i,g}(t) s}) + + \frac{s}{v_g} \left(\frac{\partial}{\partial t} \psi_{r,g}(0, t) - \frac{T^{1}_{i,g}(t)}{\Sigma_{t,i,g}(t)} \right) e^{-\Sigma_{t,i,g}(t)s} + +which can be substituted into Equation :eq:`moc_td_final_flat` to get + + +.. math:: + :label: char-td-sdp + + \begin{aligned} + \Delta \psi(t) = \bigg(\psi_{r,g}(0, t) - \frac{Q_{i,g}(t)}{\Sigma{t,i,g}(t)} + &+ \frac{1}{v_g} \frac{T^{1}_{i,g}(t)}{(\Sigma_{t,i,g}(t))^{2}}\bigg) (1 - + e^{-\Sigma_t,i,g(t) \ell_r})\\ &+ \frac{s}{v_g} + \bigg(\frac{\partial}{\partial t} \psi_{r,g}(0, t) - + \frac{T^{1}_{i,g}(t)}{\Sigma_{t,i,g}(t)} \bigg) e^{-\Sigma_{t,i,g}(t)s} + \end{aligned} + + +Equations :eq:`char-td-derivative-sdp` and :eq:`char-td-sdp` are solved in +sequence during flux propagation. The avoidance of a first-order isotropic +approximation may make Source Derivative Propagation more accurate in problems +with strongly absorbing regions. + + +~~~~~~~~~~~~~~~~~~ +Initial Conditions +~~~~~~~~~~~~~~~~~~ +An initial condition for a kinetic simulation may be obtained obtained by +running a static eigenvalue simulation. To bake-in the initial steady +state, the prompt fission source in the total source term (Eq. +:eq:`source_td_final_flat`) and the delayed fission source in the precursor +integration (Eq. :eq:`dnp-eq-final-flat`) are divided by +:math:`k_\text{eff}`. `Kraus `_ found that utilizing a single +value of :math:`k_\text{eff}` for the initial condition results in a biased +flux. This bias is due applying :math:`k_\text{eff}` computed on the initial +quadrature to source terms on a subsequent (different) quadrature. There are +two approaches to resolving this bias: + +1. Recomputing :math:`k_\text{eff}`: This approach recomputed + :math:`k_\text{eff}` of the unperturbed system used to scale the fission + source on the current quadrature. +2. Time-consistent seed: This approach resets the PRNG seed at the beginning of + each time step so the resulting quadrature is the same in each time step. + +The time-consistent seed approach results in lower variance and shorter run +times than the :math:`k_\text{eff}` recomputation approach, so is the approach +used in OpenMC's implementation of the time-dependent Random Ray Method. + .. _adjoint: ------------------------ @@ -1158,6 +1469,8 @@ in random ray particle transport are: .. _Cosgrove-2023: https://doi.org/10.1080/00295639.2023.2270618 .. _Ferrer-2016: https://doi.org/10.13182/NSE15-6 .. _Gunow-2018: https://dspace.mit.edu/handle/1721.1/119030 +.. _Hoffman-2013: https://doi.org/10.1016/j.jcp.2015.10.039 +.. _Kraus-2025: https://doi.org/10.1080/00295639.2025.2456413 .. only:: html diff --git a/docs/source/usersguide/index.rst b/docs/source/usersguide/index.rst index 5f8e0197e70..12e5555cfb9 100644 --- a/docs/source/usersguide/index.rst +++ b/docs/source/usersguide/index.rst @@ -29,4 +29,5 @@ essential aspects of using OpenMC to perform simulations. volume variance_reduction random_ray + kinetic troubleshoot diff --git a/docs/source/usersguide/kinetic.rst b/docs/source/usersguide/kinetic.rst new file mode 100644 index 00000000000..ec32c82d590 --- /dev/null +++ b/docs/source/usersguide/kinetic.rst @@ -0,0 +1,90 @@ +.. _random_ray: + +=================== +Kinetic Simulations +=================== + +Much of OpenMC's existing infrastructure assumes a system is at a steady state. +Users interested in reactor transients should use the kinetic simulation +capability. Kinetic simulations can be enabled as:: + + settings.kinetic_simulation = True + +Kinetic simulations require the user to specify time step settings using the +``settings.timestep_parameters`` object. As an example, a 10 second long +simulation using 1 millisecond time steps can be set as:: + + settings.timestep_parameters = {'n_timesteps': 1000, + 'dt': 1, + 'timestep_units': 'ms'} + +.. note:: + When running kineic simulations, OpenMC will generate a statepoint file for + each time step named ``openmc_td_simulation_{i}.h5``, where ``i`` is the + index of the time step. + +Currently, only material density transients can be simulated. A density +transient can be specified using the Python API:: + + water = openmc.Material(name='water') + + # Linear ramp transient + densities = np.linspace(1, 0.95, 1000) + + water.set_density('macro', 1.0, density_timeseries=densities) + +.. note:: + Kinetic simulations are currently only supported in eigenvalue mode using the Random Ray solver. + +---------------------- +Random Ray Quick Start +---------------------- + +Random Ray's :ref:`automatic setup workflow ` can +be utilized to quickly generate models for kinetic simulations, however +slight modifications are needed: + +1. The `kinetic` flag should be set to `True`, and the number of delay groups must be + specified via `num_delay_groups` in the call to :meth:`openmc.Model.convert_to_multigroup`. + Take care to not specify more delay groups than the cross section library you + are using supports, as this will cause all sorts of problems when you try to run + the simulation. +2. By default, a time step size of 1 ms is chosen to accurately resolve delayed + neutron precursor dynamics, but the number of time steps must be manually + set in `openmc.Settings.timestep_parameters['n_timesteps']`. +3. Material density timeseries' must be manually added. + +An example process of converting an existing continuous energy +Monte Carlo model to a random ray model for kinetic simulations +is show below:: + + # Define continuous energy model as normal + model = openmc.Model() + ... + + # Convert model to kinetic multigroup (will auto-generate MGXS library if needed) + # Most cross-section libraries support 6 delay groups + model.convert_to_multigroup(kinetic=True, num_delayed_groups=6) + + # Add required manual kinetic simulation settigns + model.settings.timestep_parameters['n_timesteps'] = 5 + + # Add a material density timeseries to material 0 in the model + density_timeseries = np.linspace(1, 0.95, 100) + model.materials[0].set_density('macro', density=1.0, density_timeseries=density_timeseries) + + # Convert model to random ray and initialize random ray parameters + # to reasonable defaults based on the specifics of the geometry + model.convert_to_random_ray() + + # (Optional) Overlay source region decomposition mesh to improve fidelity of the + # random ray solver. Adjust 'n' for fidelity vs runtime. + n = 100 + mesh = openmc.RegularMesh() + mesh.dimension = (n, n, n) + mesh.lower_left = model.geometry.bounding_box.lower_left + mesh.upper_right = model.geometry.bounding_box.upper_right + model.settings.random_ray['source_region_meshes'] = [(mesh, [model.geometry.root_universe])] + + # (Optional) Increase the number of rays/batch, to reduce uncertainty + model.settings.particles = 500 diff --git a/docs/source/usersguide/random_ray.rst b/docs/source/usersguide/random_ray.rst index 0c9a0402814..2ccb8c53310 100644 --- a/docs/source/usersguide/random_ray.rst +++ b/docs/source/usersguide/random_ray.rst @@ -641,7 +641,7 @@ model to use these multigroup cross sections. An example is given below:: # Assume we already have a working continuous energy model model.convert_to_multigroup( method="material_wise", - groups="CASMO-2", + energy_groups="CASMO-2", nparticles=2000, overwrite_mgxs_library=False, mgxs_path="mgxs.h5", @@ -710,6 +710,11 @@ generation and use an existing library file. with a :math:`\rho` default value of 1.0, which can be adjusted with the ``settings.random_ray['diagonal_stabilization_rho']`` parameter. +.. note:: + Cross sections for kinetic simulations may be generating by passing the + ``kinetic`` parameter as ``True``. Delay groups may be specified with an additional + parameter, ``num_delayed_group``, which by default is zero. + When generating MGXS data with either the ``stochastic_slab`` or ``infinite_medium`` methods, by default the simulation will use a uniform source distribution spread evenly over all energy groups. This ensures that all energy diff --git a/include/openmc/constants.h b/include/openmc/constants.h index 0b425a673dc..30bdac27238 100644 --- a/include/openmc/constants.h +++ b/include/openmc/constants.h @@ -287,6 +287,7 @@ enum class MgxsType { PROMPT_NU_FISSION, DELAYED_NU_FISSION, NU_FISSION, + CHI, CHI_PROMPT, CHI_DELAYED }; @@ -328,7 +329,10 @@ enum TallyScore { SCORE_PULSE_HEIGHT = -17, // pulse-height SCORE_IFP_TIME_NUM = -18, // IFP lifetime numerator SCORE_IFP_BETA_NUM = -19, // IFP delayed fraction numerator - SCORE_IFP_DENOM = -20 // IFP common denominator + SCORE_IFP_DENOM = -20, // IFP common denominator + SCORE_PRECURSORS = -21 // delyaed neutron precursor concentration + // + }; // Global tally parameters @@ -368,6 +372,7 @@ enum class SolverType { MONTE_CARLO, RANDOM_RAY }; enum class RandomRayVolumeEstimator { NAIVE, SIMULATION_AVERAGED, HYBRID }; enum class RandomRaySourceShape { FLAT, LINEAR, LINEAR_XY }; enum class RandomRaySampleMethod { PRNG, HALTON, S2 }; +enum class RandomRayTimeMethod { ISOTROPIC, PROPAGATION }; //============================================================================== // Geometry Constants diff --git a/include/openmc/material.h b/include/openmc/material.h index 3967c36878f..0a61d8fe53f 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -208,6 +208,10 @@ class Material { unique_ptr ttb_; + // Time dependent data + vector + density_timeseries_; //!< Total atom density timeseries in [atom/b-cm] + private: //---------------------------------------------------------------------------- // Private methods diff --git a/include/openmc/random_ray/bd_utilities.h b/include/openmc/random_ray/bd_utilities.h new file mode 100644 index 00000000000..785ea061a75 --- /dev/null +++ b/include/openmc/random_ray/bd_utilities.h @@ -0,0 +1,80 @@ +#ifndef OPENMC_RANDOM_RAY_BD_UTILITIES_H +#define OPENMC_RANDOM_RAY_BD_UTILITIES_H + +#include +#include +#include + +#include "openmc/error.h" +#include "openmc/vector.h" + +namespace openmc { + +//---------------------------------------------------------------------------- +// Helper Variables +// Coefficients come from Table 3 in Fornberg (1988) +// DOI: 10.1090/S0025-5718-1988-0935077-0 +// Note that the signs are flipped compared to the citation, as the author was +// formulating weights for a forward difference +const std::map> bd_coefficients_first_order_ = { + {1, {1.0, -1.0}}, {2, {1.5, -2.0, 0.5}}, + {3, {1.833333333333333, -3.0, 1.5, -0.333333333333333}}, + {4, {2.083333333333333, -4.0, 3.0, -1.333333333333333, 0.25}}, + {5, {2.283333333333333, -5.0, 5.0, -3.333333333333333, 1.25, -0.2}}, + {6, {2.45, -6.0, 7.5, -6.666666666666667, 3.75, -1.2, 0.166666666666667}}}; + +// Coefficients come from Table 3 in Fornberg (1988) +// DOI: 10.1090/S0025-5718-1988-0935077-0 +const std::map> bd_coefficients_second_order_ = { + {1, {1.0, -2.0, 1.0}}, {2, {2.0, -5.0, 4, -1}}, + {3, {2.916666666666667, -8.666666666666667, 9.5, -4.666666666666667, + 0.916666666666667}}, + {4, {3.75, -12.833333333333333, 17.833333333333333, -13.0, 5.083333333333333, + -0.833333333333333}}, + {5, {4.511111111111111, -17.4, 29.25, -28.222222222222222, 16.5, -5.4, + 0.761111111111111}}, + {6, {5.211111111111111, -22.3, 43.95, -52.722222222222222, 41.0, -20.1, + 5.661111111111111, -0.7}}}; + +// Take RHS derivative to solve for the current timestep +template +T rhs_backwards_difference( + std::deque& bd_vector, int bd_order, double dt, int derivative_order = 1) +{ + vector bd_coeffs; + int n_bd_terms; + double time_factor; + if (derivative_order == 1) { + bd_coeffs = bd_coefficients_first_order_.at(bd_order); + time_factor = 1 / dt; + n_bd_terms = bd_order; + } else if (derivative_order == 2) { + bd_coeffs = bd_coefficients_second_order_.at(bd_order); + n_bd_terms = bd_order + 1; + time_factor = 1 / (dt * dt); + } else { + fatal_error("Only first or second order bd derivatives are allowed."); + } + T rhs_bd = 0.0; + for (int i = 0; i < n_bd_terms; i++) + rhs_bd += bd_coeffs[i + 1] * bd_vector[i]; + rhs_bd *= time_factor; + return rhs_bd; +} + +template +void add_value_to_bd_vector(std::deque& bd_vector, T& new_value, + bool increment_not_initialize, int initialize_size) +{ + bd_vector.push_front(new_value); + if (increment_not_initialize) { + bd_vector.pop_back(); + } else { + for (int i = 1; i < initialize_size; i++) + bd_vector.push_front(new_value); + } +} + +} // namespace openmc + +#endif // OPENMC_RANDOM_RAY_BD_UTILITIES_H diff --git a/include/openmc/random_ray/flat_source_domain.h b/include/openmc/random_ray/flat_source_domain.h index c40982712eb..4412a78e274 100644 --- a/include/openmc/random_ray/flat_source_domain.h +++ b/include/openmc/random_ray/flat_source_domain.h @@ -39,6 +39,7 @@ class FlatSourceDomain { void reset_tally_volumes(); void random_ray_tally(); virtual void accumulate_iteration_flux(); + void accumulate_iteration_source(); void output_to_vtk() const; void convert_external_sources(); void count_external_source_regions(); @@ -49,6 +50,7 @@ class FlatSourceDomain { void flatten_xs(); void transpose_scattering_matrix(); void serialize_final_fluxes(vector& flux); + void serialize_final_sources(vector& source); void apply_meshes(); void apply_mesh_to_cell_instances(int32_t i_cell, int32_t mesh_idx, int target_material_id, const vector& instances, @@ -72,6 +74,32 @@ class FlatSourceDomain { int64_t lookup_mesh_bin(int64_t sr, Position r) const; int lookup_mesh_idx(int64_t sr) const; + //---------------------------------------------------------------------------- + // Methods for kinetic simulations + void compute_single_phi_prime(SourceRegionHandle& srh); + void compute_single_T1(SourceRegionHandle& srh); + + void compute_single_delayed_fission_source(SourceRegionHandle& srh); + void compute_single_precursors(SourceRegionHandle& srh); + void compute_all_precursors(); + + void serialize_final_precursors(vector& precursors); + void serialize_final_delayed_fission_source( + vector& delayed_fission_source); + + void precursors_swap(); + void accumulate_iteration_quantities(); + void normalize_final_quantities(); + void propagate_final_quantities(); + void store_time_step_quantities(bool increment_not_initialize = true); + void compute_rhs_bd_quantities(); + void update_material_density(int i); + + int64_t n_delay_elements() const + { + return source_regions_.n_source_regions() * ndgroups_; + } + //---------------------------------------------------------------------------- // Static Data members static bool volume_normalized_flux_tallies_; @@ -90,7 +118,10 @@ class FlatSourceDomain { //---------------------------------------------------------------------------- // Public Data members - double k_eff_ {1.0}; // Eigenvalue + double k_eff_ {1.0}; // Eigenvalue + double + fission_rate_; // The system's fission rate (per cm^3), in eigenvalue mode + bool mapped_all_tallies_ {false}; // If all source regions have been visited int64_t n_external_source_regions_ {0}; // Total number of source regions with @@ -151,6 +182,24 @@ class FlatSourceDomain { // technique. bool is_transport_stabilization_needed_ {false}; + //--------------------------------------------------------------------------- + // Public Data Members for kinetic simulations + + // 2D arrays stored in 1D representing values for all materials x + // delay_groups + vector lambda_; + + // 3D arrays stored in 1D representing values for all materials x energy + // groups x delay groups + vector nu_d_sigma_f_; + vector chi_d_lambda_; // chi-delayed * lambda in each delay group + + // 2D arrays stored in 1D representing values for all materials x energy + // groups + vector nu_p_sigma_f_; + vector chi_p_; + vector inverse_vbar_; + protected: //---------------------------------------------------------------------------- // Methods @@ -167,14 +216,12 @@ class FlatSourceDomain { //---------------------------------------------------------------------------- // Private data members int negroups_; // Number of energy groups in simulation + int ndgroups_; // Number of delay groups in simulation double simulation_volume_; // Total physical volume of the simulation domain, as // defined by the 3D box of the random ray source - double - fission_rate_; // The system's fission rate (per cm^3), in eigenvalue mode - // Volumes for each tally and bin/score combination. This intermediate data // structure is used when tallying quantities that must be normalized by // volume (i.e., flux). The vector is index by tally index, while the inner 2D diff --git a/include/openmc/random_ray/random_ray.h b/include/openmc/random_ray/random_ray.h index b61d2d67aa8..51d96f643dc 100644 --- a/include/openmc/random_ray/random_ray.h +++ b/include/openmc/random_ray/random_ray.h @@ -51,12 +51,32 @@ class RandomRay : public Particle { static RandomRaySourceShape source_shape_; // Flag for linear source static RandomRaySampleMethod sample_method_; // Flag for sampling method + static double avg_miss_rate_; // Average ray miss rate per + // iteration for reporting + static int64_t n_source_regions_; // Total number of source regions + static int64_t + n_external_source_regions_; // Total number of source regions with + // non-zero external source terms + static uint64_t total_geometric_intersections_; // Tracks the total number of + // geometric intersections by + // all rays for reporting + + // Kinetic simulation variables + static int bd_order_; // Order of backwards difference approximation for + // time-derivatives + static RandomRayTimeMethod + time_method_; // Method for resolving angular flux time derivative term for + // flux propogation + //---------------------------------------------------------------------------- // Public data members vector angular_flux_; - bool ray_trace_only_ {false}; // If true, only perform geometry operations + //--------------------------------------------------------------------------- + // Public data members for kinetic simulations + vector angular_flux_prime_; + private: //---------------------------------------------------------------------------- // Private data members @@ -72,6 +92,7 @@ class RandomRay : public Particle { double distance_travelled_ {0}; bool is_active_ {false}; bool is_alive_ {true}; + }; // class RandomRay } // namespace openmc diff --git a/include/openmc/random_ray/random_ray_simulation.h b/include/openmc/random_ray/random_ray_simulation.h index 68c7779edad..0d64de8e239 100644 --- a/include/openmc/random_ray/random_ray_simulation.h +++ b/include/openmc/random_ray/random_ray_simulation.h @@ -23,12 +23,12 @@ class RandomRaySimulation { void prepare_fixed_sources_adjoint(); void prepare_adjoint_simulation(); void simulate(); + void initialize_time_step(int i); + void finalize_time_step(); void output_simulation_results() const; void instability_check( int64_t n_hits, double k_eff, double& avg_miss_rate) const; - void print_results_random_ray(uint64_t total_geometric_intersections, - double avg_miss_rate, int negroups, int64_t n_source_regions, - int64_t n_external_source_regions) const; + void print_results_random_ray() const; //---------------------------------------------------------------------------- // Accessors @@ -57,9 +57,19 @@ class RandomRaySimulation { // Number of energy groups int negroups_; + // Number of delay groups + int ndgroups_; + // Toggle for first simulation bool is_first_simulation_; + //---------------------------------------------------------------------------- + // Data Members for kinetic simulations + + double static_avg_k_eff_; + vector static_k_eff_; + vector static_fission_rate_; + }; // class RandomRaySimulation //============================================================================ @@ -70,6 +80,16 @@ void validate_random_ray_inputs(); void print_adjoint_header(); void openmc_finalize_random_ray(); +//! Write data related to randaom ray to statepoint +//! \param[in] group HDF5 group +void write_random_ray_hdf5(hid_t group); +void print_adjoint_header(); + +// Functions for kinetic simulations +void set_time_dependent_settings(); +void rename_time_step_file( + std::string base_filename, std::string extension, int i); + } // namespace openmc #endif // OPENMC_RANDOM_RAY_SIMULATION_H diff --git a/include/openmc/random_ray/source_region.h b/include/openmc/random_ray/source_region.h index 65f2e6bc41c..7789c4a7f95 100644 --- a/include/openmc/random_ray/source_region.h +++ b/include/openmc/random_ray/source_region.h @@ -1,6 +1,8 @@ #ifndef OPENMC_RANDOM_RAY_SOURCE_REGION_H #define OPENMC_RANDOM_RAY_SOURCE_REGION_H +#include + #include "openmc/openmp_interface.h" #include "openmc/position.h" #include "openmc/random_ray/moment_matrix.h" @@ -141,6 +143,7 @@ class SourceRegionHandle { //---------------------------------------------------------------------------- // Public Data members int negroups_; + int ndgroups_; bool is_numerical_fp_artifact_ {false}; bool is_linear_ {false}; @@ -178,6 +181,7 @@ class SourceRegionHandle { double* scalar_flux_old_; double* scalar_flux_new_; float* source_; + float* source_final_; float* external_source_; double* scalar_flux_final_; @@ -191,6 +195,39 @@ class SourceRegionHandle { // associated with it, necessitating the use of a jagged array. vector* tally_task_; + //--------------------------------------------------------------------------- + // Public Data Members for kinetic simulations + + // Energy group-wise 1D time-derivative arrays + double* phi_prime_; + double* T1_; + + // Delay group-wise 1D arrays + double* delayed_fission_source_; + double* precursors_old_; + double* precursors_new_; + double* precursors_final_; + + // Energy group-wise 2D BD arrays (g x time step) + std::deque* scalar_flux_bd_; + std::deque* source_bd_; + + // Delay group-wise 2D BD arrays (dg x time step) + std::deque* precursors_bd_; + + // Energy group-wise 1D RHS BD arrays + double* scalar_flux_rhs_bd_; + float* source_rhs_bd_; + double* scalar_flux_rhs_bd_2_; + + // Delay group-wise 1D RHS BD arrays + double* precursors_rhs_bd_; + + // 2D array representing values for all delay groups x tally + // tasks. Each group may have a different number of tally tasks + // associated with it, necessitating the use of a jagged array. + vector* tally_delay_task_; + //---------------------------------------------------------------------------- // Public Accessors @@ -282,6 +319,9 @@ class SourceRegionHandle { float& source(int g) { return source_[g]; } const float source(int g) const { return source_[g]; } + float& source_final(int g) { return source_final_[g]; } + const float source_final(int g) const { return source_final_[g]; } + float& external_source(int g) { return external_source_[g]; } const float external_source(int g) const { return external_source_[g]; } @@ -309,13 +349,78 @@ class SourceRegionHandle { vector& tally_task(int g) { return tally_task_[g]; } const vector& tally_task(int g) const { return tally_task_[g]; } + //--------------------------------------------------------------------------- + // Public Accessors for kinetic simulations + double& phi_prime(int g) { return phi_prime_[g]; } + const double phi_prime(int g) const { return phi_prime_[g]; } + + double& T1(int g) { return T1_[g]; } + const double T1(int g) const { return T1_[g]; } + + double& delayed_fission_source(int dg) { return delayed_fission_source_[dg]; } + const double delayed_fission_source(int dg) const + { + return delayed_fission_source_[dg]; + } + + double& precursors_old(int dg) { return precursors_old_[dg]; } + const double precursors_old(int dg) const { return precursors_old_[dg]; } + + double& precursors_new(int dg) { return precursors_new_[dg]; } + const double precursors_new(int dg) const { return precursors_new_[dg]; } + + double& precursors_final(int dg) { return precursors_final_[dg]; } + const double precursors_final(int dg) const { return precursors_final_[dg]; } + + std::deque& scalar_flux_bd(int g) { return scalar_flux_bd_[g]; } + const std::deque& scalar_flux_bd(int g) const + { + return scalar_flux_bd_[g]; + } + + std::deque& source_bd(int g) { return source_bd_[g]; } + const std::deque& source_bd(int g) const { return source_bd_[g]; } + + std::deque& precursors_bd(int dg) { return precursors_bd_[dg]; } + const std::deque& precursors_bd(int dg) const + { + return precursors_bd_[dg]; + } + + double& scalar_flux_rhs_bd(int g) { return scalar_flux_rhs_bd_[g]; } + const double scalar_flux_rhs_bd(int g) const + { + return scalar_flux_rhs_bd_[g]; + } + + float& source_rhs_bd(int g) { return source_rhs_bd_[g]; } + const float source_rhs_bd(int g) const { return source_rhs_bd_[g]; } + + double& scalar_flux_rhs_bd_2(int g) { return scalar_flux_rhs_bd_2_[g]; } + const double scalar_flux_rhs_bd_2(int g) const + { + return scalar_flux_rhs_bd_2_[g]; + } + + double& precursors_rhs_bd(int dg) { return precursors_rhs_bd_[dg]; } + const double precursors_rhs_bd(int dg) const + { + return precursors_rhs_bd_[dg]; + } + + vector& tally_delay_task(int dg) { return tally_delay_task_[dg]; } + const vector& tally_delay_task(int dg) const + { + return tally_delay_task_[dg]; + } + }; // class SourceRegionHandle class SourceRegion { public: //---------------------------------------------------------------------------- // Constructors - SourceRegion(int negroups, bool is_linear); + SourceRegion(int negroups, int ndgroups, bool is_linear); SourceRegion() = default; //---------------------------------------------------------------------------- @@ -373,7 +478,9 @@ class SourceRegion { vector external_source_; //!< The external source term vector scalar_flux_final_; //!< The scalar flux accumulated over all //!< active iterations (used for plotting, - //!< or computing adjoint sources) + //!< computing adjoint sources, or + //!< computing an initial condition for a + //!< kinetic simulation) vector source_gradients_; //!< The linear source gradients vector @@ -389,14 +496,84 @@ class SourceRegion { // tasks. Each group may have a different number of tally tasks // associated with it, necessitating the use of a jagged array. vector> tally_task_; + + //---------------------------------------------------------------------------- + // Public Data Members for kinetic simulations + + // Energy group-wise 1D time-dependent arrrays + vector source_final_; //!< The total source accumulated over all + //!< active iterations (used for SDP) + + // Energy group-wise 1D derivative arrays + vector + phi_prime_; //!< The 1st order scalar flux time derivative (used for TI) + vector T1_; //!< The combined source time derivative and 2nd order + //!< scalar flux time derivative (used for SDP) + // Delay group-wise 1D arrays + vector delayed_fission_source_; //!< The delayed fission source binned + //!< by delay group + vector precursors_old_; //!< The precursor density from the previous + //!< iteration. + vector + precursors_new_; //!< The precursor density for the current iteration + vector + precursors_final_; //!< The precursor density accumulated over all + //!< active iterations (used for computing + //!< the time derivative of precursor population) + + // Energy group-wise 2D BD arrays (g x time step) + vector> + scalar_flux_bd_; //!< The final scalar flux in each energy group from a + //!< finite number of previous time steps (used for + //!< computing the first order (and second order, for SDP) + //!< scalar flux time derivative) + vector> + source_bd_; //!< The final scalar flux in each energy group from a + //!< finite number of previous time steps (used for + //!< computing the first order source time derivative for + //!< SDP) + + // Delay group-wise 2D BD arrays (dg x time step) + vector> + precursors_bd_; //!< The final precursor population in each energy group + //!< from a finite number of previous time steps (used for + //!< computing the first order precursor time derivative) + + // Energy group-wise 1D RHS BD arrays + vector + scalar_flux_rhs_bd_; //!< RHS dervative for the scalar flux from previous + //!< timesteps. Used to compute the total scalar flux + //!< time derivative for both TI and SDP time-dependent + //!< simulations + vector source_rhs_bd_; //!< RHS derivative for the neutron source from + //!< previous timesteps Used for compute the + //!< total neutron source derivative for SDP + vector + scalar_flux_rhs_bd_2_; //!< 2nd order RHS derivative for the scalar flux + //!< from previous timesteps. Used to compute the + //!< total 2nd order scalar flux time derivative for + //!< SDP. + + // Delay group-wise 1D RHS BD arrays + vector + precursors_rhs_bd_; //!< RHS derivative for the precursors from previous + //!< timesteps. Used to compute the total precursor time + //!< derivative for solving the precursor equation using + //!< backwards differences. + + // 2D array representing values for all delay groups x tally + // tasks. Each group may have a different number of tally tasks + // associated with it, necessitating the use of a jagged array. + vector> tally_delay_task_; + }; // class SourceRegion class SourceRegionContainer { public: //---------------------------------------------------------------------------- // Constructors - SourceRegionContainer(int negroups, bool is_linear) - : negroups_(negroups), is_linear_(is_linear) + SourceRegionContainer(int negroups, int ndgroups, bool is_linear) + : negroups_(negroups), ndgroups_(ndgroups), is_linear_(is_linear) {} SourceRegionContainer() = default; @@ -577,6 +754,14 @@ class SourceRegionContainer { float& source(int64_t se) { return source_[se]; } const float source(int64_t se) const { return source_[se]; } + float& source_final(int64_t sr, int g) { return source_final_[index(sr, g)]; } + const float source_final(int64_t sr, int g) const + { + return source_final_[index(sr, g)]; + } + float& source_final(int64_t se) { return source_final_[se]; } + const float source_final(int64_t se) const { return source_final_[se]; } + float& external_source(int64_t sr, int g) { return external_source_[index(sr, g)]; @@ -618,6 +803,186 @@ class SourceRegionContainer { int64_t& parent_sr(int64_t sr) { return parent_sr_[sr]; } const int64_t parent_sr(int64_t sr) const { return parent_sr_[sr]; } + //--------------------------------------- + // For kinetic simulations + double& phi_prime(int64_t sr, int g) { return phi_prime_[index(sr, g)]; } + const double& phi_prime(int64_t sr, int g) const + { + return phi_prime_[index(sr, g)]; + } + double& phi_prime(int64_t se) { return phi_prime_[se]; } + const double& phi_prime(int64_t se) const { return phi_prime_[se]; } + + double& T1(int64_t sr, int g) { return T1_[index(sr, g)]; } + const double& T1(int64_t sr, int g) const { return T1_[index(sr, g)]; } + double& T1(int64_t se) { return T1_[se]; } + const double& T1(int64_t se) const { return T1_[se]; } + + double& precursors_old(int64_t sr, int dg) + { + return precursors_old_[dindex(sr, dg)]; + } + const double& precursors_old(int64_t sr, int dg) const + { + return precursors_old_[dindex(sr, dg)]; + } + double& precursors_old(int64_t de) { return precursors_old_[de]; } + const double& precursors_old(int64_t de) const { return precursors_old_[de]; } + + double& precursors_new(int64_t sr, int dg) + { + return precursors_new_[dindex(sr, dg)]; + } + const double& precursors_new(int64_t sr, int dg) const + { + return precursors_new_[dindex(sr, dg)]; + } + double& precursors_new(int64_t de) { return precursors_new_[de]; } + const double& precursors_new(int64_t de) const { return precursors_new_[de]; } + + double& precursors_final(int64_t sr, int dg) + { + return precursors_final_[dindex(sr, dg)]; + } + const double& precursors_final(int64_t sr, int dg) const + { + return precursors_final_[dindex(sr, dg)]; + } + double& precursors_final(int64_t de) { return precursors_final_[de]; } + const double& precursors_final(int64_t de) const + { + return precursors_final_[de]; + } + + double& delayed_fission_source(int64_t sr, int dg) + { + return delayed_fission_source_[dindex(sr, dg)]; + } + const double& delayed_fission_source(int64_t sr, int dg) const + { + return delayed_fission_source_[dindex(sr, dg)]; + } + double& delayed_fission_source(int64_t de) + { + return delayed_fission_source_[de]; + } + const double& delayed_fission_source(int64_t de) const + { + return delayed_fission_source_[de]; + } + + std::deque& scalar_flux_bd(int64_t sr, int g) + { + return scalar_flux_bd_[index(sr, g)]; + } + const std::deque& scalar_flux_bd(int64_t sr, int g) const + { + return scalar_flux_bd_[index(sr, g)]; + } + std::deque& scalar_flux_bd(int64_t se) { return scalar_flux_bd_[se]; } + const std::deque& scalar_flux_bd(int64_t se) const + { + return scalar_flux_bd_[se]; + } + + std::deque& source_bd(int64_t sr, int g) + { + return source_bd_[index(sr, g)]; + } + const std::deque& source_bd(int64_t sr, int g) const + { + return source_bd_[index(sr, g)]; + } + std::deque& source_bd(int64_t se) { return source_bd_[se]; } + const std::deque& source_bd(int64_t se) const + { + return source_bd_[se]; + } + + std::deque& precursors_bd(int64_t sr, int dg) + { + return precursors_bd_[dindex(sr, dg)]; + } + const std::deque& precursors_bd(int64_t sr, int dg) const + { + return precursors_bd_[dindex(sr, dg)]; + } + std::deque& precursors_bd(int64_t de) { return precursors_bd_[de]; } + const std::deque& precursors_bd(int64_t de) const + { + return precursors_bd_[de]; + } + + double& scalar_flux_rhs_bd(int64_t sr, int g) + { + return scalar_flux_rhs_bd_[index(sr, g)]; + } + const double& scalar_flux_rhs_bd(int64_t sr, int g) const + { + return scalar_flux_rhs_bd_[index(sr, g)]; + } + double& scalar_flux_rhs_bd(int64_t se) { return scalar_flux_rhs_bd_[se]; } + const double& scalar_flux_rhs_bd(int64_t se) const + { + return scalar_flux_rhs_bd_[se]; + } + + double& precursors_rhs_bd(int64_t sr, int dg) + { + return precursors_rhs_bd_[dindex(sr, dg)]; + } + const double& precursors_rhs_bd(int64_t sr, int dg) const + { + return precursors_rhs_bd_[dindex(sr, dg)]; + } + double& precursors_rhs_bd(int64_t de) { return precursors_rhs_bd_[de]; } + const double& precursors_rhs_bd(int64_t de) const + { + return precursors_rhs_bd_[de]; + } + + float& source_rhs_bd(int64_t sr, int g) + { + return source_rhs_bd_[index(sr, g)]; + } + const float& source_rhs_bd(int64_t sr, int g) const + { + return source_rhs_bd_[index(sr, g)]; + } + float& source_rhs_bd(int64_t se) { return source_rhs_bd_[se]; } + const float& source_rhs_bd(int64_t se) const { return source_rhs_bd_[se]; } + + double& scalar_flux_rhs_bd_2(int64_t sr, int g) + { + return scalar_flux_rhs_bd_2_[index(sr, g)]; + } + const double& scalar_flux_rhs_bd_2(int64_t sr, int g) const + { + return scalar_flux_rhs_bd_2_[index(sr, g)]; + } + double& scalar_flux_rhs_bd_2(int64_t se) { return scalar_flux_rhs_bd_2_[se]; } + const double& scalar_flux_rhs_bd_2(int64_t se) const + { + return scalar_flux_rhs_bd_2_[se]; + } + + vector& tally_delay_task(int64_t sr, int dg) + { + return tally_delay_task_[dindex(sr, dg)]; + } + const vector& tally_delay_task(int64_t sr, int dg) const + { + return tally_delay_task_[dindex(sr, dg)]; + } + vector& tally_delay_task(int64_t de) + { + return tally_delay_task_[de]; + } + const vector& tally_delay_task(int64_t de) const + { + return tally_delay_task_[de]; + } + //---------------------------------------------------------------------------- // Public Methods @@ -631,13 +996,25 @@ class SourceRegionContainer { bool& is_linear() { return is_linear_; } const bool is_linear() const { return is_linear_; } SourceRegionHandle get_source_region_handle(int64_t sr); + void simulation_reset(); void adjoint_reset(); + //--------------------------------------------------------------------------- + // Public Methods for kinetic simulations + + int64_t n_delay_elements() const { return n_source_regions_ * ndgroups_; } + int& ndgroups() { return ndgroups_; } + const int ndgroups() const { return ndgroups_; } + + void precursors_swap(); + void time_step_reset(); + private: //---------------------------------------------------------------------------- // Private Data Members int64_t n_source_regions_ {0}; int negroups_ {0}; + int ndgroups_ {0}; bool is_linear_ {false}; // SoA storage for scalar fields (one item per source region) @@ -672,6 +1049,7 @@ class SourceRegionContainer { vector scalar_flux_new_; vector scalar_flux_final_; vector source_; + vector source_final_; vector external_source_; vector source_gradients_; @@ -686,11 +1064,47 @@ class SourceRegionContainer { // dimension. vector> tally_task_; + //--------------------------------------------------------------------------- + // Private Data Members for kinetic simulations + + // SoA energy group-wise 2D derivative arrays flattened to 1D + vector phi_prime_; + vector T1_; + + // SoA delay group-wise 2D arrays flattened to 1D + vector delayed_fission_source_; + vector precursors_old_; + vector precursors_new_; + vector precursors_final_; + + // SoA energy group-wise 3D BD arrays (sr x g X timestep) flattened to 2D + vector> scalar_flux_bd_; + vector> source_bd_; + + // SoA delay group-wise 3D BD arrays (sr x dg X timestep) flattened to 2D + vector> precursors_bd_; + + // SoA energy group-wise 2D RHS BD arrays flattened to 1D + vector scalar_flux_rhs_bd_; + vector source_rhs_bd_; + vector scalar_flux_rhs_bd_2_; + + // SoA delay group-wise 2D RHS BD arrays flattened to 1D + vector precursors_rhs_bd_; + + // SoA 3D array representing values for all source regions x delay groups x + // tally tasks. The outer two dimensions (source regions and delay groups) + // are flattened to 1D. Each group may have a different number of tally tasks + // associated with it, necessitating the use of a jagged array for the inner + // dimension. + vector> tally_delay_task_; + //---------------------------------------------------------------------------- // Private Methods // Helper function for indexing inline int index(int64_t sr, int g) const { return sr * negroups_ + g; } + inline int dindex(int64_t sr, int dg) const { return sr * ndgroups_ + dg; } }; } // namespace openmc diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 0914a0958b8..10e81fc32e3 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -71,6 +71,8 @@ extern "C" bool entropy_on; //!< calculate Shannon entropy? extern "C" bool event_based; //!< use event-based mode (instead of history-based) extern bool ifp_on; //!< Use IFP for kinetics parameters? +extern bool kinetic_simulation; //!< Flag determining if the simulation is + //!< kinetic or static extern bool legendre_to_tabular; //!< convert Legendre distributions to tabular? extern bool material_cell_offsets; //!< create material cells offsets? extern "C" bool output_summary; //!< write summary.h5? @@ -203,6 +205,9 @@ extern "C" int verbosity; //!< How verbose to make output extern double weight_cutoff; //!< Weight cutoff for Russian roulette extern double weight_survive; //!< Survival weight after Russian roulette +// Timestep variables for kinetic simulation +extern int n_timesteps; //!< number of timesteps +extern double dt; //!< fixed timestep size } // namespace settings //============================================================================== diff --git a/include/openmc/simulation.h b/include/openmc/simulation.h index 9a6cf1b2131..2e310726fb8 100644 --- a/include/openmc/simulation.h +++ b/include/openmc/simulation.h @@ -49,6 +49,15 @@ extern const RegularMesh* ufs_mesh; extern vector k_generation; extern vector work_index; +//----------------------------------------------------------------------------- +// Global variables for kinetic simulations +extern bool + is_initial_condition; //!< if eigenvalue/fixed source sim is an initial + //!< condition for a kinetic simulation +extern int current_timestep; // !< current time step in kinetic simulation +extern double current_time; // !< current time in kinetic simulation +extern bool k_eff_correction; // !< flag to indicate if the simulation is meant + // to correct batchwise k_effs } // namespace simulation //============================================================================== diff --git a/include/openmc/timer.h b/include/openmc/timer.h index d928aad4560..bcb2a7e644a 100644 --- a/include/openmc/timer.h +++ b/include/openmc/timer.h @@ -33,6 +33,9 @@ extern Timer time_event_collision; extern Timer time_event_death; extern Timer time_update_src; +extern Timer time_update_bd_vectors; +extern Timer time_compute_precursors; + } // namespace simulation //============================================================================== diff --git a/include/openmc/volume_calc.h b/include/openmc/volume_calc.h index ef75ec0653c..9d3f1d02615 100644 --- a/include/openmc/volume_calc.h +++ b/include/openmc/volume_calc.h @@ -34,7 +34,7 @@ class VolumeCalculation { vector atoms; //!< Number of atoms for each nuclide vector uncertainty; //!< Uncertainty on number of atoms int iterations; //!< Number of iterations needed to obtain the results - }; // Results for a single domain + }; // Results for a single domain // Constructors VolumeCalculation(pugi::xml_node node); diff --git a/include/openmc/weight_windows.h b/include/openmc/weight_windows.h index 42846f9d140..1d943e383eb 100644 --- a/include/openmc/weight_windows.h +++ b/include/openmc/weight_windows.h @@ -188,8 +188,8 @@ class WeightWindows { vector energy_bounds_; //!< Energy boundaries [eV] tensor::Tensor lower_ww_; //!< Lower weight window bounds (shape: //!< energy_bins, mesh_bins (k, j, i)) - tensor::Tensor - upper_ww_; //!< Upper weight window bounds (shape: energy_bins, mesh_bins) + tensor::Tensor upper_ww_; //!< Upper weight window bounds (shape: + //!< energy_bins, mesh_bins) double survival_ratio_ {3.0}; //!< Survival weight ratio double max_lb_ratio_ {1.0}; //!< Maximum lower bound to particle weight ratio double weight_cutoff_ {DEFAULT_WEIGHT_CUTOFF}; //!< Weight cutoff diff --git a/include/openmc/xsdata.h b/include/openmc/xsdata.h index c9dbde986b2..aaa964b9c76 100644 --- a/include/openmc/xsdata.h +++ b/include/openmc/xsdata.h @@ -83,6 +83,9 @@ class XsData { // delayed_nu_fission has the following dimensions: // [angle][delayed group][incoming group] tensor::Tensor delayed_nu_fission; + // chi has the following dimensions: + // [angle][incoming group][outgoing group] + tensor::Tensor chi; // chi_prompt has the following dimensions: // [angle][incoming group][outgoing group] tensor::Tensor chi_prompt; diff --git a/openmc/examples.py b/openmc/examples.py index f7f2bd48da1..519cfc1e4fd 100644 --- a/openmc/examples.py +++ b/openmc/examples.py @@ -4,7 +4,8 @@ import openmc -PINCELL_PITCH = 1.26 # cm +C5G7_N_DG = 8 +PINCELL_PITCH = 1.26 def pwr_pin_cell() -> openmc.Model: """Create a PWR pin-cell model. @@ -656,7 +657,7 @@ def slab_mg(num_regions=1, mat_names=None, mgxslib_name='2g.h5') -> openmc.Model return model -def _generate_c5g7_materials(second_temp = False) -> openmc.Materials: +def _generate_c5g7_materials(second_temp = False, kinetic = False) -> openmc.Materials: """Generate materials utilizing multi-group cross sections based on the the C5G7 Benchmark. @@ -668,6 +669,8 @@ def _generate_c5g7_materials(second_temp = False) -> openmc.Materials: of 294 K. The second data point is the C5G7 cross sections multiplied by 1/2, which corresponds to a temperature of 394 K. This temperature dependence is fictitious; it is used for testing temperature feedback in the random ray solver. + kinetic : bool, optional + Flag to generate cross sections for kinetic simulations. Returns ------- @@ -676,7 +679,14 @@ def _generate_c5g7_materials(second_temp = False) -> openmc.Materials: Data Sources ------------ - All cross section data are from: + Prompt and delated fission cross sections, prompt and delayed fission + spectra, decay constants, delayed neutron fractions, and velocity data come + from: + Hou et al., "OECD/NEA benchmark for time-dependnet neutron + transport calculations without homogeniztaion" + DOI: 10.1016/j.nucengdes.2017.02.008 + + All other cross section data are from: Lewis et al., "Benchmark specification for determinisitc 2D/3D MOX fuel assembly transport calculations without spatial homogenization" """ @@ -685,6 +695,13 @@ def _generate_c5g7_materials(second_temp = False) -> openmc.Materials: group_edges = [1e-5, 0.0635, 10.0, 1.0e2, 1.0e3, 0.5e6, 1.0e6, 20.0e6] groups = openmc.mgxs.EnergyGroups(group_edges) + # Number of delayed groups for a time-dependent simulation + # Delayed cross section values + # come from Hou et al., "OECD/NEA benchmark for time-dependnet neutron + # transport calculations without homogeniztaion" + # DOI: 10.1016/j.nucengdes.2017.02.008 + n_dg = C5G7_N_DG if kinetic else 0 + uo2_total = np.array([0.1779492, 0.3298048, 0.4803882, 0.5543674, 0.3118013, 0.3951678, 0.5644058]) uo2_abs = np.array([8.0248e-03, 3.7174e-03, 2.6769e-02, 9.6236e-02, 3.0020e-02, @@ -721,7 +738,7 @@ def _generate_c5g7_materials(second_temp = False) -> openmc.Materials: h2o_scatter_matrix = np.rollaxis(h2o_scatter_matrix, 0, 3) # Instantiate the 7-group (C5G7) cross section data - uo2_xsdata = openmc.XSdata('UO2', groups) + uo2_xsdata = openmc.XSdata('UO2', groups, num_delayed_groups=n_dg) uo2_xsdata.order = 0 uo2_xsdata.set_total( [0.1779492, 0.3298048, 0.4803882, 0.5543674, 0.3118013, 0.3951678, @@ -759,7 +776,65 @@ def _generate_c5g7_materials(second_temp = False) -> openmc.Materials: uo2_xsdata.set_nu_fission(uo2_nu_fission, temperature=294.0) uo2_xsdata.set_chi(uo2_chi, temperature=294.0) - h2o_xsdata = openmc.XSdata('LWTR', groups) + # Delayed and prompt cross sections for time-dependent simulation + if kinetic: + + # Table A2 in Hou et. al + beta = np.array([[2.13333e-04, 2.13333e-04, 2.13333e-04, 2.13333e-04, + 2.13333e-04, 2.13333e-04, 2.13333e-04], + [1.04514e-03, 1.04514e-03, 1.04514e-03, 1.04514e-03, + 1.04514e-03, 1.04514e-03, 1.04514e-03], + [6.03969e-04, 6.03969e-04, 6.03969e-04, 6.03969e-04, + 6.03969e-04, 6.03969e-04, 6.03969e-04], + [1.33963e-03, 1.33963e-03, 1.33963e-03, 1.33963e-03, + 1.33963e-03, 1.33963e-03, 1.33963e-03], + [2.29386e-03, 2.29386e-03, 2.29386e-03, 2.29386e-03, + 2.29386e-03, 2.29386e-03, 2.29386e-03], + [7.05174e-04, 7.05174e-04, 7.05174e-04, 7.05174e-04, + 7.05174e-04, 7.05174e-04, 7.05174e-04], + [6.00381e-04, 6.00381e-04, 6.00381e-04, 6.00381e-04, + 6.00381e-04, 6.00381e-04, 6.00381e-04], + [2.07736e-04, 2.07736e-04, 2.07736e-04, 2.07736e-04, + 2.07736e-04, 2.07736e-04, 2.07736e-04]]) + # the actual tot is 7.009223e-03 + beta_tot = 7.00922e-03 + + # Table A2 in Hou et. al + uo2_xsdata.set_decay_rate([1.247e-02, 2.829e-02, 4.252e-02, + 1.330e-01, 2.925e-01, 6.665e-01, + 1.635e+00, 3.555e+00]) + # Derived from manipulating eq. B-3 in Hou et al. + # chi_prompt = (chi - np.sum(chi_delayed * beta, 0)) / (1 - beta_tot) + uo2_xsdata.set_chi_prompt([5.91741e-01, 4.07977e-01, 2.91169e-04, + 1.18440e-07, 0.00000e+00, 0.00000e+00, + 0.00000e+00]) + # Table A3 in Hou et al. + chi_delayed = np.array([[0.00075, 0.98512, 0.01413, 0.0000e+00, + 0.0000e+00, 0.0000e+00, 0.0000e+00], + [0.03049, 0.96907, 0.00044, 0.0000e+00, + 0.0000e+00, 0.0000e+00, 0.0000e+00], + [0.00457, 0.97401, 0.02142, 0.0000e+00, + 0.0000e+00, 0.0000e+00, 0.0000e+00], + [0.02002, 0.97271, 0.00727, 0.0000e+00, + 0.0000e+00, 0.0000e+00, 0.0000e+00], + [0.05601, 0.93818, 0.00581, 0.0000e+00, + 0.0000e+00, 0.0000e+00, 0.0000e+00], + [0.06098, 0.93444, 0.00458, 0.0000e+00, + 0.0000e+00, 0.0000e+00, 0.0000e+00], + [0.10635, 0.88298, 0.01067, 0.0000e+00, + 0.0000e+00, 0.0000e+00, 0.0000e+00], + [0.09346, 0.9026, 0.00394, 0.0000e+00, + 0.0000e+00, 0.0000e+00, 0.0000e+00]]) + uo2_xsdata.set_chi_delayed(chi_delayed) + # Table A4 in Hou et al. + velocities = np.array([2.23466e+09, 5.07347e+08, 3.86595e+07, + 5.13931e+06, 1.67734e+06, 7.28603e+05, 2.92902e+05]) + uo2_xsdata.set_inverse_velocity(1 / velocities) + # We need to set these so XsData::fission_matrix_beta_from_hdf5 is set + uo2_xsdata.set_prompt_nu_fission((1 - beta_tot) * nu_fission) + uo2_xsdata.set_delayed_nu_fission(beta * nu_fission) + + h2o_xsdata = openmc.XSdata('LWTR', groups, num_delayed_groups=n_dg) h2o_xsdata.order = 0 h2o_xsdata.set_total(h2o_total, temperature=294.0) h2o_xsdata.set_absorption(h2o_abs, temperature=294.0) @@ -780,7 +855,13 @@ def _generate_c5g7_materials(second_temp = False) -> openmc.Materials: h2o_xsdata.set_absorption(0.5 * h2o_abs, temperature=394.0) h2o_xsdata.set_scatter_matrix(0.5 * h2o_scatter_matrix, temperature=394.0) - mg_cross_sections = openmc.MGXSLibrary(groups) + if kinetic: + # Table A4 in Hou et al. + velocities = np.array([2.23517E+09, 4.98880E+08, 3.84974E+07, + 5.12639E+06, 1.67542E+06, 7.26031E+05, 2.81629E+05]) + h2o_xsdata.set_inverse_velocity(1 / velocities) + + mg_cross_sections = openmc.MGXSLibrary(groups, num_delayed_groups=n_dg) mg_cross_sections.add_xsdatas([uo2_xsdata, h2o_xsdata]) mg_cross_sections.export_to_hdf5('mgxs.h5') @@ -792,8 +873,13 @@ def _generate_c5g7_materials(second_temp = False) -> openmc.Materials: uo2.set_density('macro', 1.0) uo2.add_macroscopic('UO2') + if kinetic: + densities = np.linspace(1, 0.95, 100) + else: + densities = None + water = openmc.Material(name='Water') - water.set_density('macro', 1.0) + water.set_density('macro', 1.0, densities) water.add_macroscopic('LWTR') # Instantiate a Materials collection and export to XML @@ -872,9 +958,8 @@ def _generate_subdivided_pin_cell(uo2, water) -> openmc.Universe: return pincell -def random_ray_pin_cell(second_temp = False) -> openmc.Model: +def random_ray_pin_cell(second_temp = False, kinetic = False) -> openmc.Model: """Create a PWR pin cell example using C5G7 cross section data. - cross section data. Parameters ---------- @@ -884,6 +969,9 @@ def random_ray_pin_cell(second_temp = False) -> openmc.Model: of 294 K. The second data point is the C5G7 cross sections multiplied by 1/2, which corresponds to a temperature of 3934 K. This temperature dependence is fictitious; it is used for testing temperature feedback in the random ray solver. + kinetic : bool, optional + Flag to generate kinetic simulation model or not. + Returns ------- @@ -895,7 +983,7 @@ def random_ray_pin_cell(second_temp = False) -> openmc.Model: ########################################################################### # Create Materials for the problem - materials = _generate_c5g7_materials(second_temp) + materials = _generate_c5g7_materials(second_temp, kinetic) uo2 = materials[0] water = materials[1] @@ -933,6 +1021,14 @@ def random_ray_pin_cell(second_temp = False) -> openmc.Model: settings.random_ray['distance_inactive'] = 20.0 settings.random_ray['ray_source'] = rr_source settings.random_ray['volume_normalized_flux_tallies'] = True + if kinetic: + settings.random_ray['bd_order'] = 3 + settings.kinetic_simulation = True + settings.timestep_parameters = { + "dt": 0.01, + "n_timesteps": 20, + "timestep_units": "s", + } ########################################################################### # Define tallies @@ -944,6 +1040,13 @@ def random_ray_pin_cell(second_temp = False) -> openmc.Model: # Instantiate a Tallies collection and export to XML tallies = openmc.Tallies([tally]) + if kinetic: + delay_filter = openmc.DelayedGroupFilter(np.arange(1, C5G7_N_DG+1, 1)) + tally = openmc.Tally(name="Delayed tally") + tally.filters = [delay_filter] + tally.scores += ['precursors'] + tallies.append(tally) + ########################################################################### # Exporting to OpenMC model ########################################################################### @@ -955,7 +1058,7 @@ def random_ray_pin_cell(second_temp = False) -> openmc.Model: return model -def random_ray_lattice(second_temp = False) -> openmc.Model: +def random_ray_lattice(second_temp = False, kinetic = False) -> openmc.Model: """Create a 2x2 PWR pin cell asymmetrical lattice example. This model is a 2x2 reflective lattice of fuel pins with one of the lattice @@ -970,6 +1073,8 @@ def random_ray_lattice(second_temp = False) -> openmc.Model: of 294 K. The second data point is the C5G7 cross sections multiplied by 1/2, which corresponds to a temperature of 3934 K. This temperature dependence is fictitious; it is used for testing temperature feedback in the random ray solver. + kinetic : bool, optional + Flag to generate a kinetic simulation model or not. Returns ------- @@ -981,7 +1086,7 @@ def random_ray_lattice(second_temp = False) -> openmc.Model: ########################################################################### # Create Materials for the problem - materials = _generate_c5g7_materials(second_temp) + materials = _generate_c5g7_materials(second_temp, kinetic) uo2 = materials[0] water = materials[1] @@ -993,15 +1098,22 @@ def random_ray_lattice(second_temp = False) -> openmc.Model: # Define a moderator lattice universe moderator_infinite = openmc.Cell(name='moderator infinite') - moderator_infinite.fill = water + if kinetic: + water_reflector = water.clone() + water_reflector.name = 'Water Reflector' + water_reflector.set_density('macro', 1.0) + materials.append(water_reflector) + moderator_infinite.fill = water_reflector + else: + moderator_infinite.fill = water mu = openmc.Universe() mu.add_cells([moderator_infinite]) pitch = PINCELL_PITCH lattice = openmc.RectLattice() - lattice.lower_left = [-pitch/2.0, -pitch/2.0] - lattice.pitch = [pitch/10.0, pitch/10.0] + lattice.lower_left = [-PINCELL_PITCH/2.0, -PINCELL_PITCH/2.0] + lattice.pitch = [PINCELL_PITCH/10.0, PINCELL_PITCH/10.0] lattice.universes = np.full((10, 10), mu) mod_lattice_cell = openmc.Cell(fill=lattice) @@ -1013,8 +1125,8 @@ def random_ray_lattice(second_temp = False) -> openmc.Model: ######################################## # Define 2x2 outer lattice lattice2x2 = openmc.RectLattice() - lattice2x2.lower_left = (-pitch, -pitch) - lattice2x2.pitch = (pitch, pitch) + lattice2x2.lower_left = (-PINCELL_PITCH, -PINCELL_PITCH) + lattice2x2.pitch = (PINCELL_PITCH, PINCELL_PITCH) lattice2x2.universes = [ [pincell, pincell], [pincell, mod_lattice_uni] @@ -1040,8 +1152,8 @@ def random_ray_lattice(second_temp = False) -> openmc.Model: settings.particles = 100 # Create an initial uniform spatial source distribution over fissionable zones - lower_left = (-pitch, -pitch, -1) - upper_right = (pitch, pitch, 1) + lower_left = (-PINCELL_PITCH, -PINCELL_PITCH, -1) + upper_right = (PINCELL_PITCH, PINCELL_PITCH, 1) uniform_dist = openmc.stats.Box(lower_left, upper_right) rr_source = openmc.IndependentSource(space=uniform_dist) @@ -1049,6 +1161,14 @@ def random_ray_lattice(second_temp = False) -> openmc.Model: settings.random_ray['distance_inactive'] = 20.0 settings.random_ray['ray_source'] = rr_source settings.random_ray['volume_normalized_flux_tallies'] = True + if kinetic: + settings.random_ray['bd_order'] = 3 + settings.kinetic_simulation = True + settings.timestep_parameters = { + "dt": 0.01, + "n_timesteps": 2, + "timestep_units": "s", + } ########################################################################### # Define tallies @@ -1056,8 +1176,8 @@ def random_ray_lattice(second_temp = False) -> openmc.Model: # Create a mesh that will be used for tallying mesh = openmc.RegularMesh() mesh.dimension = (2, 2) - mesh.lower_left = (-pitch, -pitch) - mesh.upper_right = (pitch, pitch) + mesh.lower_left = (-PINCELL_PITCH, -PINCELL_PITCH) + mesh.upper_right = (PINCELL_PITCH, PINCELL_PITCH) # Create a mesh filter that can be used in a tally mesh_filter = openmc.MeshFilter(mesh) @@ -1075,6 +1195,13 @@ def random_ray_lattice(second_temp = False) -> openmc.Model: # Instantiate a Tallies collection and export to XML tallies = openmc.Tallies([tally]) + if kinetic: + delay_filter = openmc.DelayedGroupFilter(np.arange(1, C5G7_N_DG+1, 1)) + tally = openmc.Tally(name="Mesh delayed tally") + tally.filters = [mesh_filter, delay_filter] + tally.scores += ['precursors'] + tallies.append(tally) + ########################################################################### # Exporting to OpenMC model ########################################################################### diff --git a/openmc/material.py b/openmc/material.py index 2dbe691f525..d8f469c9121 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -92,6 +92,12 @@ class Material(IDManagerMixin): Temperature of the material in Kelvin. density : float Density of the material (units defined separately) + density_timeseries : list of float + Density timeseries of the material for time-dependent simulations. Units + assumed to be the same as `density_units`. Must be have size equal to + :attr:`openmc.Settings.time_dependent['n_timesteps']`. + + .. versionadded:: 0.16.0 density_units : str Units used for `density`. Can be one of 'g/cm3', 'g/cc', 'kg/m3', 'atom/b-cm', 'atom/cm3', 'sum', or 'macro'. The 'macro' unit only @@ -140,6 +146,7 @@ def __init__( name: str = "", temperature: float | None = None, density: float | None = None, + density_timeseries: list[float] | None = None, density_units: str = "sum", depletable: bool | None = False, volume: float | None = None, @@ -151,6 +158,7 @@ def __init__( self.name = name self.temperature = temperature self._density = None + self._density_timeseries = None self._density_units = density_units self._depletable = depletable self._paths = None @@ -178,7 +186,6 @@ def __init__( if components is not None: self.add_components(components, percent_type=percent_type) - def __repr__(self) -> str: string = 'Material\n' string += '{: <16}=\t{}\n'.format('\tID', self._id) @@ -188,13 +195,19 @@ def __repr__(self) -> str: string += '{: <16}=\t{}'.format('\tDensity', self._density) string += f' [{self._density_units}]\n' + if self._density_timeseries is not None: + string += '{: <16}\n'.format('\tDensity Timeseries') + dens_ts_string = " ".join(str(x) for x in self._density_timeseries) + string += '{: <16}\n'.format(dens_ts_string) + string += '{: <16}=\t{} [cm^3]\n'.format('\tVolume', self._volume) string += '{: <16}=\t{}\n'.format('\tDepletable', self._depletable) string += '{: <16}\n'.format('\tS(a,b) Tables') if self._ncrystal_cfg: - string += '{: <16}=\t{}\n'.format('\tNCrystal conf', self._ncrystal_cfg) + string += '{: <16}=\t{}\n'.format('\tNCrystal conf', + self._ncrystal_cfg) for sab in self._sab: string += '{: <16}=\t{}\n'.format('\tS(a,b)', sab) @@ -238,6 +251,10 @@ def temperature(self, temperature: Real | None): def density(self) -> float | None: return self._density + @property + def density_timeseries(self) -> list[str] | None: + return self._density_timeseries + @property def density_units(self) -> str: return self._density_units @@ -321,7 +338,7 @@ def fissionable_mass(self) -> float: Z = openmc.data.zam(nuc)[0] if Z >= 90: density += 1e24 * atoms_per_bcm * openmc.data.atomic_mass(nuc) \ - / openmc.data.AVOGADRO + / openmc.data.AVOGADRO return density*self.volume @property @@ -370,7 +387,8 @@ def get_decay_photon_energy( cv.check_value('units', units, {'Bq', 'Bq/g', 'Bq/kg', 'Bq/cm3', 'Bq/m3'}) if exclude_nuclides is not None and include_nuclides is not None: - raise ValueError("Cannot specify both exclude_nuclides and include_nuclides") + raise ValueError( + "Cannot specify both exclude_nuclides and include_nuclides") if units == 'Bq': multiplier = volume if volume is not None else self.volume @@ -636,6 +654,7 @@ def from_hdf5(cls, group: h5py.Group) -> Material: material.add_s_alpha_beta(name) # Set the Material's density to atom/b-cm as used by OpenMC + # TODO: Add support for density_timeseries material.set_density(density=density, units='atom/b-cm') if 'nuclides' in group: @@ -686,12 +705,12 @@ def from_ncrystal(cls, cfg, **kwargs) -> Material: nc_mat = NCrystal.createInfo(cfg) def openmc_natabund(Z): - #nc_mat.getFlattenedComposition might need natural abundancies. - #This call-back function is used so NCrystal can flatten composition - #using OpenMC's natural abundancies. In practice this function will - #only get invoked in the unlikely case where a material is specified - #by referring both to natural elements and specific isotopes of the - #same element. + # nc_mat.getFlattenedComposition might need natural abundancies. + # This call-back function is used so NCrystal can flatten composition + # using OpenMC's natural abundancies. In practice this function will + # only get invoked in the unlikely case where a material is specified + # by referring both to natural elements and specific isotopes of the + # same element. elem_name = openmc.data.ATOMIC_SYMBOL[Z] return [ (int(iso_name[len(elem_name):]), abund) @@ -712,6 +731,7 @@ def openmc_natabund(Z): else: material.add_element(elemname, frac) + # TODO: add support for density_timeseries material.set_density('g/cm3', nc_mat.getDensity()) material._ncrystal_cfg = NCrystal.normaliseCfg(cfg) @@ -734,9 +754,11 @@ def add_volume_information(self, volume_calc): raise ValueError('No volume information found for material ID={}.' .format(self.id)) else: - raise ValueError(f'No volume information found for material ID={self.id}.') + raise ValueError( + f'No volume information found for material ID={self.id}.') - def set_density(self, units: str, density: float | None = None): + def set_density(self, units: str, density: float | None = None, + density_timeseries: list[float] | None = None): """Set the density of the material Parameters @@ -746,7 +768,11 @@ def set_density(self, units: str, density: float | None = None): density : float, optional Value of the density. Must be specified unless units is given as 'sum'. + density_timeseries : list of float, optional + Timeseries of density. Can only be specified if units are not given + as 'sum'. + .. versionadded:: 0.16.0 """ cv.check_value('density units', units, DENSITY_UNITS) @@ -757,6 +783,10 @@ def set_density(self, units: str, density: float | None = None): msg = 'Density "{}" for Material ID="{}" is ignored ' \ 'because the unit is "sum"'.format(density, self.id) warnings.warn(msg) + if density_timeseries is not None: + msg = 'Density timeseries cannot be used when ' \ + 'using "sum" density units.' + raise ValueError(msg) else: if density is None: msg = 'Unable to set the density for Material ID="{}" ' \ @@ -767,6 +797,14 @@ def set_density(self, units: str, density: float | None = None): cv.check_type(f'the density for Material ID="{self.id}"', density, Real) self._density = density + if density_timeseries is not None: + cv.check_type(f'the density timeseries for Material ID="{self.id}"', + density_timeseries, Iterable, Real) + [cv.check_greater_than(f'an element in density timeseries for Material ID="{self.id}"', + x, 0.0) for x in density_timeseries] + self._density_timeseries = density_timeseries + else: + self._density_timeseries = None def add_nuclide(self, nuclide: str, percent: float, percent_type: str = 'ao'): """Add a nuclide to the material @@ -788,7 +826,8 @@ def add_nuclide(self, nuclide: str, percent: float, percent_type: str = 'ao'): if self._macroscopic is not None: msg = 'Unable to add a Nuclide to Material ID="{}" as a ' \ - 'macroscopic data-set has already been added'.format(self._id) + 'macroscopic data-set has already been added'.format( + self._id) raise ValueError(msg) if self._ncrystal_cfg is not None: @@ -1025,7 +1064,8 @@ def add_element(self, element: str, percent: float, percent_type: str = 'ao', if self._macroscopic is not None: msg = 'Unable to add an Element to Material ID="{}" as a ' \ - 'macroscopic data-set has already been added'.format(self._id) + 'macroscopic data-set has already been added'.format( + self._id) raise ValueError(msg) if enrichment is not None and enrichment_target is None: @@ -1114,10 +1154,10 @@ def add_elements_from_formula(self, formula: str, percent_type: str = 'ao', msg = f'Formula entry {token} not an element symbol.' raise ValueError(msg) elif token not in ['(', ')', ''] and not token.isdigit(): - msg = 'Formula must be made from a sequence of ' \ - 'element symbols, integers, and brackets. ' \ - '{} is not an allowable entry.'.format(token) - raise ValueError(msg) + msg = 'Formula must be made from a sequence of ' \ + 'element symbols, integers, and brackets. ' \ + '{} is not an allowable entry.'.format(token) + raise ValueError(msg) # Checks that the number of opening and closing brackets are equal if formula.count('(') != formula.count(')'): @@ -1179,12 +1219,13 @@ def add_s_alpha_beta(self, name: str, fraction: float = 1.0): if self._macroscopic is not None: msg = 'Unable to add an S(a,b) table to Material ID="{}" as a ' \ - 'macroscopic data-set has already been added'.format(self._id) + 'macroscopic data-set has already been added'.format( + self._id) raise ValueError(msg) if not isinstance(name, str): msg = 'Unable to add an S(a,b) table to Material ID="{}" with a ' \ - 'non-string table name "{}"'.format(self._id, name) + 'non-string table name "{}"'.format(self._id, name) raise ValueError(msg) cv.check_type('S(a,b) fraction', fraction, Real) @@ -1321,7 +1362,7 @@ def get_nuclide_atom_densities(self, nuclide: str | None = None) -> dict[str, fl if not percent_in_atom: for n, nuc in enumerate(nucs): nuc_densities[n] *= self.average_molar_mass / \ - openmc.data.atomic_mass(nuc) + openmc.data.atomic_mass(nuc) # Now that we have the atomic amounts, lets finish calculating densities sum_percent = np.sum(nuc_densities) @@ -1330,7 +1371,7 @@ def get_nuclide_atom_densities(self, nuclide: str | None = None) -> dict[str, fl # Convert the mass density to an atom density if not density_in_atom: density = -density / self.average_molar_mass * 1.e-24 \ - * openmc.data.AVOGADRO + * openmc.data.AVOGADRO nuc_densities = density * nuc_densities @@ -1370,7 +1411,8 @@ def get_element_atom_densities(self, element: str | None = None) -> dict[str, fl # Accumulate densities for each nuclide for nuclide, density in nuc_densities.items(): - nuc_element = openmc.data.ATOMIC_SYMBOL[openmc.data.zam(nuclide)[0]] + nuc_element = openmc.data.ATOMIC_SYMBOL[openmc.data.zam(nuclide)[ + 0]] if element is None or element == nuc_element: if nuc_element not in densities: densities[nuc_element] = 0.0 @@ -1378,11 +1420,10 @@ def get_element_atom_densities(self, element: str | None = None) -> dict[str, fl # If specific element was requested, make sure it is present if element is not None and element not in densities: - raise ValueError(f'Element {element} not found in material.') + raise ValueError(f'Element {element} not found in material.') return densities - def get_activity(self, units: str = 'Bq/cm3', by_nuclide: bool = False, volume: float | None = None) -> dict[str, float] | float: """Return the activity of the material or each nuclide within. @@ -1488,7 +1529,8 @@ def get_decay_heat(self, units: str = 'W', by_nuclide: bool = False, decay_erg = openmc.data.decay_energy(nuclide) inv_seconds = openmc.data.decay_constant(nuclide) decay_erg *= openmc.data.JOULE_PER_EV - decayheat[nuclide] = inv_seconds * decay_erg * 1e24 * atoms_per_bcm * multiplier + decayheat[nuclide] = inv_seconds * decay_erg * \ + 1e24 * atoms_per_bcm * multiplier return decayheat if by_nuclide else sum(decayheat.values()) @@ -1539,7 +1581,7 @@ def get_mass_density(self, nuclide: str | None = None) -> float: mass_density = 0.0 for nuc, atoms_per_bcm in self.get_nuclide_atom_densities(nuclide=nuclide).items(): density_i = 1e24 * atoms_per_bcm * openmc.data.atomic_mass(nuc) \ - / openmc.data.AVOGADRO + / openmc.data.AVOGADRO mass_density += density_i return mass_density @@ -1722,12 +1764,13 @@ def _get_macroscopic_xml(self, macroscopic: str) -> ET.Element: def _get_nuclides_xml( self, nuclides: Iterable[NuclideTuple], - nuclides_to_ignore: Iterable[str] | None = None)-> list[ET.Element]: + nuclides_to_ignore: Iterable[str] | None = None) -> list[ET.Element]: xml_elements = [] # Remove any nuclides to ignore from the XML export if nuclides_to_ignore: - nuclides = [nuclide for nuclide in nuclides if nuclide.name not in nuclides_to_ignore] + nuclides = [ + nuclide for nuclide in nuclides if nuclide.name not in nuclides_to_ignore] xml_elements = [self._get_nuclide_xml(nuclide) for nuclide in nuclides] @@ -1764,9 +1807,11 @@ def to_xml_element( if self._ncrystal_cfg: if self._sab: - raise ValueError("NCrystal materials are not compatible with S(a,b).") + raise ValueError( + "NCrystal materials are not compatible with S(a,b).") if self._macroscopic is not None: - raise ValueError("NCrystal materials are not compatible with macroscopic cross sections.") + raise ValueError( + "NCrystal materials are not compatible with macroscopic cross sections.") element.set("cfg", str(self._ncrystal_cfg)) @@ -1780,8 +1825,14 @@ def to_xml_element( if self._density_units != 'sum': subelement.set("value", str(self._density)) subelement.set("units", self._density_units) + if self._density_timeseries is not None: + timeseries_text = " ".join(str(x) + for x in self._density_timeseries) + subelement.set("value_timeseries", timeseries_text) + else: - raise ValueError(f'Density has not been set for material {self.id}!') + raise ValueError( + f'Density has not been set for material {self.id}!') if self._macroscopic is None: # Create nuclide XML subelements @@ -1807,6 +1858,7 @@ def to_xml_element( return element + # TODO: add support for density_timeseries @classmethod def mix_materials(cls, materials, fracs: Iterable[float], percent_type: str = 'ao', **kwargs) -> Material: @@ -1881,17 +1933,18 @@ def mix_materials(cls, materials, fracs: Iterable[float], nuc_per_cc = wgt*1.e24*atoms_per_bcm nuclides_per_cc[nuc] += nuc_per_cc mass_per_cc[nuc] += nuc_per_cc*openmc.data.atomic_mass(nuc) / \ - openmc.data.AVOGADRO + openmc.data.AVOGADRO # Create the new material with the desired name if "name" not in kwargs: kwargs["name"] = '-'.join([f'{m.name}({f})' for m, f in - zip(materials, fracs)]) + zip(materials, fracs)]) new_mat = cls(**kwargs) # Compute atom fractions of nuclides and add them to the new material - tot_nuclides_per_cc = np.sum([dens for dens in nuclides_per_cc.values()]) + tot_nuclides_per_cc = np.sum( + [dens for dens in nuclides_per_cc.values()]) for nuc, atom_dens in nuclides_per_cc.items(): new_mat.add_nuclide(nuc, atom_dens/tot_nuclides_per_cc, 'ao') @@ -1963,7 +2016,12 @@ def from_xml_element(cls, elem: ET.Element) -> Material: mat.set_density(units) else: value = float(get_text(density, 'value')) - mat.set_density(units, value) + text = get_text(density, 'value_timeseries') + if text is not None: + density_timeseries = [float(x) for x in text.split()] + else: + density_timeseries = None + mat.set_density(units, value, density_timeseries) # Check for isotropic scattering nuclides isotropic = get_elem_list(elem, "isotropic", str) @@ -2033,7 +2091,6 @@ def deplete( return depleted_materials_dict[self.id] - def mean_free_path(self, energy: float) -> float: """Calculate the mean free path of neutrons in the material at a given energy. @@ -2176,7 +2233,8 @@ def _write_xml(self, file, header=True, level=0, spaces_per_level=2, # Write the elements. for material in sorted(set(self), key=lambda x: x.id): - element = material.to_xml_element(nuclides_to_ignore=nuclides_to_ignore) + element = material.to_xml_element( + nuclides_to_ignore=nuclides_to_ignore) clean_indentation(element, level=level+1) element.tail = element.tail.strip(' ') file.write((level+1)*spaces_per_level*' ') @@ -2262,7 +2320,6 @@ def from_xml(cls, path: PathLike = 'materials.xml') -> Materials: return cls.from_xml_element(root) - def deplete( self, multigroup_fluxes: Sequence[Sequence[float]], diff --git a/openmc/model/model.py b/openmc/model/model.py index 884e3ff6f95..4464aa462da 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -723,18 +723,18 @@ def import_properties(self, filename: PathLike): lib_cell.set_temperature(temperature[0]) if group['density']: - density = group['density'][()] - if density.size > 1: - cell.density = [rho for rho in density] - else: - cell.density = density - if self.is_initialized: - lib_cell = openmc.lib.cells[cell_id] - if density.size > 1: - for i, rho in enumerate(density): - lib_cell.set_density(rho, i) - else: - lib_cell.set_density(density[0]) + density = group['density'][()] + if density.size > 1: + cell.density = [rho for rho in density] + else: + cell.density = density + if self.is_initialized: + lib_cell = openmc.lib.cells[cell_id] + if density.size > 1: + for i, rho in enumerate(density): + lib_cell.set_density(rho, i) + else: + lib_cell.set_density(density[0]) # Make sure number of materials matches mats_group = fh['materials'] @@ -996,7 +996,6 @@ def calculate_volumes( openmc.lib.materials[domain_id].volume = \ vol_calc.volumes[domain_id].n - def _set_plot_defaults( self, origin: Sequence[float] | None, @@ -1703,9 +1702,11 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo @staticmethod def _auto_generate_mgxs_lib( model: openmc.model.model, - groups: openmc.mgxs.EnergyGroups, + energy_groups: openmc.mgxs.EnergyGroups, correction: str | none, directory: pathlike, + kinetic: bool | None = None, + num_delayed_groups: int = 0, ) -> openmc.mgxs.Library: """ Automatically generate a multi-group cross section libray from a model @@ -1713,7 +1714,7 @@ def _auto_generate_mgxs_lib( Parameters ---------- - groups : openmc.mgxs.EnergyGroups + energy_groups : openmc.mgxs.EnergyGroups Energy group structure for the MGXS. nparticles : int Number of particles to simulate per batch when generating MGXS. @@ -1724,6 +1725,10 @@ def _auto_generate_mgxs_lib( "P0". directory : str Directory to run the simulation in, so as to contain XML files. + kinetic : bool, optional + Flag to indicate if kinetic simulation cross sections are needed. + num_delayed_groups : int, optional + Number of delayed groups for kinetic simulations. Returns ------- @@ -1735,7 +1740,10 @@ def _auto_generate_mgxs_lib( mgxs_lib = openmc.mgxs.Library(model.geometry) # Pick energy group structure - mgxs_lib.energy_groups = groups + mgxs_lib.energy_groups = energy_groups + + if (kinetic): + mgxs_lib.num_delayed_groups = num_delayed_groups # Disable transport correction mgxs_lib.correction = correction @@ -1753,6 +1761,9 @@ def _auto_generate_mgxs_lib( 'consistent nu-scatter matrix', 'multiplicity matrix', 'chi', 'kappa-fission' ] + if kinetic: + mgxs_lib.mgxs_types += ['chi-prompt', 'chi-delayed', + 'decay-rate', 'inverse-velocity', 'beta'] # Specify a "material" domain type for the cross section tally filters mgxs_lib.domain_type = "material" @@ -1783,7 +1794,7 @@ def _auto_generate_mgxs_lib( def _create_mgxs_sources( self, - groups: openmc.mgxs.EnergyGroups, + energy_groups: openmc.mgxs.EnergyGroups, spatial_dist: openmc.stats.Spatial, source_energy: openmc.stats.Univariate | None = None, ) -> list[openmc.IndependentSource]: @@ -1805,7 +1816,7 @@ def _create_mgxs_sources( Parameters ---------- - groups : openmc.mgxs.EnergyGroups + energy_groups : openmc.mgxs.EnergyGroups Energy group structure for the MGXS. spatial_dist : openmc.stats.Spatial Spatial distribution to use for all sources. @@ -1821,8 +1832,8 @@ def _create_mgxs_sources( # Make a discrete source that is uniform over the bins of the group structure midpoints = [] strengths = [] - for i in range(groups.num_groups): - bounds = groups.get_group_bounds(i+1) + for i in range(energy_groups.num_groups): + bounds = energy_groups.get_group_bounds(i+1) midpoints.append((bounds[0] + bounds[1]) / 2.0) strengths.append(1.0) @@ -1869,13 +1880,15 @@ def _create_mgxs_sources( @staticmethod def _isothermal_infinite_media_mgxs( material: openmc.Material, - groups: openmc.mgxs.EnergyGroups, + energy_groups: openmc.mgxs.EnergyGroups, nparticles: int, correction: str | None, directory: PathLike, source: openmc.IndependentSource, temperature_settings: dict, temperature: float | None = None, + kinetic: bool | None = None, + num_delayed_groups: int = 0, ) -> openmc.XSdata: """Generate a single MGXS set for one material, where the geometry is an infinite medium composed of that material at an isothermal temperature value. @@ -1884,7 +1897,7 @@ def _isothermal_infinite_media_mgxs( ---------- material : openmc.Material The material to generate MGXS for - groups : openmc.mgxs.EnergyGroups + energy_groups : openmc.mgxs.EnergyGroups Energy group structure for the MGXS. nparticles : int Number of particles to simulate per batch when generating MGXS. @@ -1902,6 +1915,10 @@ def _isothermal_infinite_media_mgxs( temperature : float, optional The isothermal temperature value to apply to the material. If not specified, defaults to the temperature in the material. + kinetic : bool, optional + Flag to indicate if kinetic simulation cross sections are needed. + num_delayed_groups : int, optional + Number of delayed groups for kinetic simulations. Returns ------- @@ -1937,7 +1954,13 @@ def _isothermal_infinite_media_mgxs( # Generate MGXS mgxs_lib = Model._auto_generate_mgxs_lib( - model, groups, correction, directory) + model, + energy_groups, + correction, + directory, + kinetic, + num_delayed_groups + ) if temperature != None: return mgxs_lib.get_xsdata(domain=material, xsdata_name=name, @@ -1947,7 +1970,7 @@ def _isothermal_infinite_media_mgxs( def _generate_infinite_medium_mgxs( self, - groups: openmc.mgxs.EnergyGroups, + energy_groups: openmc.mgxs.EnergyGroups, nparticles: int, mgxs_path: PathLike, correction: str | None, @@ -1955,6 +1978,8 @@ def _generate_infinite_medium_mgxs( source_energy: openmc.stats.Univariate | None = None, temperatures: Sequence[float] | None = None, temperature_settings: dict | None = None, + kinetic: bool | None = None, + num_delayed_groups: int = 0, ) -> None: """Generate a MGXS library by running multiple OpenMC simulations, each representing an infinite medium simulation of a single isolated @@ -1981,7 +2006,7 @@ def _generate_infinite_medium_mgxs( Parameters ---------- - groups : openmc.mgxs.EnergyGroups + energy_groups : openmc.mgxs.EnergyGroups Energy group structure for the MGXS. nparticles : int Number of particles to simulate per batch when generating MGXS. @@ -2003,10 +2028,14 @@ def _generate_infinite_medium_mgxs( A dictionary of temperature settings to use when generating MGXS. Valid entries for temperature_settings are the same as the valid entries in openmc.Settings.temperature_settings. + kinetic : bool, optional + Flag to indicate if kinetic simulation cross sections are needed. + num_delayed_groups : int, optional + Number of delayed groups for kinetic simulations. """ src = self._create_mgxs_sources( - groups, + energy_groups, spatial_dist=openmc.stats.Point(), source_energy=source_energy ) @@ -2022,17 +2051,22 @@ def _generate_infinite_medium_mgxs( for material in self.materials: xs_data = Model._isothermal_infinite_media_mgxs( material, - groups, + energy_groups, nparticles, correction, directory, src, - temp_settings + temp_settings, + kinetic=kinetic, + num_delayed_groups=num_delayed_groups, ) mgxs_sets.append(xs_data) # Write the file to disk. - mgxs_file = openmc.MGXSLibrary(energy_groups=groups) + mgxs_file = openmc.MGXSLibrary( + energy_groups=energy_groups, + num_delayed_groups=num_delayed_groups + ) for mgxs_set in mgxs_sets: mgxs_file.add_xsdata(mgxs_set) mgxs_file.export_to_hdf5(mgxs_path) @@ -2044,27 +2078,38 @@ def _generate_infinite_medium_mgxs( for material in self.materials: xs_data = Model._isothermal_infinite_media_mgxs( material, - groups, + energy_groups, nparticles, correction, directory, src, temp_settings, - temperature + temperature, + kinetic, + num_delayed_groups, ) raw_mgxs_sets[temperature].append(xs_data) # Unpack the isothermal XSData objects and build a single XSData object per material. mgxs_sets = [] for m in range(len(self.materials)): - mgxs_sets.append(openmc.XSdata(self.materials[m].name, groups, - temperatures=temperatures)) + mgxs_sets.append( + openmc.XSdata( + self.materials[m].name, + energy_groups, + temperatures=temperatures, + num_delayed_groups=num_delayed_groups + ) + ) mgxs_sets[-1].order = 0 for temperature in temperatures: mgxs_sets[-1].add_temperature_data(raw_mgxs_sets[temperature][m]) # Write the file to disk. - mgxs_file = openmc.MGXSLibrary(energy_groups=groups) + mgxs_file = openmc.MGXSLibrary( + energy_groups=energy_groups, + num_delayed_groups=num_delayed_groups, + ) for mgxs_set in mgxs_sets: mgxs_file.add_xsdata(mgxs_set) mgxs_file.export_to_hdf5(mgxs_path) @@ -2146,13 +2191,15 @@ def _create_stochastic_slab_geometry( @staticmethod def _isothermal_stochastic_slab_mgxs( stoch_geom: openmc.Geometry, - groups: openmc.mgxs.EnergyGroups, + energy_groups: openmc.mgxs.EnergyGroups, nparticles: int, correction: str | None, directory: PathLike, source: openmc.IndependentSource, temperature_settings: dict, temperature: float | None = None, + kinetic: bool | None = None, + num_delayed_groups: int = 0, ) -> dict[str, openmc.XSdata]: """Generate MGXS assuming a stochastic "sandwich" of materials in a layered slab geometry. If a temperature is specified, all materials in the slab have @@ -2162,7 +2209,7 @@ def _isothermal_stochastic_slab_mgxs( ---------- stoch_geom : openmc.Geometry The stochastic slab geometry. - groups : openmc.mgxs.EnergyGroups + energy_groups : openmc.mgxs.EnergyGroups Energy group structure for the MGXS. nparticles : int Number of particles to simulate per batch when generating MGXS. @@ -2180,6 +2227,10 @@ def _isothermal_stochastic_slab_mgxs( temperature : float, optional The isothermal temperature value to apply to the materials in the slab. If not specified, defaults to the temperature in the materials. + kinetic : bool, optional + Flag to indicate if kinetic simulation cross sections are needed. + num_delayed_groups : int, optional + Number of delayed groups for kinetic simulations. Returns ------- @@ -2211,7 +2262,13 @@ def _isothermal_stochastic_slab_mgxs( # Generate MGXS mgxs_lib = Model._auto_generate_mgxs_lib( - model, groups, correction, directory) + model, + energy_groups, + correction, + directory, + kinetic, + num_delayed_groups + ) # Fetch all of the isothermal results. if temperature != None: @@ -2228,7 +2285,7 @@ def _isothermal_stochastic_slab_mgxs( def _generate_stochastic_slab_mgxs( self, - groups: openmc.mgxs.EnergyGroups, + energy_groups: openmc.mgxs.EnergyGroups, nparticles: int, mgxs_path: PathLike, correction: str | None, @@ -2236,6 +2293,8 @@ def _generate_stochastic_slab_mgxs( source_energy: openmc.stats.Univariate | None = None, temperatures: Sequence[float] | None = None, temperature_settings: dict | None = None, + kinetic: bool | None = None, + num_delayed_groups: int = 0, ) -> None: """Generate MGXS assuming a stochastic "sandwich" of materials in a layered slab geometry. While geometry-specific spatial shielding effects are not @@ -2251,7 +2310,7 @@ def _generate_stochastic_slab_mgxs( Parameters ---------- - groups : openmc.mgxs.EnergyGroups + energy_groups : openmc.mgxs.EnergyGroups Energy group structure for the MGXS. nparticles : int Number of particles to simulate per batch when generating MGXS. @@ -2287,6 +2346,10 @@ def _generate_stochastic_slab_mgxs( A dictionary of temperature settings to use when generating MGXS. Valid entries for temperature_settings are the same as the valid entries in openmc.Settings.temperature_settings. + kinetic : bool, optional + Flag to indicate if kinetic simulation cross sections are needed. + num_delayed_groups : int, optional + Number of delayed groups for kinetic simulations. """ # Stochastic slab geometry @@ -2294,7 +2357,7 @@ def _generate_stochastic_slab_mgxs( self.materials) src = self._create_mgxs_sources( - groups, + energy_groups, spatial_dist=spatial_distribution, source_energy=source_energy ) @@ -2308,16 +2371,21 @@ def _generate_stochastic_slab_mgxs( if temperatures == None: mgxs_sets = Model._isothermal_stochastic_slab_mgxs( geo, - groups, + energy_groups, nparticles, correction, directory, src, - temp_settings + temp_settings, + kinetic=kinetic, + num_delayed_groups=num_delayed_groups ).values() # Write the file to disk. - mgxs_file = openmc.MGXSLibrary(energy_groups=groups) + mgxs_file = openmc.MGXSLibrary( + energy_groups=energy_groups, + num_delayed_groups=num_delayed_groups + ) for mgxs_set in mgxs_sets: mgxs_file.add_xsdata(mgxs_set) mgxs_file.export_to_hdf5(mgxs_path) @@ -2327,25 +2395,37 @@ def _generate_stochastic_slab_mgxs( for temperature in temperatures: raw_mgxs_sets[temperature] = Model._isothermal_stochastic_slab_mgxs( geo, - groups, + energy_groups, nparticles, correction, directory, src, temp_settings, - temperature + temperature, + kinetic, + num_delayed_groups ) # Unpack the isothermal XSData objects and build a single XSData object per material. mgxs_sets = [] for mat in self.materials: - mgxs_sets.append(openmc.XSdata(mat.name, groups, temperatures=temperatures)) + mgxs_sets.append( + openmc.XSdata( + mat.name, + energy_groups, + temperatures=temperatures, + num_delayed_groups=num_delayed_groups + ) + ) mgxs_sets[-1].order = 0 for temperature in temperatures: mgxs_sets[-1].add_temperature_data(raw_mgxs_sets[temperature][mat.name]) # Write the file to disk. - mgxs_file = openmc.MGXSLibrary(energy_groups=groups) + mgxs_file = openmc.MGXSLibrary( + energy_groups=energy_groups, + num_delayed_groups=num_delayed_groups + ) for mgxs_set in mgxs_sets: mgxs_file.add_xsdata(mgxs_set) mgxs_file.export_to_hdf5(mgxs_path) @@ -2353,12 +2433,14 @@ def _generate_stochastic_slab_mgxs( @staticmethod def _isothermal_materialwise_mgxs( input_model: openmc.Model, - groups: openmc.mgxs.EnergyGroups, + energy_groups: openmc.mgxs.EnergyGroups, nparticles: int, correction: str | None, directory: PathLike, temperature_settings: dict, temperature: float | None = None, + kinetic: bool | None = None, + num_delayed_groups: int = 0, ) -> dict[str, openmc.XSdata]: """Generate a material-wise MGXS library for the model by running the original continuous energy OpenMC simulation. If a temperature is @@ -2372,7 +2454,7 @@ def _isothermal_materialwise_mgxs( ---------- input_model : openmc.Model The model to use when computing material-wise MGXS. - groups : openmc.mgxs.EnergyGroups + energy_groups : openmc.mgxs.EnergyGroups Energy group structure for the MGXS. nparticles : int Number of particles to simulate per batch when generating MGXS. @@ -2389,6 +2471,10 @@ def _isothermal_materialwise_mgxs( The isothermal temperature value to apply to the materials in the input model. If not specified, defaults to the temperatures in the materials. + kinetic : bool, optional + Flag to indicate if kinetic simulation cross sections are needed. + num_delayed_groups : int, optional + Number of delayed groups for kinetic simulations. Returns ------- @@ -2411,7 +2497,13 @@ def _isothermal_materialwise_mgxs( # Generate MGXS mgxs_lib = Model._auto_generate_mgxs_lib( - model, groups, correction, directory) + model, + energy_groups, + correction, + directory, + kinetic, + num_delayed_groups + ) # Fetch all of the isothermal results. if temperature != None: @@ -2428,13 +2520,15 @@ def _isothermal_materialwise_mgxs( def _generate_material_wise_mgxs( self, - groups: openmc.mgxs.EnergyGroups, + energy_groups: openmc.mgxs.EnergyGroups, nparticles: int, mgxs_path: PathLike, correction: str | None, directory: PathLike, temperatures: Sequence[float] | None = None, temperature_settings: dict | None = None, + kinetic: bool | None = None, + num_delayed_groups: int = 0, ) -> None: """Generate a material-wise MGXS library for the model by running the original continuous energy OpenMC simulation of the full material @@ -2448,7 +2542,7 @@ def _generate_material_wise_mgxs( Parameters ---------- - groups : openmc.mgxs.EnergyGroups + energy_groups : openmc.mgxs.EnergyGroups Energy group structure for the MGXS. nparticles : int Number of particles to simulate per batch when generating MGXS. @@ -2467,6 +2561,10 @@ def _generate_material_wise_mgxs( A dictionary of temperature settings to use when generating MGXS. Valid entries for temperature_settings are the same as the valid entries in openmc.Settings.temperature_settings. + kinetic : bool, optional + Flag to indicate if kinetic simulation cross sections are needed. + num_delayed_groups : int, optional + Number of delayed groups for kinetic simulations. """ temp_settings = {} if temperature_settings == None: @@ -2477,15 +2575,20 @@ def _generate_material_wise_mgxs( if temperatures == None: mgxs_sets = Model._isothermal_materialwise_mgxs( self, - groups, + energy_groups, nparticles, correction, directory, - temp_settings + temp_settings, + kinetic=kinetic, + num_delayed_groups=num_delayed_groups ).values() # Write the file to disk. - mgxs_file = openmc.MGXSLibrary(energy_groups=groups) + mgxs_file = openmc.MGXSLibrary( + energy_groups=energy_groups, + num_delayed_groups=num_delayed_groups + ) for mgxs_set in mgxs_sets: mgxs_file.add_xsdata(mgxs_set) mgxs_file.export_to_hdf5(mgxs_path) @@ -2495,24 +2598,36 @@ def _generate_material_wise_mgxs( for temperature in temperatures: raw_mgxs_sets[temperature] = Model._isothermal_materialwise_mgxs( self, - groups, + energy_groups, nparticles, correction, directory, temp_settings, - temperature + temperature, + kinetic, + num_delayed_groups, ) # Unpack the isothermal XSData objects and build a single XSData object per material. mgxs_sets = [] for mat in self.materials: - mgxs_sets.append(openmc.XSdata(mat.name, groups, temperatures=temperatures)) + mgxs_sets.append( + openmc.XSdata( + mat.name, + energy_groups, + temperatures=temperatures, + num_delayed_groups=num_delayed_groups + ) + ) mgxs_sets[-1].order = 0 for temperature in temperatures: mgxs_sets[-1].add_temperature_data(raw_mgxs_sets[temperature][mat.name]) # Write the file to disk. - mgxs_file = openmc.MGXSLibrary(energy_groups=groups) + mgxs_file = openmc.MGXSLibrary( + energy_groups=energy_groups, + num_delayed_groups=num_delayed_groups + ) for mgxs_set in mgxs_sets: mgxs_file.add_xsdata(mgxs_set) mgxs_file.export_to_hdf5(mgxs_path) @@ -2520,7 +2635,7 @@ def _generate_material_wise_mgxs( def convert_to_multigroup( self, method: str = "material_wise", - groups: str | Sequence[float] | openmc.mgxs.EnergyGroups = "CASMO-2", + energy_groups: str | Sequence[float] | openmc.mgxs.EnergyGroups = "CASMO-2", nparticles: int = 2000, overwrite_mgxs_library: bool = False, mgxs_path: PathLike = "mgxs.h5", @@ -2528,6 +2643,8 @@ def convert_to_multigroup( source_energy: openmc.stats.Univariate | None = None, temperatures: Sequence[float] | None = None, temperature_settings: dict | None = None, + kinetic: bool | None = None, + num_delayed_groups: int = 0, ): """Convert all materials from continuous energy to multigroup. @@ -2538,7 +2655,7 @@ def convert_to_multigroup( ---------- method : {"material_wise", "stochastic_slab", "infinite_medium"}, optional Method to generate the MGXS. - groups : openmc.mgxs.EnergyGroups, str, or sequence of float, optional + energy_groups : openmc.mgxs.EnergyGroups, str, or sequence of float, optional Energy group structure for the MGXS. Can be an :class:`openmc.mgxs.EnergyGroups` object, a string name of a predefined group structure from :data:`openmc.mgxs.GROUP_STRUCTURES` @@ -2580,9 +2697,13 @@ def convert_to_multigroup( A dictionary of temperature settings to use when generating MGXS. Valid entries for temperature_settings are the same as the valid entries in openmc.Settings.temperature_settings. + kinetic : bool, optional + Flag to indicate if kinetic simulation cross sections are needed. + num_delayed_groups : int, optional + Number of delayed groups for kinetic simulations. """ - if not isinstance(groups, openmc.mgxs.EnergyGroups): - groups = openmc.mgxs.EnergyGroups(groups) + if not isinstance(energy_groups, openmc.mgxs.EnergyGroups): + energy_groups = openmc.mgxs.EnergyGroups(energy_groups) # Do all work (including MGXS generation) in a temporary directory # to avoid polluting the working directory with residual XML files @@ -2616,16 +2737,42 @@ def convert_to_multigroup( if not Path(mgxs_path).is_file() or overwrite_mgxs_library: if method == "infinite_medium": self._generate_infinite_medium_mgxs( - groups, nparticles, mgxs_path, correction, tmpdir, source_energy, - temperatures, temperature_settings) + energy_groups, + nparticles, + mgxs_path, + correction, + tmpdir, + source_energy, + temperatures, + temperature_settings, + kinetic, + num_delayed_groups + ) elif method == "material_wise": self._generate_material_wise_mgxs( - groups, nparticles, mgxs_path, correction, tmpdir, - temperatures, temperature_settings) + energy_groups, + nparticles, + mgxs_path, + correction, + tmpdir, + temperatures, + temperature_settings, + kinetic, + num_delayed_groups + ) elif method == "stochastic_slab": self._generate_stochastic_slab_mgxs( - groups, nparticles, mgxs_path, correction, tmpdir, source_energy, - temperatures, temperature_settings) + energy_groups, + nparticles, + mgxs_path, + correction, + tmpdir, + source_energy, + temperatures, + temperature_settings, + kinetic, + num_delayed_groups + ) else: raise ValueError( f'MGXS generation method "{method}" not recognized') @@ -2642,6 +2789,20 @@ def convert_to_multigroup( self.settings.energy_mode = 'multi-group' + # If making a set the time step size to 0.01 + if kinetic: + self.settings.kinetic_simulation = True + warnings.warn( + "Kinetic model. Currently, only the random ray solver" + " supports kinetic simulations. The number of time" + " steps to run and material density transient using " + " openmc.Settings.timestep_parameters['n_timesteps'] " + " and openmc.Material.set_density(), respectively.") + self.settings.timestep_parameters = { + 'dt': 0.01, + 'timestep_units': 's' + } + def convert_to_random_ray(self): """Convert a multigroup model to use random ray. diff --git a/openmc/settings.py b/openmc/settings.py index 6919afca4cc..090716958b5 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -130,6 +130,8 @@ class Settings: type are 'variance', 'std_dev', and 'rel_err'. The threshold value should be a float indicating the variance, standard deviation, or relative error used. + kinetic_simulation : bool + Flag for whether or not to run a kinetic simulation log_grid_bins : int Number of bins for logarithmic energy grid search material_cell_offsets : bool @@ -236,6 +238,15 @@ class Settings: stabilization, which may be desirable as stronger diagonal stabilization also tends to dampen the convergence rate of the solver, thus requiring more iterations to converge. + Additional options are available when running a kinetic simulation: + + :bd_order: + Indicates the integer order of backwards difference approximation used to + numerically compute time derivatives. + :time_derivative_method: + Method for resolving :math:`\\frac{\\partial}{\\partial t} + \\psi_{r,g}(s,t)` in the time-dependent charactersitic equation. + Options are 'isotropic' (default), or 'propagation'. .. versionadded:: 0.15.0 resonance_scattering : dict @@ -328,6 +339,18 @@ class Settings: sections be loaded at all temperatures within the range. 'multipole' is a boolean indicating whether or not the windowed multipole method should be used to evaluate resolved resonance cross sections. + timestep_parameters : dict + Time step parameters for kinetic simulations. Acceptable keys are: + :dt: + Fixed timestep size. + :n_timesteps: + Numeber of timesteps. + :timestep_units: + `ms`,`s`, `min`. Units for timesteps. `ms` means miliseconds, `s` + means seconds, `min` means minutes. + + .. versionadded:: 0.16 + trace : tuple or list Show detailed information about a single particle, indicated by three integers: the batch number, generation number, and particle number @@ -397,6 +420,8 @@ def __init__(self, **kwargs): self._max_write_lost_particles = None self._particles = None self._keff_trigger = None + self._kinetic_simulation = None + self._timestep_parameters = {} # Energy mode subelement self._energy_mode = None @@ -620,6 +645,39 @@ def keff_trigger(self, keff_trigger: dict): self._keff_trigger = keff_trigger + @property + def kinetic_simulation(self) -> bool: + return self._kinetic_simulation + + @kinetic_simulation.setter + def kinetic_simulation(self, value: bool): + cv.check_type('kinetic simulation', value, bool) + self._kinetic_simulation = value + + @property + def timestep_parameters(self) -> dict: + return self._timestep_parameters + + @timestep_parameters.setter + def timestep_parameters(self, timestep_parameters: dict): + if not isinstance(timestep_parameters, Mapping): + raise ValueError(f'Unable to set timestep_parameters from "{timestep_parameters}" ' + 'which is not a dict.') + for key, value in timestep_parameters.items(): + if key == 'dt': + cv.check_type('dt', value, Real) + cv.check_greater_than('dt', value, 0) + elif key == 'n_timesteps': + cv.check_type('n_timesteps', value, Integral) + cv.check_greater_than('n_timesteps', value, 0) + elif key == 'timestep_units': + cv.check_value( + 'timestep units', value, ('ms', 's', 'min')) + else: + raise ValueError(f'Unable to set time dependent to "{key}" which is ' + 'unsupported by OpenMC') + self._timestep_parameters = timestep_parameters + @property def energy_mode(self) -> str: return self._energy_mode @@ -1421,6 +1479,14 @@ def random_ray(self, random_ray: dict): cv.check_type('diagonal stabilization rho', value, Real) cv.check_greater_than('diagonal stabilization rho', value, 0.0, True) + elif self.run_mode == 'time dependent': + if key == 'bd_order': + cv.check_type('BD order', value, Integer) + cv.check_greater_than('BD order', value, 0) + cv.check_less_than('BD order', value, 7) + elif key == 'time_derivative method': + cv.check_value('time derivative method', value, + ('isotropic', 'propagation')) else: raise ValueError(f'Unable to set random ray to "{key}" which is ' 'unsupported by OpenMC') @@ -1458,7 +1524,8 @@ def free_gas_threshold(self) -> float | None: def free_gas_threshold(self, free_gas_threshold: float | None): if free_gas_threshold is not None: cv.check_type('free gas threshold', free_gas_threshold, Real) - cv.check_greater_than('free gas threshold', free_gas_threshold, 0.0) + cv.check_greater_than('free gas threshold', + free_gas_threshold, 0.0) self._free_gas_threshold = free_gas_threshold def _create_run_mode_subelement(self, root): @@ -1507,6 +1574,18 @@ def _create_keff_trigger_subelement(self, root): subelement = ET.SubElement(element, key) subelement.text = str(value).lower() + def _create_kinetic_simulation_subelement(self, root): + if self._kinetic_simulation is not None: + elem = ET.SubElement(root, "kinetic_simulation") + elem.text = str(self._kinetic_simulation).lower() + + def _create_timestep_parameters_subelement(self, root): + if self._timestep_parameters: + element = ET.SubElement(root, "timestep_parameters") + for key, value in self._timestep_parameters.items(): + subelement = ET.SubElement(element, key) + subelement.text = str(value) + def _create_energy_mode_subelement(self, root): if self._energy_mode is not None: element = ET.SubElement(root, "energy_mode") @@ -1990,7 +2069,8 @@ def _create_random_ray_subelement(self, root, mesh_memo=None): domain_elem.set( 'type', domain.__class__.__name__.lower()) if mesh_memo is not None and mesh.id not in mesh_memo: - domain_elem.set('type', domain.__class__.__name__.lower()) + domain_elem.set( + 'type', domain.__class__.__name__.lower()) # See if a element already exists -- if not, add it path = f"./mesh[@id='{mesh.id}']" if root.find(path) is None: @@ -2073,6 +2153,23 @@ def _keff_trigger_from_xml_element(self, root): threshold = float(get_text(elem, 'threshold')) self.keff_trigger = {'type': trigger, 'threshold': threshold} + def _kinetic_simulation_from_xml_element(self, root): + text = get_text(root, 'kinetic_simulation') + if text is not None: + self.kinetic_simulation = text in ('true', '1') + + def _timestep_parameters_from_xml_element(self, root): + elem = root.find('timestep_parameters') + if elem is not None: + self.timestep_parameters = {} + for child in elem: + if child.tag == 'n_timesteps': + self.timestep_parameters[child.tag] = int(child.text) + elif child.tag == 'timestep_units': + self.timestep_parameters['timestep_units'] = child.text + elif child.tag == 'dt': + self.timestep_parameters['dt'] = float(child.text) + def _source_from_xml_element(self, root, meshes=None): for elem in root.findall('source'): src = SourceBase.from_xml_element(elem, meshes) @@ -2484,6 +2581,10 @@ def _random_ray_from_xml_element(self, root, meshes=None): domains.append(domain) self.random_ray['source_region_meshes'].append( (mesh, domains)) + elif child.tag == 'bd_order': + self.random_ray['bd_order'] = int(child.text) + elif child.tag == 'time_derivative_method': + self.random_ray['time_derivative_method'] = child.text def _use_decay_photons_from_xml_element(self, root): text = get_text(root, 'use_decay_photons') @@ -2520,6 +2621,8 @@ def to_xml_element(self, mesh_memo=None): self._create_max_write_lost_particles_subelement(element) self._create_generations_per_batch_subelement(element) self._create_keff_trigger_subelement(element) + self._create_kinetic_simulation_subelement(element) + self._create_timestep_parameters_subelement(element) self._create_source_subelement(element, mesh_memo) self._create_output_subelement(element) self._create_statepoint_subelement(element) @@ -2637,6 +2740,8 @@ def from_xml_element(cls, elem, meshes=None): settings._max_write_lost_particles_from_xml_element(elem) settings._generations_per_batch_from_xml_element(elem) settings._keff_trigger_from_xml_element(elem) + settings._kinetic_simulation_from_xml_element(elem) + settings._timestep_parameters_from_xml_element(elem) settings._source_from_xml_element(elem, meshes) settings._volume_calcs_from_xml_element(elem) settings._output_from_xml_element(elem) diff --git a/openmc/statepoint.py b/openmc/statepoint.py index a10ec3a839d..175388839c1 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -17,7 +17,8 @@ _VERSION_STATEPOINT = 18 -KineticsParameters = namedtuple("KineticsParameters", ["generation_time", "beta_effective"]) +KineticsParameters = namedtuple( + "KineticsParameters", ["generation_time", "beta_effective"]) class StatePoint: @@ -57,6 +58,10 @@ class StatePoint: Number of batches simulated date_and_time : datetime.datetime Date and time at which statepoint was written + energy_mode : str + 'continuous-energy', 'multi-group' + + .. versionadded:: 0.16.0 entropy : numpy.ndarray Shannon entropy of fission source at each batch filters : dict @@ -85,6 +90,14 @@ class StatePoint: .. versionadded:: 0.13.1 meshes : dict Dictionary whose keys are mesh IDs and whose values are MeshBase objects + n_energy_groups : int + Number of energy groups used in a multi-group simulation. + + .. versionadded:: 0.16.0 + n_delay_groups : int + Number of delay groups used in a multi-group simulation. + + .. versionadded:: 0.16.0 n_batches : int Number of batches n_inactive : int @@ -97,6 +110,14 @@ class StatePoint: Working directory for simulation photon_transport : bool Indicate whether photon transport is active + random_ray : dict + Dictionary whose keys are the following strings used to describing + various random ray metrics. These include simulation settings (e.g. + 'source_shape', 'volume_estimator', 'volume_normalized_flux_tallies', + etc.) and performance metrics (e.g. 'avg_miss_rate', + 'n_source_regions', 'n_integrations', etc.) + + .. versionadded:: 0.16.0 run_mode : str Simulation run mode, e.g. 'eigenvalue' runtime : dict @@ -106,6 +127,10 @@ class StatePoint: Pseudorandom number generator seed stride : int Number of random numbers allocated for each particle history + solver_type : str + 'monte carlo', 'random ray' + + .. versionadded:: 0.16.0 source : numpy.ndarray of compound datatype Array of source sites. The compound datatype has fields 'r', 'u', 'E', 'wgt', 'delayed_group', 'surf_id', and 'particle', corresponding to @@ -152,12 +177,14 @@ def __init__(self, filepath, autolink=True): # Automatically link in a summary file if one exists if autolink: - path_summary = os.path.join(os.path.dirname(filename), 'summary.h5') + path_summary = os.path.join( + os.path.dirname(filename), 'summary.h5') if os.path.exists(path_summary): su = openmc.Summary(path_summary) self.link_with_summary(su) - path_volume = os.path.join(os.path.dirname(filename), 'volume_*.h5') + path_volume = os.path.join( + os.path.dirname(filename), 'volume_*.h5') for path_i in glob.glob(path_volume): if re.search(r'volume_\d+\.h5', path_i): vol = openmc.VolumeCalculation.from_hdf5(path_i) @@ -210,9 +237,13 @@ def date_and_time(self): s = self._f.attrs['date_and_time'].decode() return datetime.strptime(s, '%Y-%m-%d %H:%M:%S') + @property + def energy_mode(self): + return self._f['energy_mode'][()].decode() + @property def entropy(self): - if self.run_mode == 'eigenvalue': + if self.run_mode == 'eigenvalue' or self.run_mode == 'time dependent': return self._f['entropy'][()] else: return None @@ -233,7 +264,7 @@ def filters(self): @property def generations_per_batch(self): - if self.run_mode == 'eigenvalue': + if self.run_mode == 'eigenvalue' or self.run_mode == 'time dependent': return self._f['generations_per_batch'][()] else: return None @@ -247,8 +278,8 @@ def global_tallies(self): ('mean', 'f8'), ('std_dev', 'f8')]) gt['name'] = ['k-collision', 'k-absorption', 'k-tracklength', 'leakage'] - gt['sum'] = data[:,1] - gt['sum_sq'] = data[:,2] + gt['sum'] = data[:, 1] + gt['sum_sq'] = data[:, 2] # Calculate mean and sample standard deviation of mean n = self.n_realizations @@ -275,7 +306,7 @@ def k_generation(self): @property def keff(self): - if self.run_mode == 'eigenvalue': + if self.run_mode == 'eigenvalue' or self.run_mode == 'time dependent': return ufloat(*self._f['k_combined'][()]) else: return None @@ -323,13 +354,27 @@ def meshes(self): return self._meshes + @property + def n_energy_groups(self): + if self.energy_mode == 'multi-group': + return self._f['n_energy_groups'][()] + else: + return None + + @property + def n_delay_groups(self): + if self.energy_mode == 'multi-group': + return self._f['n_delay_groups'][()] + else: + return None + @property def n_batches(self): return self._f['n_batches'][()] @property def n_inactive(self): - if self.run_mode == 'eigenvalue': + if self.run_mode == 'eigenvalue' or self.run_mode == 'time dependent': return self._f['n_inactive'][()] else: return None @@ -350,6 +395,21 @@ def path(self): def photon_transport(self): return self._f.attrs['photon_transport'] > 0 + @property + def random_ray(self): + if self.solver_type == 'random ray': + rr = {} + for name, dataset in self._f['random_ray'].items(): + data = dataset[()] + if type(data) == np.bytes_: + data = data.decode() + if name in ['adjoint', 'volume_normalized_flux_tallies']: + data = data > 0 + rr[name] = data + return rr + else: + return None + @property def run_mode(self): return self._f['run_mode'][()].decode() @@ -367,6 +427,10 @@ def seed(self): def stride(self): return self._f['stride'][()] + @property + def solver_type(self): + return self._f['solver_type'][()].decode() + @property def source(self): return self._f['source_bank'][()] if self.source_present else None @@ -428,15 +492,18 @@ def tallies(self): # Create Tally object and assign basic properties tally = openmc.Tally(tally_id) tally._sp_filename = Path(self._f.filename) - tally.name = group['name'][()].decode() if 'name' in group else '' + tally.name = group['name'][()].decode( + ) if 'name' in group else '' # Check if tally has multiply_density attribute if "multiply_density" in group.attrs: - tally.multiply_density = group.attrs["multiply_density"].item() > 0 + tally.multiply_density = group.attrs["multiply_density"].item( + ) > 0 # Check if tally has higher_moments attribute if 'higher_moments' in group.attrs: - tally.higher_moments = bool(group.attrs['higher_moments'][()]) + tally.higher_moments = bool( + group.attrs['higher_moments'][()]) # Read the number of realizations n_realizations = group['n_realizations'][()] @@ -464,7 +531,8 @@ def tallies(self): nuclide_names = group['nuclides'][()] # Add all nuclides to the Tally - tally.nuclides = [name.decode().strip() for name in nuclide_names] + tally.nuclides = [name.decode().strip() + for name in nuclide_names] # Add the scores to the Tally scores = group['score_bins'][()] @@ -709,7 +777,7 @@ def link_with_summary(self, summary): if not isinstance(summary, openmc.Summary): msg = f'Unable to link statepoint with "{summary}" which is not a' \ - 'Summary object' + 'Summary object' raise ValueError(msg) cells = summary.geometry.get_all_cells() diff --git a/src/material.cpp b/src/material.cpp index 21b11b8b9ce..6e98325a443 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -75,6 +75,32 @@ Material::Material(pugi::xml_node node) std::string units; if (density_node) { units = get_node_value(density_node, "units"); + if (settings::kinetic_simulation) { + if (check_for_node(density_node, "value_timeseries")) { + if (units == "sum") { + fatal_error("Use of density_timeseries is incompatible with 'sum'" + "density unit"); + } else { + density_timeseries_ = + get_node_array(density_node, "value_timeseries"); + if (density_timeseries_.size() >= settings::n_timesteps) { + warning(fmt::format( + "Material {} has a density_timeseries (size={}) longer than " + "n_timesteps ({}). Only the first {} entries of the density " + "timeseries " + "will be simulated.", + this->id(), density_timeseries_.size(), settings::n_timesteps, + settings::n_timesteps)); + } else { + fatal_error( + fmt::format("Material {} has a density_timeseries (size={}) " + "shorter than n_timesteps ({}). Not all " + "time steps can be simulated. Aborting.", + this->id(), density_timeseries_.size(), settings::n_timesteps)); + } + } + } + } if (units == "sum") { sum_density = true; } else if (units == "macro") { @@ -90,18 +116,51 @@ Material::Material(pugi::xml_node node) std::to_string(id_) + "."); } + double scale_factor; if (units == "g/cc" || units == "g/cm3") { density_ = -val; + scale_factor = 1.0; } else if (units == "kg/m3") { density_ = -1.0e-3 * val; + scale_factor = -1.0e-3; } else if (units == "atom/b-cm") { density_ = val; + scale_factor = 1.0; } else if (units == "atom/cc" || units == "atom/cm3") { density_ = 1.0e-24 * val; + scale_factor = 1.0e-24; } else { fatal_error("Unknown units '" + units + "' specified on material " + std::to_string(id_) + "."); } + // Scale density_timeseries_ to the same units as density_ + if (density_timeseries_.size() > 0) { + for (double& p : density_timeseries_) { + if (p <= 0) { + fatal_error( + "Zero or negative value detected in material timeseries. This " + "will break any time-dependent simulations. Please fix the " + "density timeseries in your input file. Aborting..."); + } + p *= scale_factor; + } + + // Check that all elements are the same sign. + vector zero_vector; + for (int i = 0; i < density_timeseries_.size(); i++) { + zero_vector.push_back(i); + } + if (!(density_timeseries_ >= zero_vector || + density_timeseries_ <= zero_vector)) { + fatal_error( + "Cannot mix atom and weight percents for density timeseries " + "in material " + + std::to_string(id_)); + } else if ((density_timeseries_ >= zero_vector && density_ <= 0) || + (density_timeseries_ <= zero_vector && density_ >= 0)) { + fatal_error("density_timeseries_ must be same type as density_"); + } + } } } else { fatal_error("Must specify element in material " + @@ -369,6 +428,8 @@ Material& Material::clone() mat->ncrystal_mat_ = ncrystal_mat_.clone(); mat->atom_density_ = atom_density_; mat->density_ = density_; + if (settings::kinetic_simulation) + mat->density_timeseries_ = density_timeseries_; mat->density_gpcc_ = density_gpcc_; mat->volume_ = volume_; mat->fissionable() = fissionable_; @@ -447,6 +508,9 @@ void Material::normalize_density() } sum_percent = 1.0 / sum_percent; density_ = -density_ * N_AVOGADRO / MASS_NEUTRON * sum_percent; + + for (double& p : density_timeseries_) + p = -p * N_AVOGADRO / MASS_NEUTRON * sum_percent; } // Calculate nuclide atom densities diff --git a/src/mgxs.cpp b/src/mgxs.cpp index 1dc090ab969..a4dce78ff8f 100644 --- a/src/mgxs.cpp +++ b/src/mgxs.cpp @@ -365,7 +365,7 @@ Mgxs::Mgxs(const std::string& in_name, const vector& mat_kTs, } } } // end switch - } // end microscopic temperature loop + } // end microscopic temperature loop // Now combine the microscopic data at each relevant temperature // We will do this by treating the multiple temperatures of a nuclide as @@ -426,31 +426,38 @@ double Mgxs::get_xs(MgxsType xstype, int gin, const int* gout, const double* mu, { XsData* xs_t = &xs[t]; double val; + std::string mgxs_type; switch (xstype) { case MgxsType::TOTAL: val = xs_t->total(a, gin); + mgxs_type = "total"; break; case MgxsType::NU_FISSION: val = fissionable ? xs_t->nu_fission(a, gin) : 0.; + mgxs_type = "nu-fission"; break; case MgxsType::ABSORPTION: val = xs_t->absorption(a, gin); - ; + mgxs_type = "absorption"; break; case MgxsType::FISSION: val = fissionable ? xs_t->fission(a, gin) : 0.; + mgxs_type = "fission"; break; case MgxsType::KAPPA_FISSION: val = fissionable ? xs_t->kappa_fission(a, gin) : 0.; + mgxs_type = "kappa-fission"; break; case MgxsType::NU_SCATTER: case MgxsType::SCATTER: case MgxsType::NU_SCATTER_FMU: case MgxsType::SCATTER_FMU: val = xs_t->scatter[a]->get_xs(xstype, gin, gout, mu); + mgxs_type = "scatter_data"; break; case MgxsType::PROMPT_NU_FISSION: val = fissionable ? xs_t->prompt_nu_fission(a, gin) : 0.; + mgxs_type = "prompt-nu-fission"; break; case MgxsType::DELAYED_NU_FISSION: if (fissionable) { @@ -465,6 +472,23 @@ double Mgxs::get_xs(MgxsType xstype, int gin, const int* gout, const double* mu, } else { val = 0.; } + mgxs_type = "delayed-nu-fission"; + break; + case MgxsType::CHI: + if (fissionable) { + if (gout != nullptr) { + val = xs_t->chi(a, gin, *gout); + } else { + // provide an outgoing group-wise sum + val = 0.; + for (int g = 0; g < xs_t->chi.shape()[2]; g++) { + val += xs_t->chi(a, gin, g); + } + } + } else { + val = 0.; + } + mgxs_type = "chi"; break; case MgxsType::CHI_PROMPT: if (fissionable) { @@ -480,6 +504,7 @@ double Mgxs::get_xs(MgxsType xstype, int gin, const int* gout, const double* mu, } else { val = 0.; } + mgxs_type = "chi-prompt"; break; case MgxsType::CHI_DELAYED: if (fissionable) { @@ -507,9 +532,11 @@ double Mgxs::get_xs(MgxsType xstype, int gin, const int* gout, const double* mu, } else { val = 0.; } + mgxs_type = "chi-delayed"; break; case MgxsType::INVERSE_VELOCITY: val = xs_t->inverse_velocity(a, gin); + mgxs_type = "inverse-velocity"; if (!(val > 0)) val = data::mg.default_inverse_velocity_[gin]; break; @@ -519,10 +546,20 @@ double Mgxs::get_xs(MgxsType xstype, int gin, const int* gout, const double* mu, } else { val = xs_t->decay_rate(a, 0); } + mgxs_type = "decay-rate"; break; default: val = 0.; } + // TODO: need to add more robust handling of zero-values cross sections that + // produce NANs on normalization + if (val != val) { + warning( + fmt::format("The {} cross section for this material has a nan present " + "(possibly due to a division by zero). Setting to zero...", + mgxs_type)); + val = 0; + } return val; } diff --git a/src/output.cpp b/src/output.cpp index c66c313dce5..d55918db099 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -609,6 +609,7 @@ const std::unordered_map score_names = { {SCORE_IFP_TIME_NUM, "IFP lifetime numerator"}, {SCORE_IFP_BETA_NUM, "IFP delayed fraction numerator"}, {SCORE_IFP_DENOM, "IFP common denominator"}, + {SCORE_PRECURSORS, "Precursor Population"}, }; //! Create an ASCII output file showing all tally results. diff --git a/src/random_ray/flat_source_domain.cpp b/src/random_ray/flat_source_domain.cpp index 1a6e7c0be46..5c84038d538 100644 --- a/src/random_ray/flat_source_domain.cpp +++ b/src/random_ray/flat_source_domain.cpp @@ -1,4 +1,5 @@ #include "openmc/random_ray/flat_source_domain.h" +#include "openmc/random_ray/bd_utilities.h" #include "openmc/cell.h" #include "openmc/constants.h" @@ -17,7 +18,9 @@ #include "openmc/timer.h" #include "openmc/weight_windows.h" +#include #include +#include #include namespace openmc { @@ -35,7 +38,10 @@ double FlatSourceDomain::diagonal_stabilization_rho_ {1.0}; std::unordered_map>> FlatSourceDomain::mesh_domain_map_; -FlatSourceDomain::FlatSourceDomain() : negroups_(data::mg.num_energy_groups_) +FlatSourceDomain::FlatSourceDomain() + : negroups_(data::mg.num_energy_groups_), + ndgroups_(data::mg.num_delayed_groups_) + { // Count the number of source regions, compute the cell offset // indices, and store the material type The reason for the offsets is that @@ -53,7 +59,7 @@ FlatSourceDomain::FlatSourceDomain() : negroups_(data::mg.num_energy_groups_) // Initialize source regions. bool is_linear = RandomRay::source_shape_ != RandomRaySourceShape::FLAT; - source_regions_ = SourceRegionContainer(negroups_, is_linear); + source_regions_ = SourceRegionContainer(negroups_, ndgroups_, is_linear); // Initialize tally volumes if (volume_normalized_flux_tallies_) { @@ -89,6 +95,12 @@ void FlatSourceDomain::batch_reset() for (int64_t se = 0; se < n_source_elements(); se++) { source_regions_.scalar_flux_new(se) = 0.0; } + + if (settings::kinetic_simulation && settings::create_delayed_neutrons) { +#pragma omp parallel for + for (int64_t de = 0; de < n_delay_elements(); de++) + source_regions_.precursors_new(de) = 0.0; + } } void FlatSourceDomain::accumulate_iteration_flux() @@ -120,21 +132,45 @@ void FlatSourceDomain::update_single_neutron_source(SourceRegionHandle& srh) double sigma_t = sigma_t_[material_offset + g_out] * density_mult; double scatter_source = 0.0; double fission_source = 0.0; + double total_source = 0.0; for (int g_in = 0; g_in < negroups_; g_in++) { double scalar_flux = srh.scalar_flux_old(g_in); double sigma_s = sigma_s_[scatter_offset + g_out * negroups_ + g_in] * density_mult; - double nu_sigma_f = nu_sigma_f_[material_offset + g_in] * density_mult; - double chi = chi_[material_offset + g_out]; - + double nu_sigma_f; + double chi; + if (settings::kinetic_simulation && !simulation::is_initial_condition) { + nu_sigma_f = nu_p_sigma_f_[material_offset + g_in]; + chi = chi_p_[material_offset + g_out]; + } else { + nu_sigma_f = nu_sigma_f_[material_offset + g_in]; + chi = chi_[material_offset + g_out]; + } + nu_sigma_f *= density_mult; scatter_source += sigma_s * scalar_flux; if (settings::create_fission_neutrons) { fission_source += nu_sigma_f * scalar_flux * chi; } } - srh.source(g_out) = - (scatter_source + fission_source * inverse_k_eff) / sigma_t; + total_source = (scatter_source + fission_source * inverse_k_eff); + + // Add delayed source for kinetic simulation if delayed neutrons are + // turned on + if (settings::kinetic_simulation && !simulation::is_initial_condition && + settings::create_delayed_neutrons) { + const int delay_offset = + (material * ntemperature_ + temp) * negroups_ * ndgroups_; + double delayed_source = 0.0; + for (int dg = 0; dg < ndgroups_; dg++) { + double chi_d_lambda = + chi_d_lambda_[delay_offset + dg * negroups_ + g_out]; + double precursors = srh.precursors_old(dg); + delayed_source += chi_d_lambda * precursors; + } + total_source += delayed_source; + } + srh.source(g_out) = total_source / sigma_t; } } @@ -156,6 +192,12 @@ void FlatSourceDomain::update_all_neutron_sources() for (int64_t sr = 0; sr < n_source_regions(); sr++) { SourceRegionHandle srh = source_regions_.get_source_region_handle(sr); update_single_neutron_source(srh); + if (settings::kinetic_simulation && !simulation::is_initial_condition) { + if (RandomRay::time_method_ == RandomRayTimeMethod::ISOTROPIC) + compute_single_phi_prime(srh); + else if (RandomRay::time_method_ == RandomRayTimeMethod::PROPAGATION) + compute_single_T1(srh); + } } simulation::time_update_src.stop(); @@ -210,6 +252,21 @@ void FlatSourceDomain::set_flux_to_flux_plus_source( source_regions_.scalar_flux_new(sr, g) /= (sigma_t * volume); source_regions_.scalar_flux_new(sr, g) += source_regions_.source(sr, g); } + if (settings::kinetic_simulation && !simulation::is_initial_condition) { + double inverse_vbar = + inverse_vbar_[(material * ntemperature_ + temp) * negroups_ + g]; + double scalar_flux_rhs_bd = source_regions_.scalar_flux_rhs_bd(sr, g); + double A0 = + (bd_coefficients_first_order_.at(RandomRay::bd_order_))[0] / settings::dt; + + // TODO: Add support for expicit void regions + double sigma_t = + sigma_t_[(material * ntemperature_ + temp) * negroups_ + g] * + source_regions_.density_mult(sr); + source_regions_.scalar_flux_new(sr, g) -= + scalar_flux_rhs_bd * inverse_vbar / sigma_t; + source_regions_.scalar_flux_new(sr, g) /= 1 + A0 * inverse_vbar / sigma_t; + } } void FlatSourceDomain::set_flux_to_old_flux(int64_t sr, int g) @@ -490,6 +547,10 @@ void FlatSourceDomain::convert_source_regions_to_tallies(int64_t start_sr_id) // Loop over scores for (int score = 0; score < tally.scores_.size(); score++) { auto score_bin = tally.scores_[score]; + // Skip precursor score. These must be scored by delay group via + // tally_delay_task. + if (score_bin == SCORE_PRECURSORS) + break; // If a valid tally, filter, and score combination has been found, // then add it to the list of tally tasks for this source element. TallyTask task(i_tally, filter_index, score, score_bin); @@ -505,6 +566,63 @@ void FlatSourceDomain::convert_source_regions_to_tallies(int64_t start_sr_id) for (auto& match : p.filter_matches()) match.bins_present_ = false; } + // Loop over delayed groups (so as to support tallying delayed quantities) + if (settings::kinetic_simulation && settings::create_delayed_neutrons) { + for (int dg = 0; dg < ndgroups_; dg++) { + + // Set particle to the current delay group + p.delayed_group() = dg; + + int64_t delay_element = sr * ndgroups_ + dg; + + // If this task has already been populated, we don't need to do + // it again. + if (source_regions_.tally_delay_task(sr, dg).size() > 0) { + continue; + } + + // Loop over all active tallies. This logic is essentially identical + // to what happens when scanning for applicable tallies during + // MC transport. + // Loop over all active tallies. This logic is essentially identical + for (int i_tally = 0; i_tally < model::tallies.size(); i_tally++) { + Tally& tally {*model::tallies[i_tally]}; + + // Initialize an iterator over valid filter bin combinations. + // If there are no valid combinations, use a continue statement + // to ensure we skip the assume_separate break below. + auto filter_iter = FilterBinIter(tally, p); + auto end = FilterBinIter(tally, true, &p.filter_matches()); + if (filter_iter == end) + continue; + + // Loop over filter bins. + for (; filter_iter != end; ++filter_iter) { + auto filter_index = filter_iter.index_; + auto filter_weight = filter_iter.weight_; + + // Loop over scores + for (int score = 0; score < tally.scores_.size(); score++) { + auto score_bin = tally.scores_[score]; + // We only want to score precursors + if (score_bin != SCORE_PRECURSORS) + break; + // If a valid tally, filter, and score combination has been found, + // then add it to the list of tally tasks for this source element. + TallyTask task(i_tally, filter_index + dg, score, score_bin); + source_regions_.tally_delay_task(sr, dg).push_back(task); + + // Also add this task to the list of volume tasks for this source + // region. + source_regions_.volume_task(sr).insert(task); + } + } + } + // Reset all the filter matches for the next tally event. + for (auto& match : p.filter_matches()) + match.bins_present_ = false; + } + } } openmc::simulation::time_tallies.stop(); @@ -605,6 +723,7 @@ double FlatSourceDomain::compute_fixed_source_normalization_factor() const // tally function simply traverses the mapping data structure and executes // the scoring operations to OpenMC's native tally result arrays. +// TODO: Add support for prompt and delayed nu fission tallies void FlatSourceDomain::random_ray_tally() { openmc::simulation::time_tallies.start(); @@ -685,10 +804,23 @@ void FlatSourceDomain::random_ray_tally() density_mult; break; + case SCORE_PRECURSORS: + // Score precursors in tally_delay_tasks + if (settings::kinetic_simulation && + settings::create_delayed_neutrons) { + break; + } else { + fatal_error("Invalid score specified in tallies.xml. Precursors " + "are only supported in random ray mode for kinetic " + "simulations when delayed neutrons are turned on."); + } + default: fatal_error("Invalid score specified in tallies.xml. Only flux, " "total, fission, nu-fission, kappa-fission, and events " - "are supported in random ray mode."); + "are supported in random ray mode (precursors are " + "supported in kinetic simulations when delayed neutrons " + "are turned on)."); break; } // Apply score to the appropriate tally bin @@ -698,13 +830,51 @@ void FlatSourceDomain::random_ray_tally() score; } } + if (settings::kinetic_simulation && settings::create_delayed_neutrons) { + for (int dg = 0; dg < ndgroups_; dg++) { + // Determine numerical score value + for (auto& task : source_regions_.tally_delay_task(sr, dg)) { + double score = 0.0; + switch (task.score_type) { + + // Certain scores already tallied + case SCORE_FLUX: + case SCORE_TOTAL: + case SCORE_FISSION: + case SCORE_NU_FISSION: + case SCORE_EVENTS: + break; + + case SCORE_PRECURSORS: + score = source_regions_.precursors_new(sr, dg) * + source_normalization_factor * volume; + break; + + default: + fatal_error( + "Invalid score specified in tallies.xml. Only flux, " + "total, fission, nu-fission, and events are supported in " + "random ray mode (precursors are supported in kinetic " + "simulations when delayed neutrons are turned on)."); + break; + } + + // Apply score to the appropriate tally bin + Tally& tally {*model::tallies[task.tally_idx]}; +#pragma omp atomic + tally.results_(task.filter_idx, task.score_idx, TallyResult::VALUE) += + score; + } + } + } - // For flux tallies, the total volume of the spatial region is needed - // for normalizing the flux. We store this volume in a separate tensor. - // We only contribute to each volume tally bin once per FSR. + // For flux and precursor tallies, the total volume of the spatial region is + // needed for normalizing the tally. We store this volume in a separate + // tensor. We only contribute to each volume tally bin once per FSR. if (volume_normalized_flux_tallies_) { for (const auto& task : source_regions_.volume_task(sr)) { - if (task.score_type == SCORE_FLUX) { + if (task.score_type == SCORE_FLUX || + task.score_type == SCORE_PRECURSORS) { #pragma omp atomic tally_volumes_[task.tally_idx](task.filter_idx, task.score_idx) += volume; @@ -713,11 +883,11 @@ void FlatSourceDomain::random_ray_tally() } } // end FSR loop - // Normalize any flux scores by the total volume of the FSRs scoring to that - // bin. To do this, we loop over all tallies, and then all filter bins, - // and then scores. For each score, we check the tally data structure to - // see what index that score corresponds to. If that score is a flux score, - // then we divide it by volume. + // Normalize any flux or precursor scores by the total volume of the FSRs + // scoring to that bin. To do this, we loop over all tallies, and then all + // filter bins, and then scores. For each score, we check the tally data + // structure to see what index that score corresponds to. If that score is a + // flux or precursor score, then we divide it by volume. if (volume_normalized_flux_tallies_) { for (int i = 0; i < model::tallies.size(); i++) { Tally& tally {*model::tallies[i]}; @@ -725,7 +895,7 @@ void FlatSourceDomain::random_ray_tally() for (int bin = 0; bin < tally.n_filter_bins(); bin++) { for (int score_idx = 0; score_idx < tally.n_scores(); score_idx++) { auto score_type = tally.scores_[score_idx]; - if (score_type == SCORE_FLUX) { + if (score_type == SCORE_FLUX || score_type == SCORE_PRECURSORS) { double vol = tally_volumes_[i](bin, score_idx); if (vol > 0.0) { tally.results_(bin, score_idx, TallyResult::VALUE) /= vol; @@ -1185,8 +1355,7 @@ void FlatSourceDomain::flatten_xs() m.get_xs(MgxsType::FISSION, g_out, NULL, NULL, NULL, t, a); sigma_f_.push_back(sigma_f); - double chi = - m.get_xs(MgxsType::CHI_PROMPT, g_out, &g_out, NULL, NULL, t, a); + double chi = m.get_xs(MgxsType::CHI, g_out, &g_out, NULL, NULL, t, a); if (!std::isfinite(chi)) { // MGXS interface may return NaN in some cases, such as when // material is fissionable but has very small sigma_f. @@ -1208,6 +1377,25 @@ void FlatSourceDomain::flatten_xs() if (g_out == g_in && sigma_s < 0.0) is_transport_stabilization_needed_ = true; } + // Prompt cross-sections for kinetic simulations + if (settings::kinetic_simulation) { + double chi_p = + m.get_xs(MgxsType::CHI_PROMPT, g_out, &g_out, NULL, NULL, t, a); + if (!std::isfinite(chi_p)) { + // MGXS interface may return NaN in some cases, such as when + // material is fissionable but has very small sigma_f. + chi_p = 0.0; + } + chi_p_.push_back(chi_p); + + double inverse_vbar = m.get_xs( + MgxsType::INVERSE_VELOCITY, g_out, NULL, NULL, NULL, t, a); + inverse_vbar_.push_back(inverse_vbar); + + double nu_p_Sigma_f = m.get_xs( + MgxsType::PROMPT_NU_FISSION, g_out, NULL, NULL, NULL, t, a); + nu_p_sigma_f_.push_back(nu_p_Sigma_f); + } } else { sigma_t_.push_back(0); nu_sigma_f_.push_back(0); @@ -1217,6 +1405,40 @@ void FlatSourceDomain::flatten_xs() for (int g_in = 0; g_in < negroups_; g_in++) { sigma_s_.push_back(0); } + if (settings::kinetic_simulation) { + chi_p_.push_back(0); + inverse_vbar_.push_back(0); + nu_p_sigma_f_.push_back(0); + } + } + } + // Delayed cross sections for time-dependent simulations + if (settings::kinetic_simulation) { + for (int dg = 0; dg < ndgroups_; dg++) { + if (m.exists_in_model) { + double lambda = + m.get_xs(MgxsType::DECAY_RATE, 0, NULL, NULL, &dg, t, a); + lambda_.push_back(lambda); + for (int g_out = 0; g_out < negroups_; g_out++) { + double nu_d_Sigma_f = m.get_xs( + MgxsType::DELAYED_NU_FISSION, g_out, NULL, NULL, &dg, t, a); + nu_d_sigma_f_.push_back(nu_d_Sigma_f); + double chi_d = + m.get_xs(MgxsType::CHI_DELAYED, g_out, &g_out, NULL, &dg, t, a); + if (!std::isfinite(chi_d)) { + // MGXS interface may return NaN in some cases, such as when + // material is fissionable but has very small sigma_f. + chi_d = 0.0; + } + chi_d_lambda_.push_back(chi_d * lambda); + } + } else { + lambda_.push_back(0); + for (int g_out = 0; g_out < negroups_; g_out++) { + nu_d_sigma_f_.push_back(0); + chi_d_lambda_.push_back(0); + } + } } } } @@ -1333,6 +1555,17 @@ void FlatSourceDomain::serialize_final_fluxes(vector& flux) } } +void FlatSourceDomain::serialize_final_sources(vector& source) +{ + // Ensure array is correct size + source.resize(n_source_regions() * negroups_); + // Serialize the final sources for output +#pragma omp parallel for + for (int64_t se = 0; se < n_source_elements(); se++) { + source[se] = source_regions_.source_final(se); + } +} + void FlatSourceDomain::apply_mesh_to_cell_instances(int32_t i_cell, int32_t mesh_idx, int target_material_id, const vector& instances, bool is_target_void) @@ -1528,8 +1761,8 @@ SourceRegionHandle FlatSourceDomain::get_subdivided_source_region_handle( // Call the basic constructor for the source region and store in the parallel // map. bool is_linear = RandomRay::source_shape_ != RandomRaySourceShape::FLAT; - SourceRegion* sr_ptr = - discovered_source_regions_.emplace(sr_key, {negroups_, is_linear}); + SourceRegion* sr_ptr = discovered_source_regions_.emplace( + sr_key, {negroups_, ndgroups_, is_linear}); SourceRegionHandle handle {*sr_ptr}; // Determine the material @@ -1554,6 +1787,11 @@ SourceRegionHandle FlatSourceDomain::get_subdivided_source_region_handle( } } + if (settings::kinetic_simulation && material == MATERIAL_VOID) { + fatal_error("Explicit void treatment for kinetic simulations " + " is not currently supported."); + } + handle.material() = material; handle.temperature_idx() = temp; @@ -1597,6 +1835,12 @@ SourceRegionHandle FlatSourceDomain::get_subdivided_source_region_handle( // Compute the combined source term update_single_neutron_source(handle); + if (settings::kinetic_simulation && !simulation::is_initial_condition) { + if (RandomRay::time_method_ == RandomRayTimeMethod::ISOTROPIC) + compute_single_phi_prime(handle); + else if (RandomRay::time_method_ == RandomRayTimeMethod::PROPAGATION) + compute_single_T1(handle); + } // Unlock the parallel map. Note: we may be tempted to release // this lock earlier, and then just use the source region's lock to protect @@ -1750,4 +1994,306 @@ int64_t FlatSourceDomain::lookup_mesh_bin(int64_t sr, Position r) const return mesh_bin; } +//------------------------------------------------------------------------------ +// Methods for kinetic simulations + +// Generates new estimate of k_dynamic based on the fraction between this +// timestep's estimate of neutron production and loss. (previous timestep +// fission vs current timestep fission?) +// TODO: implement compute_k_dynamic + +// Compute new estimate of scattering + fission (+ precursor decay for +// kinetic simulations) sources in each source region based on the flux +// estimate from the previous iteration. + +// TODO: support void regions +void FlatSourceDomain::compute_single_phi_prime(SourceRegionHandle& srh) +{ + double A0 = + (bd_coefficients_first_order_.at(RandomRay::bd_order_))[0] / settings::dt; + int material = srh.material(); + int temp = srh.temperature_idx(); + double density_mult = srh.density_mult(); + const int material_offset = (material * ntemperature_ + temp) * negroups_; + for (int g = 0; g < negroups_; g++) { + double inverse_vbar = inverse_vbar_[material_offset + g]; + // TODO: add support for explicit void + double sigma_t = sigma_t_[material_offset + g] * density_mult; + + double scalar_flux_time_derivative = + A0 * srh.scalar_flux_old(g) + srh.scalar_flux_rhs_bd(g); + srh.phi_prime(g) = + scalar_flux_time_derivative * inverse_vbar / (4 * PI * sigma_t); + } +} + +// T1 calculation +// TODO: support void regions +void FlatSourceDomain::compute_single_T1(SourceRegionHandle& srh) +{ + double A0 = + (bd_coefficients_first_order_.at(RandomRay::bd_order_))[0] / settings::dt; + double B0 = (bd_coefficients_second_order_.at(RandomRay::bd_order_))[0] / + (settings::dt * settings::dt); + int material = srh.material(); + int temp = srh.temperature_idx(); + double density_mult = srh.density_mult(); + const int material_offset = (material * ntemperature_ + temp) * negroups_; + for (int g = 0; g < negroups_; g++) { + double inverse_vbar = inverse_vbar_[material_offset + g]; + // TODO: add support for explicit void + double sigma_t = sigma_t_[material_offset + g] * density_mult; + + // Multiply out sigma_t to correctly compute the derivative term + float source_time_derivative = + A0 * srh.source(g) * sigma_t + srh.source_rhs_bd(g); + + double scalar_flux_time_derivative_2 = + B0 * srh.scalar_flux_old(g) + srh.scalar_flux_rhs_bd_2(g); + scalar_flux_time_derivative_2 *= inverse_vbar / (4 * PI); + + // Divide by sigma_t to save time during transport + srh.T1(g) = + (source_time_derivative - scalar_flux_time_derivative_2) / sigma_t; + } +} + +void FlatSourceDomain::compute_single_delayed_fission_source( + SourceRegionHandle& srh) +{ + + // Reset all delayed fission sources to zero (important for void regions) + for (int dg = 0; dg < ndgroups_; dg++) { + srh.delayed_fission_source(dg) = 0.0; + } + + int material = srh.material(); + int temp = srh.temperature_idx(); + double density_mult = srh.density_mult(); + const int delay_offset = + (material * ntemperature_ + temp) * negroups_ * ndgroups_; + if (material != MATERIAL_VOID) { + double inverse_k_eff = 1.0 / k_eff_; + for (int dg = 0; dg < ndgroups_; dg++) { + // We cannot have delayed neutrons if there is no delayed data + double lambda = lambda_[material * ndgroups_ + dg]; + if (lambda != 0.0) { + for (int g = 0; g < negroups_; g++) { + double scalar_flux = scalar_flux = srh.scalar_flux_new(g); + double nu_d_sigma_f = + nu_d_sigma_f_[delay_offset + dg * negroups_ + g] * density_mult; + srh.delayed_fission_source(dg) += nu_d_sigma_f * scalar_flux; + } + srh.delayed_fission_source(dg) *= inverse_k_eff; + } + } + } +} + +void FlatSourceDomain::compute_single_precursors(SourceRegionHandle& srh) +{ + // Reset all precursors to zero (important for void regions) + for (int dg = 0; dg < ndgroups_; dg++) { + srh.precursors_new(dg) = 0.0; + } + + int material = srh.material(); + int temp = srh.temperature_idx(); + const int delay_offset = (material * ntemperature_ + temp) * ndgroups_; + if (material != MATERIAL_VOID) { + for (int dg = 0; dg < ndgroups_; dg++) { + double lambda = lambda_[delay_offset + dg]; + if (lambda != 0.0) { + double delayed_fission_source = srh.delayed_fission_source(dg); + if (simulation::is_initial_condition) { + srh.precursors_new(dg) = delayed_fission_source / lambda; + } else { + double precursor_rhs_bd = srh.precursors_rhs_bd(dg); + srh.precursors_new(dg) = delayed_fission_source - precursor_rhs_bd; + double A0 = + (bd_coefficients_first_order_.at(RandomRay::bd_order_))[0] / + settings::dt; + srh.precursors_new(dg) /= A0 + lambda; + } + } + } + } +} + +void FlatSourceDomain::compute_all_precursors() +{ + simulation::time_compute_precursors.start(); + +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + SourceRegionHandle srh = source_regions_.get_source_region_handle(sr); + compute_single_delayed_fission_source(srh); + compute_single_precursors(srh); + } + + simulation::time_compute_precursors.start(); +} + +void FlatSourceDomain::serialize_final_precursors(vector& precursors) +{ + // Ensure array is correct size + precursors.resize(n_source_regions() * ndgroups_); +// Serialize the precursors for output +#pragma omp parallel for + for (int64_t de = 0; de < n_delay_elements(); de++) { + precursors[de] = source_regions_.precursors_final(de); + } +} + +void FlatSourceDomain::precursors_swap() +{ + source_regions_.precursors_swap(); +} + +void FlatSourceDomain::accumulate_iteration_quantities() +{ + accumulate_iteration_flux(); + if (settings::kinetic_simulation) { +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + for (int g = 0; g < negroups_; g++) { + if (RandomRay::time_method_ == RandomRayTimeMethod::PROPAGATION) { + source_regions_.source_final(sr, g) += source_regions_.source(sr, g); + } + } + if (settings::create_delayed_neutrons) { + for (int dg = 0; dg < ndgroups_; dg++) { + source_regions_.precursors_final(sr, dg) += + source_regions_.precursors_new(sr, dg); + } + } + } + } +} + +void FlatSourceDomain::normalize_final_quantities() +{ + double normalization_factor = + 1.0 / (settings::n_batches - settings::n_inactive); + double source_normalization_factor; + if (!settings::kinetic_simulation || + settings::kinetic_simulation && + simulation::current_timestep == settings::n_timesteps) + source_normalization_factor = + compute_fixed_source_normalization_factor() * normalization_factor; + else + // The source normalization should only be applied to internal quantities at + // the end of time stepping in preparation for an adjoint solve. + source_normalization_factor = 1.0 * normalization_factor; + +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + for (int g = 0; g < negroups_; g++) { + source_regions_.scalar_flux_final(sr, g) *= source_normalization_factor; + if (RandomRay::time_method_ == RandomRayTimeMethod::PROPAGATION) { + source_regions_.source_final(sr, g) *= source_normalization_factor; + } + } + if (settings::kinetic_simulation) { + for (int dg = 0; dg < ndgroups_; dg++) { + source_regions_.precursors_final(sr, dg) *= source_normalization_factor; + } + } + } +} + +void FlatSourceDomain::propagate_final_quantities() +{ +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + for (int g = 0; g < negroups_; g++) { + source_regions_.scalar_flux_old(sr, g) = + source_regions_.scalar_flux_final(sr, g); + } + if (settings::create_delayed_neutrons) { + for (int dg = 0; dg < ndgroups_; dg++) { + source_regions_.precursors_old(sr, dg) = + source_regions_.precursors_final(sr, dg); + } + } + } +} + +void FlatSourceDomain::store_time_step_quantities(bool increment_not_initialize) +{ +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + for (int g = 0; g < negroups_; g++) { + int j = 0; + if (RandomRay::time_method_ == RandomRayTimeMethod::PROPAGATION) + j = 1; + add_value_to_bd_vector(source_regions_.scalar_flux_bd(sr, g), + source_regions_.scalar_flux_final(sr, g), increment_not_initialize, + RandomRay::bd_order_ + j); + if (RandomRay::time_method_ == RandomRayTimeMethod::PROPAGATION) { + // Multiply out sigma_t to store the base source + int material = source_regions_.material(sr); + int temp = source_regions_.temperature_idx(sr); + double density_mult = source_regions_.density_mult(sr); + // TODO: add support for explicit void regions + double sigma_t = + sigma_t_[(material * ntemperature_ + temp) * negroups_ + g] * + density_mult; + float source = source_regions_.source_final(sr, g) * sigma_t; + add_value_to_bd_vector(source_regions_.source_bd(sr, g), source, + increment_not_initialize, RandomRay::bd_order_); + } + } + if (settings::create_delayed_neutrons) { + for (int dg = 0; dg < ndgroups_; dg++) { + add_value_to_bd_vector(source_regions_.precursors_bd(sr, dg), + source_regions_.precursors_final(sr, dg), increment_not_initialize, + RandomRay::bd_order_); + } + } + } +} + +void FlatSourceDomain::compute_rhs_bd_quantities() +{ +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + for (int g = 0; g < negroups_; g++) { + source_regions_.scalar_flux_rhs_bd(sr, g) = + rhs_backwards_difference(source_regions_.scalar_flux_bd(sr, g), + RandomRay::bd_order_, settings::dt); + + if (RandomRay::time_method_ == RandomRayTimeMethod::PROPAGATION) { + source_regions_.source_rhs_bd(sr, g) = rhs_backwards_difference( + source_regions_.source_bd(sr, g), RandomRay::bd_order_, settings::dt); + + source_regions_.scalar_flux_rhs_bd_2(sr, g) = + rhs_backwards_difference(source_regions_.scalar_flux_bd(sr, g), + RandomRay::bd_order_, settings::dt, 2); + } + } + if (settings::create_delayed_neutrons) { + for (int dg = 0; dg < ndgroups_; dg++) { + source_regions_.precursors_rhs_bd(sr, dg) = + rhs_backwards_difference(source_regions_.precursors_bd(sr, dg), + RandomRay::bd_order_, settings::dt); + } + } + } +} + +// Update material density by source region +void FlatSourceDomain::update_material_density(int i) +{ +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + int material = source_regions_.material(sr); + auto& mat {model::materials[material]}; + if (mat->density_timeseries_.size() != 0) { + double density_factor = mat->density_timeseries_[i] / mat->density_; + source_regions_.density_mult(sr) = density_factor; + } + } +} + } // namespace openmc diff --git a/src/random_ray/random_ray.cpp b/src/random_ray/random_ray.cpp index dde5023e44f..66debc4c274 100644 --- a/src/random_ray/random_ray.cpp +++ b/src/random_ray/random_ray.cpp @@ -241,10 +241,21 @@ unique_ptr RandomRay::ray_source_; RandomRaySourceShape RandomRay::source_shape_ {RandomRaySourceShape::FLAT}; RandomRaySampleMethod RandomRay::sample_method_ {RandomRaySampleMethod::PRNG}; +double RandomRay::avg_miss_rate_; +int64_t RandomRay::n_source_regions_; +int64_t RandomRay::n_external_source_regions_; +uint64_t RandomRay::total_geometric_intersections_; + +// Kinetic simulation variables +int RandomRay::bd_order_ {3}; // order 3 BD balances accuracy with speed nicely +RandomRayTimeMethod RandomRay::time_method_ {RandomRayTimeMethod::ISOTROPIC}; + RandomRay::RandomRay() : angular_flux_(data::mg.num_energy_groups_), delta_psi_(data::mg.num_energy_groups_), - negroups_(data::mg.num_energy_groups_) + negroups_(data::mg.num_energy_groups_), + angular_flux_prime_(data::mg.num_energy_groups_) + { if (source_shape_ == RandomRaySourceShape::LINEAR || source_shape_ == RandomRaySourceShape::LINEAR_XY) { @@ -402,6 +413,7 @@ void RandomRay::attenuate_flux_inner( break; case RandomRaySourceShape::LINEAR: case RandomRaySourceShape::LINEAR_XY: + // TODO: time-dependent linear source regions if (srh.material() == MATERIAL_VOID) { attenuate_flux_linear_source_void(srh, distance, is_active, r); } else { @@ -444,6 +456,22 @@ void RandomRay::attenuate_flux_flat_source( float tau = sigma_t * distance; float exponential = cjosey_exponential(tau); // exponential = 1 - exp(-tau) float new_delta_psi = (angular_flux_[g] - srh.source(g)) * exponential; + if (settings::kinetic_simulation && !simulation::is_initial_condition) { + if (RandomRay::time_method_ == RandomRayTimeMethod::ISOTROPIC) { + new_delta_psi += srh.phi_prime(g) * exponential; + } else if (RandomRay::time_method_ == RandomRayTimeMethod::PROPAGATION) { + // Source Derivative Propogation terms for Characteristic Equation + float inverse_vbar = domain_->inverse_vbar_[material * negroups_ + g]; + float T1 = srh.T1(g); + float new_delta_psi_prime = (angular_flux_prime_[g] - T1); + new_delta_psi += T1 * inverse_vbar * exponential / sigma_t; + new_delta_psi += + distance * inverse_vbar * new_delta_psi_prime * (1 - exponential); + + // Time Derivative Characteristic Equation + angular_flux_prime_[g] -= new_delta_psi_prime * exponential; + } + } delta_psi_[g] = new_delta_psi; angular_flux_[g] -= new_delta_psi; } @@ -481,6 +509,7 @@ void RandomRay::attenuate_flux_flat_source( } // Alternative flux attenuation function for true void regions. +// TODO: Implement support for time-dependent voids void RandomRay::attenuate_flux_flat_source_void( SourceRegionHandle& srh, double distance, bool is_active, Position r) { @@ -824,6 +853,12 @@ void RandomRay::initialize_ray(uint64_t ray_id, FlatSourceDomain* domain) for (int g = 0; g < negroups_; g++) { angular_flux_[g] = srh.source(g); } + if (settings::kinetic_simulation && !simulation::is_initial_condition && + RandomRay::time_method_ == RandomRayTimeMethod::PROPAGATION) { + for (int g = 0; g < negroups_; g++) { + angular_flux_prime_[g] = srh.T1(g); + } + } } } diff --git a/src/random_ray/random_ray_simulation.cpp b/src/random_ray/random_ray_simulation.cpp index 80cbfc3fe5b..8fde63a34da 100644 --- a/src/random_ray/random_ray_simulation.cpp +++ b/src/random_ray/random_ray_simulation.cpp @@ -43,10 +43,22 @@ void validate_random_ray_inputs() case SCORE_EVENTS: case SCORE_KAPPA_FISSION: break; + + // TODO: add support for prompt and delayed fission + case SCORE_PRECURSORS: { + if (settings::kinetic_simulation && settings::create_delayed_neutrons) { + break; + } else { + fatal_error("Invalid score specified in tallies.xml. Precursors can " + "only be scored for kinetic simulations."); + } + } default: fatal_error( "Invalid score specified. Only flux, total, fission, nu-fission, " - "kappa-fission, and event scores are supported in random ray mode."); + "kappa-fission and event scores are supported in random ray mode. " + "(precursors are supported for kinetic simulations when delayed " + "neutrons are turned on)."); } } @@ -64,15 +76,25 @@ void validate_random_ray_inputs() case FilterType::UNIVERSE: case FilterType::PARTICLE: break; + case FilterType::DELAYED_GROUP: + if (settings::kinetic_simulation) { + break; + } else { + fatal_error("Invalid filter specified in tallies.xml. Kinetic " + "simulations is required " + "to tally with a delayed_group filter."); + } default: fatal_error("Invalid filter specified. Only cell, cell_instance, " "distribcell, energy, material, mesh, and universe filters " - "are supported in random ray mode."); + "are supported in random ray mode (delayed_group is " + "supported for kinetic simulations)."); } } } - // Validate MGXS data + // TODO: validate kinetic data is present + // Validate MGXS data /////////////////////////////////////////////////////////////////// for (auto& material : data::mg.macro_xs_) { if (!material.is_isotropic) { @@ -237,6 +259,85 @@ void validate_random_ray_inputs() } } +void openmc_reset_random_ray() +{ + FlatSourceDomain::volume_estimator_ = RandomRayVolumeEstimator::HYBRID; + FlatSourceDomain::volume_normalized_flux_tallies_ = false; + FlatSourceDomain::adjoint_ = false; + FlatSourceDomain::mesh_domain_map_.clear(); + RandomRay::ray_source_.reset(); + RandomRay::source_shape_ = RandomRaySourceShape::FLAT; + RandomRay::sample_method_ = RandomRaySampleMethod::PRNG; + RandomRay::bd_order_ = 3; + RandomRay::time_method_ = RandomRayTimeMethod::ISOTROPIC; +} + +void write_random_ray_hdf5(hid_t group) +{ + hid_t random_ray_group = create_group(group, "random_ray"); + switch (RandomRay::source_shape_) { + case RandomRaySourceShape::FLAT: + write_dataset(random_ray_group, "source_shape", "flat"); + break; + case RandomRaySourceShape::LINEAR: + write_dataset(random_ray_group, "source_shape", "linear"); + break; + case RandomRaySourceShape::LINEAR_XY: + write_dataset(random_ray_group, "source_shape", "linear xy"); + break; + default: + break; + } + + switch (FlatSourceDomain::volume_estimator_) { + case RandomRayVolumeEstimator::SIMULATION_AVERAGED: + write_dataset(random_ray_group, "volume_estimator", "simulation averaged"); + break; + case RandomRayVolumeEstimator::NAIVE: + write_dataset(random_ray_group, "volume_estimator", "naive"); + break; + case RandomRayVolumeEstimator::HYBRID: + write_dataset(random_ray_group, "volume_estimator", "hybrid"); + break; + default: + break; + } + + write_dataset( + random_ray_group, "distance_active", RandomRay::distance_active_); + write_dataset( + random_ray_group, "distance_inactive", RandomRay::distance_inactive_); + write_dataset(random_ray_group, "volume_normalized_flux_tallies", + FlatSourceDomain::volume_normalized_flux_tallies_); + write_dataset(random_ray_group, "adjoint_mode", FlatSourceDomain::adjoint_); + + write_dataset(random_ray_group, "avg_miss_rate", RandomRay::avg_miss_rate_); + write_dataset( + random_ray_group, "n_source_regions", RandomRay::n_source_regions_); + write_dataset(random_ray_group, "n_external_source_regions", + RandomRay::n_external_source_regions_); + write_dataset(random_ray_group, "n_geometric_intersections", + RandomRay::total_geometric_intersections_); + int64_t n_integrations = + RandomRay::total_geometric_intersections_ * data::mg.num_energy_groups_; + write_dataset(random_ray_group, "n_integrations", n_integrations); + + if (settings::kinetic_simulation && !simulation::is_initial_condition) { + write_dataset(random_ray_group, "bd_order", RandomRay::bd_order_); + switch (RandomRay::time_method_) { + case RandomRayTimeMethod::ISOTROPIC: + write_dataset(random_ray_group, "time_method", "isotropic"); + break; + case RandomRayTimeMethod::PROPAGATION: + write_dataset(random_ray_group, "time_method", "propogation"); + break; + default: + break; + } + } + close_group(random_ray_group); +} + void print_adjoint_header() { if (!FlatSourceDomain::adjoint_) @@ -248,12 +349,30 @@ void print_adjoint_header() header("ADJOINT FLUX SOLVE", 3); } +//----------------------------------------------------------------------------- +// Non-member functions for kinetic simulations + +void rename_time_step_file( + std::string base_filename, std::string extension, int i) +{ + // Rename file + std::string old_filename_ = + fmt::format("{0}{1}{2}", settings::path_output, base_filename, extension); + std::string new_filename_ = fmt::format( + "{0}{1}_{2}{3}", settings::path_output, base_filename, i, extension); + + const char* old_fname = old_filename_.c_str(); + const char* new_fname = new_filename_.c_str(); + std::rename(old_fname, new_fname); +} + //============================================================================== // RandomRaySimulation implementation //============================================================================== RandomRaySimulation::RandomRaySimulation() - : negroups_(data::mg.num_energy_groups_) + : negroups_(data::mg.num_energy_groups_), + ndgroups_(data::mg.num_delayed_groups_) { // There are no source sites in random ray mode, so be sure to disable to // ensure we don't attempt to write source sites to statepoint @@ -288,6 +407,15 @@ RandomRaySimulation::RandomRaySimulation() // The first simulation is run after initialization is_first_simulation_ = true; + + // Initialize vectors used for baking in the initial condition during time + // stepping + if (settings::kinetic_simulation) { + // Initialize vars used for time-consistent seed approach + static_avg_k_eff_; + static_k_eff_; + static_fission_rate_; + } } void RandomRaySimulation::apply_fixed_sources_and_mesh_domains() @@ -310,6 +438,9 @@ void RandomRaySimulation::prepare_fixed_sources_adjoint() void RandomRaySimulation::prepare_adjoint_simulation() { + // Reset all simulation values + domain_->source_regions_.simulation_reset(); + // Configure the domain for adjoint simulation FlatSourceDomain::adjoint_ = true; @@ -356,7 +487,7 @@ void RandomRaySimulation::simulate() // Reset total starting particle weight used for normalizing tallies simulation::total_weight = 1.0; - // Update source term (scattering + fission) + // Update source term (scattering + fission (+ delayed if kinetic)) domain_->update_all_neutron_sources(); // Reset scalar fluxes, iteration volume tallies, and region hit flags @@ -399,19 +530,34 @@ void RandomRaySimulation::simulate() domain_->apply_transport_stabilization(); if (settings::run_mode == RunMode::EIGENVALUE) { - // Compute random ray k-eff - domain_->compute_k_eff(); + // Compute random ray k-eff for initial condition. + // This keff will be preserved + if (simulation::is_initial_condition) { + domain_->compute_k_eff(); + if (settings::kinetic_simulation && simulation::k_eff_correction) { + static_fission_rate_.push_back(domain_->fission_rate_); + static_k_eff_.push_back(domain_->k_eff_); + } + } else { + domain_->k_eff_ = static_k_eff_[simulation::current_batch - 1]; + domain_->fission_rate_ = + static_fission_rate_[simulation::current_batch - 1]; + } // Store random ray k-eff into OpenMC's native k-eff variable global_tally_tracklength = domain_->k_eff_; } + // Compute precursors if delayed neutrons are turned on + if (settings::kinetic_simulation && settings::create_delayed_neutrons) + domain_->compute_all_precursors(); + // Execute all tallying tasks, if this is an active batch if (simulation::current_batch > settings::n_inactive) { // Add this iteration's scalar flux estimate to final accumulated // estimate - domain_->accumulate_iteration_flux(); + domain_->accumulate_iteration_quantities(); // Use above mapping to contribute FSR flux data to appropriate // tallies @@ -420,11 +566,20 @@ void RandomRaySimulation::simulate() // Set phi_old = phi_new domain_->flux_swap(); + if (settings::kinetic_simulation && settings::create_delayed_neutrons) { + domain_->precursors_swap(); + } // Check for any obvious insabilities/nans/infs instability_check(n_hits, domain_->k_eff_, avg_miss_rate_); } // End MPI master work + // Store simulation metrics + RandomRay::avg_miss_rate_ = avg_miss_rate_ / settings::n_batches; + RandomRay::total_geometric_intersections_ = total_geometric_intersections_; + RandomRay::n_external_source_regions_ = domain_->n_external_source_regions_; + RandomRay::n_source_regions_ = domain_->n_source_regions(); + // Finalize the current batch finalize_generation(); finalize_batch(); @@ -435,16 +590,8 @@ void RandomRaySimulation::simulate() // End main simulation timer simulation::time_total.stop(); - // Normalize and save the final flux - double source_normalization_factor = - domain_->compute_fixed_source_normalization_factor() / - (settings::n_batches - settings::n_inactive); - -#pragma omp parallel for - for (uint64_t se = 0; se < domain_->n_source_elements(); se++) { - domain_->source_regions_.scalar_flux_final(se) *= - source_normalization_factor; - } + // Normalize and save the final forward quantities + domain_->normalize_final_quantities(); // Finalize OpenMC openmc_simulation_finalize(); @@ -458,13 +605,56 @@ void RandomRaySimulation::simulate() is_first_simulation_ = false; } +void RandomRaySimulation::initialize_time_step(int i) +{ + if (simulation::k_eff_correction) + static_avg_k_eff_ = simulation::keff; + domain_->k_eff_ = static_avg_k_eff_; + + // Increment current timestep and simuation time + simulation::current_timestep = i + 1; + + // Propagate previous converted solution for kinetic simulation + domain_->source_regions_.simulation_reset(); + domain_->propagate_final_quantities(); + domain_->source_regions_.time_step_reset(); + + if (!simulation::is_initial_condition) { + // Compute RHS backward differences + domain_->compute_rhs_bd_quantities(); + + // Update time dependent cross section based on the density + domain_->update_material_density(i); + + simulation::current_time += settings::dt; + } +} + +void RandomRaySimulation::finalize_time_step() +{ + if (simulation::is_initial_condition) { + // Initialize the BD arrays if initial condition + domain_->store_time_step_quantities(false); + // Toggle off initial condition and source correction + simulation::is_initial_condition = false; + simulation::k_eff_correction = false; + } else { + // Else, store final quantities for the current time step + domain_->store_time_step_quantities(); + } + + // Rename statepoint and tallies file for the current time step + rename_time_step_file(fmt::format("statepoint.{0}", settings::n_batches), + ".h5", simulation::current_timestep); + if (settings::output_tallies) + rename_time_step_file("tallies", ".out", simulation::current_timestep); +} + void RandomRaySimulation::output_simulation_results() const { // Print random ray results if (mpi::master) { - print_results_random_ray(total_geometric_intersections_, - avg_miss_rate_ / settings::n_batches, negroups_, - domain_->n_source_regions(), domain_->n_external_source_regions_); + print_results_random_ray(); if (model::plots.size() > 0) { domain_->output_to_vtk(); } @@ -502,20 +692,22 @@ void RandomRaySimulation::instability_check( } // Print random ray simulation results -void RandomRaySimulation::print_results_random_ray( - uint64_t total_geometric_intersections, double avg_miss_rate, int negroups, - int64_t n_source_regions, int64_t n_external_source_regions) const +void RandomRaySimulation::print_results_random_ray() const { using namespace simulation; if (settings::verbosity >= 6) { - double total_integrations = total_geometric_intersections * negroups; + double total_integrations = + RandomRay::total_geometric_intersections_ * negroups_; double time_per_integration = simulation::time_transport.elapsed() / total_integrations; double misc_time = time_total.elapsed() - time_update_src.elapsed() - time_transport.elapsed() - time_tallies.elapsed() - time_bank_sendrecv.elapsed(); + if (settings::kinetic_simulation && !simulation::is_initial_condition) { + misc_time -= time_update_bd_vectors.elapsed(); + } header("Simulation Statistics", 4); fmt::print( " Total Iterations = {}\n", settings::n_batches); @@ -525,18 +717,24 @@ void RandomRaySimulation::print_results_random_ray( RandomRay::distance_inactive_); fmt::print(" Active Distance = {} cm\n", RandomRay::distance_active_); - fmt::print(" Source Regions (SRs) = {}\n", n_source_regions); - fmt::print( - " SRs Containing External Sources = {}\n", n_external_source_regions); + fmt::print(" Source Regions (SRs) = {}\n", + RandomRay::n_source_regions_); + fmt::print(" SRs Containing External Sources = {}\n", + RandomRay::n_external_source_regions_); fmt::print(" Total Geometric Intersections = {:.4e}\n", - static_cast(total_geometric_intersections)); + static_cast(RandomRay::total_geometric_intersections_)); fmt::print(" Avg per Iteration = {:.4e}\n", - static_cast(total_geometric_intersections) / settings::n_batches); + static_cast(RandomRay::total_geometric_intersections_) / + settings::n_batches); fmt::print(" Avg per Iteration per SR = {:.2f}\n", - static_cast(total_geometric_intersections) / - static_cast(settings::n_batches) / n_source_regions); - fmt::print(" Avg SR Miss Rate per Iteration = {:.4f}%\n", avg_miss_rate); - fmt::print(" Energy Groups = {}\n", negroups); + static_cast(RandomRay::total_geometric_intersections_) / + static_cast(settings::n_batches) / + RandomRay::n_source_regions_); + fmt::print(" Avg SR Miss Rate per Iteration = {:.4f}%\n", + RandomRay::avg_miss_rate_); + fmt::print(" Energy Groups = {}\n", negroups_); + if (settings::kinetic_simulation) + fmt::print(" Delay Groups = {}\n", ndgroups_); fmt::print( " Total Integrations = {:.4e}\n", total_integrations); fmt::print(" Avg per Iteration = {:.4e}\n", @@ -596,6 +794,15 @@ void RandomRaySimulation::print_results_random_ray( } else { fmt::print(" Transport XS Stabilization Used = NO\n"); } + if (settings::kinetic_simulation && !simulation::is_initial_condition) { + std::string time_method = + (RandomRay::time_method_ == RandomRayTimeMethod::ISOTROPIC) + ? "ISOTROPIC" + : "PROPAGATION"; + fmt::print(" Time Method = {}\n", time_method); + fmt::print( + " Backwards Difference Order = {}\n", RandomRay::bd_order_); + } header("Timing Statistics", 4); show_time("Total time for initialization", time_initialize.elapsed()); @@ -603,6 +810,11 @@ void RandomRaySimulation::print_results_random_ray( show_time("Total simulation time", time_total.elapsed()); show_time("Transport sweep only", time_transport.elapsed(), 1); show_time("Source update only", time_update_src.elapsed(), 1); + if (settings::kinetic_simulation && settings::create_delayed_neutrons) { + show_time( + "Precursor computation only", time_compute_precursors.elapsed(), 1); + misc_time -= time_compute_precursors.elapsed(); + } show_time("Tally conversion only", time_tallies.elapsed(), 1); show_time("MPI source reductions only", time_bank_sendrecv.elapsed(), 1); show_time("Other iteration routines", misc_time, 1); @@ -644,7 +856,6 @@ void openmc_run_random_ray() ////////////////////////////////////////////////////////// // Run forward simulation ////////////////////////////////////////////////////////// - if (openmc::mpi::master) { if (openmc::FlatSourceDomain::adjoint_) { openmc::FlatSourceDomain::adjoint_ = false; @@ -666,9 +877,24 @@ void openmc_run_random_ray() // Initialize fixed sources, if present sim.apply_fixed_sources_and_mesh_domains(); - // Run initial random ray simulation + // Simulate single random ray simulation (static case) + // OR get an initial estimate for scattering and fission + // distributions (if fissile material exist), + // and k-eff (if kinetic eigenvalue simulation) sim.simulate(); + if (openmc::settings::kinetic_simulation) { + // Toggle initial condition source correction + openmc::simulation::k_eff_correction = true; + int i_start = -1; + // Timestepping loop, + for (int i = i_start; i < openmc::settings::n_timesteps; i++) { + sim.initialize_time_step(i); + sim.simulate(); + sim.finalize_time_step(); + } + } + ////////////////////////////////////////////////////////// // Run adjoint simulation (if enabled) ////////////////////////////////////////////////////////// diff --git a/src/random_ray/source_region.cpp b/src/random_ray/source_region.cpp index 78543c5ab53..6f13cc56b12 100644 --- a/src/random_ray/source_region.cpp +++ b/src/random_ray/source_region.cpp @@ -1,4 +1,5 @@ #include "openmc/random_ray/source_region.h" +#include "openmc/random_ray/random_ray.h" #include "openmc/error.h" #include "openmc/message_passing.h" @@ -24,19 +25,32 @@ SourceRegionHandle::SourceRegionHandle(SourceRegion& sr) volume_task_(&sr.volume_task_), mesh_(&sr.mesh_), parent_sr_(&sr.parent_sr_), scalar_flux_old_(sr.scalar_flux_old_.data()), scalar_flux_new_(sr.scalar_flux_new_.data()), source_(sr.source_.data()), + source_final_(sr.source_.data()), external_source_(sr.external_source_.data()), scalar_flux_final_(sr.scalar_flux_final_.data()), source_gradients_(sr.source_gradients_.data()), flux_moments_old_(sr.flux_moments_old_.data()), flux_moments_new_(sr.flux_moments_new_.data()), flux_moments_t_(sr.flux_moments_t_.data()), - tally_task_(sr.tally_task_.data()) + tally_task_(sr.tally_task_.data()), phi_prime_(sr.phi_prime_.data()), + T1_(sr.T1_.data()), + delayed_fission_source_(sr.delayed_fission_source_.data()), + precursors_old_(sr.precursors_old_.data()), + precursors_new_(sr.precursors_new_.data()), + precursors_final_(sr.precursors_final_.data()), + scalar_flux_bd_(sr.scalar_flux_bd_.data()), + source_bd_(sr.source_bd_.data()), precursors_bd_(sr.precursors_bd_.data()), + scalar_flux_rhs_bd_(sr.scalar_flux_rhs_bd_.data()), + source_rhs_bd_(sr.source_rhs_bd_.data()), + scalar_flux_rhs_bd_2_(sr.scalar_flux_rhs_bd_2_.data()), + precursors_rhs_bd_(sr.precursors_rhs_bd_.data()), + tally_delay_task_(sr.tally_delay_task_.data()) {} //============================================================================== // SourceRegion implementation //============================================================================== -SourceRegion::SourceRegion(int negroups, bool is_linear) +SourceRegion::SourceRegion(int negroups, int ndgroups, bool is_linear) { if (settings::run_mode == RunMode::EIGENVALUE) { // If in eigenvalue mode, set starting flux to guess of 1 @@ -59,6 +73,36 @@ SourceRegion::SourceRegion(int negroups, bool is_linear) flux_moments_new_.resize(negroups); flux_moments_t_.resize(negroups); } + if (settings::kinetic_simulation) { + scalar_flux_bd_.resize(negroups); + scalar_flux_rhs_bd_.resize(negroups); + + if (RandomRay::time_method_ == RandomRayTimeMethod::ISOTROPIC) { + // Time Isotropic arrays + phi_prime_.assign(negroups, 0.0); + } else if (RandomRay::time_method_ == RandomRayTimeMethod::PROPAGATION) { + // Source Derivative Propogation arrays + source_final_.assign(negroups, 0.0); + + T1_.assign(negroups, 0.0); + + source_bd_.resize(negroups); + source_rhs_bd_.resize(negroups); + scalar_flux_rhs_bd_2_.resize(negroups); + } + + if (settings::create_delayed_neutrons) { + delayed_fission_source_.assign(ndgroups, 0.0); + precursors_old_.assign(ndgroups, 0.0); + precursors_new_.assign(ndgroups, 0.0); + precursors_final_.assign(ndgroups, 0.0); + + precursors_bd_.resize(ndgroups); + precursors_rhs_bd_.resize(ndgroups); + + tally_delay_task_.resize(ndgroups); + } + } } //============================================================================== @@ -117,6 +161,40 @@ void SourceRegionContainer::push_back(const SourceRegion& sr) // Tally tasks tally_task_.emplace_back(sr.tally_task_[g]); + + // Energy-dependent fields for kinetic simulations + if (settings::kinetic_simulation) { + scalar_flux_bd_.push_back(sr.scalar_flux_bd_[g]); + scalar_flux_rhs_bd_.push_back(sr.scalar_flux_rhs_bd_[g]); + + if (RandomRay::time_method_ == RandomRayTimeMethod::ISOTROPIC) { + // Time Isotropic arrays + phi_prime_.push_back(sr.phi_prime_[g]); + } else if (RandomRay::time_method_ == RandomRayTimeMethod::PROPAGATION) { + // Source Derivative Propogation arrays + source_final_.push_back(sr.source_final_[g]); + + T1_.push_back(sr.T1_[g]); + + source_bd_.push_back(sr.source_bd_[g]); + source_rhs_bd_.push_back(sr.source_rhs_bd_[g]); + scalar_flux_rhs_bd_2_.push_back(sr.scalar_flux_rhs_bd_2_[g]); + } + } + } + // Delay group-dependent fields for kinetic simulations + if (settings::kinetic_simulation && settings::create_delayed_neutrons) { + for (int dg = 0; dg < ndgroups_; dg++) { + delayed_fission_source_.push_back(sr.delayed_fission_source_[dg]); + precursors_old_.push_back(sr.precursors_old_[dg]); + precursors_new_.push_back(sr.precursors_new_[dg]); + precursors_final_.push_back(sr.precursors_final_[dg]); + tally_delay_task_.emplace_back(sr.tally_delay_task_[dg]); + + // Backward difference arrays + precursors_bd_.push_back(sr.precursors_bd_[dg]); + precursors_rhs_bd_.push_back(sr.precursors_rhs_bd_[dg]); + } } } @@ -166,6 +244,35 @@ void SourceRegionContainer::assign( tally_task_.clear(); volume_task_.clear(); + // Clear existing data for kinetic simulatons + if (settings::kinetic_simulation) { + scalar_flux_bd_.clear(); + scalar_flux_rhs_bd_.clear(); + + if (RandomRay::time_method_ == RandomRayTimeMethod::ISOTROPIC) { + phi_prime_.clear(); + } else if (RandomRay::time_method_ == RandomRayTimeMethod::PROPAGATION) { + source_final_.clear(); + + T1_.clear(); + + source_bd_.clear(); + source_rhs_bd_.clear(); + scalar_flux_rhs_bd_2_.clear(); + } + + if (settings::create_delayed_neutrons) { + precursors_bd_.clear(); + precursors_rhs_bd_.clear(); + + delayed_fission_source_.clear(); + precursors_old_.clear(); + precursors_new_.clear(); + precursors_final_.clear(); + tally_delay_task_.clear(); + } + } + // Fill with copies of source_region for (int i = 0; i < n_source_regions; ++i) { push_back(source_region); @@ -225,10 +332,39 @@ SourceRegionHandle SourceRegionContainer::get_source_region_handle(int64_t sr) handle.flux_moments_t_ = &flux_moments_t(sr, 0); } + if (settings::kinetic_simulation) { + handle.scalar_flux_bd_ = &scalar_flux_bd(sr, 0); + handle.scalar_flux_rhs_bd_ = &scalar_flux_rhs_bd(sr, 0); + + if (RandomRay::time_method_ == RandomRayTimeMethod::ISOTROPIC) { + handle.phi_prime_ = &phi_prime(sr, 0); + } else if (RandomRay::time_method_ == RandomRayTimeMethod::PROPAGATION) { + handle.source_final_ = &source_final(sr, 0); + + handle.T1_ = &T1(sr, 0); + + handle.source_bd_ = &source_bd(sr, 0); + handle.source_rhs_bd_ = &source_rhs_bd(sr, 0); + handle.scalar_flux_rhs_bd_2_ = &scalar_flux_rhs_bd_2(sr, 0); + } + + if (settings::create_delayed_neutrons) { + handle.delayed_fission_source_ = &delayed_fission_source(sr, 0); + handle.precursors_old_ = &precursors_old(sr, 0); + handle.precursors_new_ = &precursors_new(sr, 0); + handle.precursors_final_ = &precursors_final(sr, 0); + + handle.precursors_bd_ = &precursors_bd(sr, 0); + handle.precursors_rhs_bd_ = &precursors_rhs_bd(sr, 0); + + handle.tally_delay_task_ = &tally_delay_task(sr, 0); + } + } + return handle; } -void SourceRegionContainer::adjoint_reset() +void SourceRegionContainer::simulation_reset() { std::fill(n_hits_.begin(), n_hits_.end(), 0); std::fill(volume_.begin(), volume_.end(), 0.0); @@ -236,9 +372,6 @@ void SourceRegionContainer::adjoint_reset() std::fill(volume_sq_.begin(), volume_sq_.end(), 0.0); std::fill(volume_sq_t_.begin(), volume_sq_t_.end(), 0.0); std::fill(volume_naive_.begin(), volume_naive_.end(), 0.0); - std::fill( - external_source_present_.begin(), external_source_present_.end(), 0); - std::fill(external_source_.begin(), external_source_.end(), 0.0); std::fill(centroid_.begin(), centroid_.end(), Position {0.0, 0.0, 0.0}); std::fill(centroid_iteration_.begin(), centroid_iteration_.end(), Position {0.0, 0.0, 0.0}); @@ -254,7 +387,6 @@ void SourceRegionContainer::adjoint_reset() } std::fill(scalar_flux_new_.begin(), scalar_flux_new_.end(), 0.0); std::fill(source_.begin(), source_.end(), 0.0f); - std::fill(external_source_.begin(), external_source_.end(), 0.0f); std::fill(source_gradients_.begin(), source_gradients_.end(), MomentArray {0.0, 0.0, 0.0}); std::fill(flux_moments_old_.begin(), flux_moments_old_.end(), @@ -263,6 +395,52 @@ void SourceRegionContainer::adjoint_reset() MomentArray {0.0, 0.0, 0.0}); std::fill(flux_moments_t_.begin(), flux_moments_t_.end(), MomentArray {0.0, 0.0, 0.0}); + // Reset arrays for kinetic adjoint simulations + if (settings::kinetic_simulation && !simulation::is_initial_condition) { + if (settings::create_delayed_neutrons) { + std::fill( + delayed_fission_source_.begin(), delayed_fission_source_.end(), 0.0); + std::fill(precursors_old_.begin(), precursors_old_.end(), 0.0); + std::fill(precursors_new_.begin(), precursors_new_.end(), 0.0); + std::fill(precursors_rhs_bd_.begin(), precursors_rhs_bd_.end(), 0.0); + } + + // BD Vectors + std::fill(scalar_flux_rhs_bd_.begin(), scalar_flux_rhs_bd_.end(), 0.0); + + if (RandomRay::time_method_ == RandomRayTimeMethod::ISOTROPIC) { + std::fill(phi_prime_.begin(), phi_prime_.end(), 0.0); + } else if (RandomRay::time_method_ == RandomRayTimeMethod::PROPAGATION) { + std::fill(T1_.begin(), T1_.end(), 0.0); + + std::fill(source_rhs_bd_.begin(), source_rhs_bd_.end(), 0.0); + std::fill( + scalar_flux_rhs_bd_2_.begin(), scalar_flux_rhs_bd_2_.end(), 0.0); + } + } +} + +void SourceRegionContainer::adjoint_reset() +{ + std::fill( + external_source_present_.begin(), external_source_present_.end(), 0); + std::fill(external_source_.begin(), external_source_.end(), 0.0f); +} + +//----------------------------------------------------------------------------- +// Methods for kinetic simulations + +void SourceRegionContainer::precursors_swap() +{ + precursors_old_.swap(precursors_new_); +} + +void SourceRegionContainer::time_step_reset() +{ + std::fill(scalar_flux_final_.begin(), scalar_flux_final_.end(), 0.0); + std::fill(precursors_final_.begin(), precursors_final_.end(), 0.0); + if (RandomRay::time_method_ == RandomRayTimeMethod::PROPAGATION) + std::fill(source_final_.begin(), source_final_.end(), 0.0); } } // namespace openmc diff --git a/src/reaction.cpp b/src/reaction.cpp index c02e0cc407e..afe7996a13e 100644 --- a/src/reaction.cpp +++ b/src/reaction.cpp @@ -204,6 +204,7 @@ std::unordered_map REACTION_NAME_MAP { {SCORE_IFP_TIME_NUM, "ifp-time-numerator"}, {SCORE_IFP_BETA_NUM, "ifp-beta-numerator"}, {SCORE_IFP_DENOM, "ifp-denominator"}, + {SCORE_PRECURSORS, "precursors"}, // Normal ENDF-based reactions {TOTAL_XS, "(n,total)"}, {ELASTIC, "(n,elastic)"}, diff --git a/src/settings.cpp b/src/settings.cpp index ab9f9a5aafa..977251526f2 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -56,6 +56,7 @@ bool delayed_photon_scaling {true}; bool entropy_on {false}; bool event_based {false}; bool ifp_on {false}; +bool kinetic_simulation {false}; bool legendre_to_tabular {true}; bool material_cell_offsets {true}; bool output_summary {true}; @@ -153,6 +154,10 @@ int verbosity {-1}; double weight_cutoff {0.25}; double weight_survive {1.0}; +// Timestep variables for kinetic simulation +int n_timesteps; +double dt; + } // namespace settings //============================================================================== @@ -258,6 +263,52 @@ void get_run_parameters(pugi::xml_node node_base) } } + // Kinetic variables + if (check_for_node(node_base, "kinetic_simulation")) { + kinetic_simulation = get_node_value_bool(node_base, "kinetic_simulation"); + if (solver_type != SolverType::RANDOM_RAY) { + fatal_error("Unsupported solver selected for kinetic simulation. Kinetic " + "simulations currently only support the random ray solver."); + } + if (run_mode != RunMode::EIGENVALUE) { + fatal_error( + "Unsupported run mode selected for kinetic simulation. Kinetic " + "simulations currently only support run mode based on an eigenvalue " + "simulation establishing an initial condition."); + } + } + + // Get timestep parameters for kinetic simulations + if (kinetic_simulation) { + xml_node ts_node = node_base.child("timestep_parameters"); + if (check_for_node(ts_node, "n_timesteps")) { + n_timesteps = std::stoi(get_node_value(ts_node, "n_timesteps")); + } else { + fatal_error("Specify number of timesteps in settings XML"); + } + if (check_for_node(ts_node, "timestep_units")) { + std::string units = get_node_value(ts_node, "timestep_units"); + if (check_for_node(ts_node, "dt")) { + dt = std::stod(get_node_value(ts_node, "dt")); + double factor_to_seconds; + if (units == "ms") { + factor_to_seconds = 1e-3; + } else if (units == "s") { + factor_to_seconds = 1.0; + } else if (units == "min") { + factor_to_seconds = 1 / 60; + } else { + fatal_error("Invalid timestep unit, " + units); + } + dt *= factor_to_seconds; + } else { + fatal_error("Specify dt in settings XML"); + } + } else { + fatal_error("Specify timestep units in settings XML"); + } + } + // Random ray variables if (solver_type == SolverType::RANDOM_RAY) { xml_node random_ray_node = node_base.child("random_ray"); @@ -309,8 +360,16 @@ void get_run_parameters(pugi::xml_node node_base) RandomRay::source_shape_ = RandomRaySourceShape::FLAT; } else if (temp_str == "linear") { RandomRay::source_shape_ = RandomRaySourceShape::LINEAR; + if (settings::kinetic_simulation) { + fatal_error( + "linear source shapes unimplemented for kinetic simulations."); + } } else if (temp_str == "linear_xy") { RandomRay::source_shape_ = RandomRaySourceShape::LINEAR_XY; + if (settings::kinetic_simulation) { + fatal_error( + "linear_xy source shapes unimplemented for kinetic simulations."); + } } else { fatal_error("Unrecognized source shape: " + temp_str); } @@ -322,6 +381,9 @@ void get_run_parameters(pugi::xml_node node_base) if (check_for_node(random_ray_node, "adjoint")) { FlatSourceDomain::adjoint_ = get_node_value_bool(random_ray_node, "adjoint"); + if (FlatSourceDomain::adjoint_ && kinetic_simulation) { + fatal_error("Adjoint kinetic simulations are currently unsupported ."); + } } if (check_for_node(random_ray_node, "sample_method")) { std::string temp_str = @@ -369,6 +431,28 @@ void get_run_parameters(pugi::xml_node node_base) "between 0 and 1"); } } + if (kinetic_simulation) { + if (check_for_node(random_ray_node, "bd_order")) { + static int n = std::stod(get_node_value(random_ray_node, "bd_order")); + if (n < 1 || n > 6) { + fatal_error("Specified BD order of " + std::to_string(n) + + ". BD order must be between 1 and 6"); + } else { + RandomRay::bd_order_ = n; + } + } + if (check_for_node(random_ray_node, "time_derivative_method")) { + std::string temp_str = + get_node_value(random_ray_node, "time_derivative_method", true, true); + if (temp_str == "isotropic") { + RandomRay::time_method_ = RandomRayTimeMethod::ISOTROPIC; + } else if (temp_str == "propagation") { + RandomRay::time_method_ = RandomRayTimeMethod::PROPAGATION; + } else { + fatal_error("Unrecognized time derivative method: " + temp_str); + } + } + } } } @@ -1262,6 +1346,10 @@ void read_settings_xml(pugi::xml_node root) // Create weight window generator objects if (check_for_node(root, "weight_window_generators")) { + if (kinetic_simulation) { + fatal_error("Weight window generation is currently unsupported in kinetic" + " random ray solver mode."); + } auto wwgs_node = root.child("weight_window_generators"); for (pugi::xml_node node_wwg : wwgs_node.children("weight_windows_generator")) { diff --git a/src/simulation.cpp b/src/simulation.cpp index 4fad196a604..6194507ba6d 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -140,6 +140,19 @@ int openmc_simulation_init() // Display header if (mpi::master) { + if (settings::kinetic_simulation) { + if (simulation::is_initial_condition) { + if (simulation::k_eff_correction) + header("KINETIC SIMULATION INITIAL CONDITION (K-EFF CORRECTION)", 3); + else + header("KINETIC SIMULATION INITIAL CONDITION", 3); + } else { + std::string message = fmt::format( + "KINETIC SIMULATION TIME STEP {0}", simulation::current_timestep); + const char* msg = message.c_str(); + header(msg, 3); + } + } if (settings::run_mode == RunMode::FIXED_SOURCE) { if (settings::solver_type == SolverType::MONTE_CARLO) { header("FIXED SOURCE TRANSPORT SIMULATION", 3); @@ -324,6 +337,11 @@ const RegularMesh* ufs_mesh {nullptr}; vector k_generation; vector work_index; +bool is_initial_condition {true}; +int current_timestep; +double current_time {0.0}; +bool k_eff_correction {false}; + } // namespace simulation //============================================================================== diff --git a/src/state_point.cpp b/src/state_point.cpp index da1c141a230..2c196e77a7b 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -22,6 +22,7 @@ #include "openmc/nuclide.h" #include "openmc/output.h" #include "openmc/particle_type.h" +#include "openmc/random_ray/random_ray_simulation.h" #include "openmc/settings.h" #include "openmc/simulation.h" #include "openmc/tallies/derivative.h" @@ -96,6 +97,10 @@ extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) // Write run information write_dataset(file_id, "energy_mode", settings::run_CE ? "continuous-energy" : "multi-group"); + if (!settings::run_CE) { + write_dataset(file_id, "n_energy_groups", data::mg.num_energy_groups_); + write_dataset(file_id, "n_delay_groups", data::mg.num_delayed_groups_); + } switch (settings::run_mode) { case RunMode::FIXED_SOURCE: write_dataset(file_id, "run_mode", "fixed source"); @@ -106,10 +111,32 @@ extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) default: break; } + switch (settings::solver_type) { + case SolverType::MONTE_CARLO: + write_dataset(file_id, "solver_type", "monte carlo"); + break; + case SolverType::RANDOM_RAY: + write_dataset(file_id, "solver_type", "random ray"); + write_random_ray_hdf5(file_id); + break; + default: + break; + } write_attribute(file_id, "photon_transport", settings::photon_transport); write_dataset(file_id, "n_particles", settings::n_particles); write_dataset(file_id, "n_batches", settings::n_batches); + write_dataset(file_id, "kinetic_simulation", + settings::kinetic_simulation ? true : false); + if (settings::kinetic_simulation) { + hid_t timestep_group = create_group(file_id, "timestep_data"); + write_dataset(timestep_group, "dt", settings::dt); + write_dataset( + timestep_group, "current_timestep", simulation::current_timestep); + write_dataset(timestep_group, "current_time", simulation::current_time); + close_group(timestep_group); + } + // Write out current batch number write_dataset(file_id, "current_batch", simulation::current_batch); @@ -315,6 +342,13 @@ extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) write_dataset(runtime_group, "inactive batches", time_inactive.elapsed()); } write_dataset(runtime_group, "active batches", time_active.elapsed()); + if (settings::solver_type == SolverType::RANDOM_RAY) { + write_dataset(runtime_group, "source_update", time_update_src.elapsed()); + if (settings::kinetic_simulation) { + write_dataset( + runtime_group, "precursor_update", time_compute_precursors.elapsed()); + } + } if (settings::run_mode == RunMode::EIGENVALUE) { write_dataset( runtime_group, "synchronizing fission bank", time_bank.elapsed()); diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 3fe48c1b021..4ef6dbc9796 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -580,7 +580,7 @@ void Tally::set_scores(const vector& scores) // score. if (delayedgroup_filter_ != C_NONE) { if (score_str != "delayed-nu-fission" && score_str != "decay-rate" && - score_str != "ifp-beta-numerator") + score_str != "ifp-beta-numerator" && score_str != "precursors") fatal_error("Cannot tally " + score_str + "with a delayedgroup filter"); } @@ -672,6 +672,20 @@ void Tally::set_scores(const vector& scores) case SCORE_IFP_DENOM: estimator_ = TallyEstimator::COLLISION; break; + + case SCORE_PRECURSORS: + if (!settings::kinetic_simulation) + fatal_error("Can only tally precursors in kinetic simulations."); + if (!nuclides_.empty()) + if (!(nuclides_.size() == 1 && nuclides_[0] == -1)) + fatal_error("Cannot tally precursors for an individual nuclide."); + if (energyout_present) + fatal_error("Cannot tally precursors with an outgoing energy filter."); + // TODO: make this more robust: allow for tallying + // in eigenvalue and fixed source calculations by detecting + // delayed fission, delayed chi, and lambda cross sections (mg and ce) + // Also enable support for monte carlo solves + break; } scores_.push_back(score); diff --git a/src/timer.cpp b/src/timer.cpp index 6d692d4fbf6..15bca0de48c 100644 --- a/src/timer.cpp +++ b/src/timer.cpp @@ -28,6 +28,10 @@ Timer time_event_collision; Timer time_event_death; Timer time_update_src; +// Timers for kinetic simulations +Timer time_update_bd_vectors; +Timer time_compute_precursors; + } // namespace simulation //============================================================================== @@ -87,6 +91,9 @@ void reset_timers() simulation::time_event_collision.reset(); simulation::time_event_death.reset(); simulation::time_update_src.reset(); + + simulation::time_update_bd_vectors.reset(); + simulation::time_compute_precursors.reset(); } } // namespace openmc diff --git a/src/xsdata.cpp b/src/xsdata.cpp index 33d063a7b9a..7b958b52bb0 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -53,8 +53,9 @@ XsData::XsData(bool fissionable, AngleDistributionType scatter_format, // allocate delayed_nu_fission; [temperature][angle][delay group][in group] delayed_nu_fission = tensor::zeros(shape); - // chi_prompt; [temperature][angle][in group][out group] + // chi and chi_prompt; [temperature][angle][in group][out group] shape = {n_ang, n_g_, n_g_}; + chi = tensor::zeros(shape); chi_prompt = tensor::zeros(shape); // chi_delayed; [temperature][angle][delay group][in group][out group] @@ -138,11 +139,14 @@ void XsData::fission_vector_beta_from_hdf5( row /= row.sum(); } + // TODO: This is incorrect!! This makes chi_prompt and chi_delayed identical // Replicate the energy spectrum across all incoming groups — the // spectrum is independent of the incoming neutron energy for (size_t a = 0; a < n_ang; a++) - for (size_t gin = 0; gin < n_g_; gin++) + for (size_t gin = 0; gin < n_g_; gin++) { + chi.slice(a, gin) = temp_chi.slice(a); chi_prompt.slice(a, gin) = temp_chi.slice(a); + } // Same spectrum for delayed neutrons, replicated across delayed groups for (size_t a = 0; a < n_ang; a++) @@ -200,8 +204,23 @@ void XsData::fission_vector_beta_from_hdf5( void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) { - // Data is provided separately as prompt + delayed nu-fission and chi + // If chi is included in the dataset, we should store it! + if (object_exists(xsdata_grp, "chi")) { + tensor::Tensor temp_chi = tensor::zeros({n_ang, n_g_}); + read_nd_tensor(xsdata_grp, "chi", temp_chi, true); + // Normalize chi by summing over the outgoing groups for each incoming angle + for (size_t a = 0; a < n_ang; a++) { + tensor::View row = temp_chi.slice(a); + row /= row.sum(); + } + + // Replicate the spectrum across all incoming groups + for (size_t a = 0; a < n_ang; a++) + for (size_t gin = 0; gin < n_g_; gin++) + chi.slice(a, gin) = temp_chi.slice(a); + } + // Data is provided separately as prompt + delayed nu-fission and chi // Get chi-prompt tensor::Tensor temp_chi_p = tensor::zeros({n_ang, n_g_}); read_nd_tensor(xsdata_grp, "chi-prompt", temp_chi_p, true); @@ -241,6 +260,7 @@ void XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) read_nd_tensor(xsdata_grp, "delayed-nu-fission", delayed_nu_fission, true); } +// TODO: Add machinery to read chi void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) { // No beta is provided and there is no prompt/delay distinction. @@ -258,8 +278,10 @@ void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) // Replicate the energy spectrum across all incoming groups for (size_t a = 0; a < n_ang; a++) - for (size_t gin = 0; gin < n_g_; gin++) + for (size_t gin = 0; gin < n_g_; gin++) { + chi.slice(a, gin) = temp_chi.slice(a); chi_prompt.slice(a, gin) = temp_chi.slice(a); + } // Get nu-fission directly read_nd_tensor(xsdata_grp, "nu-fission", prompt_nu_fission, true); @@ -267,6 +289,7 @@ void XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) //============================================================================== +// TODO: Add machinery to read chi void XsData::fission_matrix_beta_from_hdf5( hid_t xsdata_grp, size_t n_ang, bool is_isotropic) { @@ -373,6 +396,7 @@ void XsData::fission_matrix_beta_from_hdf5( } } +// TODO: Add machinery to read chi void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) { // Data is provided separately as prompt + delayed nu-fission and chi @@ -410,6 +434,7 @@ void XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang) temp_matrix_d(a, d, gin, gout) / delayed_nu_fission(a, d, gin); } +// TODO: Add machinery to read chi void XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang) { // No beta is provided and there is no prompt/delay distinction. @@ -606,6 +631,10 @@ void XsData::combine( kappa_fission += scalar * that->kappa_fission; fission += scalar * that->fission; delayed_nu_fission += scalar * that->delayed_nu_fission; + + // This may throw an error in some cases. Need a check for if + // chi exists! + chi += scalar * that->chi; // Accumulate chi_prompt weighted by total prompt nu-fission // (summed over energy groups) for this constituent { @@ -636,6 +665,16 @@ void XsData::combine( decay_rate += scalar * that->decay_rate; } + // Normalize chi so it sums to 1 over outgoing groups + { + size_t n_ang = chi.shape(0); + size_t n_g = chi.shape(1); + for (size_t a = 0; a < n_ang; a++) + for (size_t gin = 0; gin < n_g; gin++) { + tensor::View row = chi.slice(a, gin); + row /= row.sum(); + } + } // Normalize chi_prompt so it sums to 1 over outgoing groups { size_t n_ang = chi_prompt.shape(0); diff --git a/tests/cpp_unit_tests/CMakeLists.txt b/tests/cpp_unit_tests/CMakeLists.txt index ce7e539ea57..bf2da611536 100644 --- a/tests/cpp_unit_tests/CMakeLists.txt +++ b/tests/cpp_unit_tests/CMakeLists.txt @@ -1,4 +1,5 @@ set(TEST_NAMES + test_bd_utilities test_distribution test_file_utils test_tally diff --git a/tests/cpp_unit_tests/test_bd_utilities.cpp b/tests/cpp_unit_tests/test_bd_utilities.cpp new file mode 100644 index 00000000000..4d51fbd2068 --- /dev/null +++ b/tests/cpp_unit_tests/test_bd_utilities.cpp @@ -0,0 +1,53 @@ +#include "openmc/random_ray/bd_utilities.h" +#include "openmc/random_ray/random_ray_simulation.h" +#include "openmc/vector.h" +#include +#include + +#include +#include +#include + +using namespace openmc; + +TEST_CASE("Test rhs_backwards_difference") +{ + vector> ref_rhs_bd_first_order {{-68.59, -36.1}, + {-108.02, -56.0}, {-134.66666666666663, -69.33333333333331}, + {-154.66666666666666, -79.33333333333331}, + {-170.66666666666666, -87.33333333333331}, {-184.0, -94.0}}; + + vector> ref_rhs_bd_second_order { + {-788.5999999999999, -397.9999999999999}, {-1588.0, -797.9999999999999}, + {-2321.3333333333344, -1164.6666666666667}, {-2988.0, -1497.9999999999989}, + {-3596.88888888889, -1802.4444444444416}, + {-4156.888888888892, -2082.444444444443}}; + + int vector_size = 2; + double dt = 0.1; + // Two functions t^2 and t^3 across x = 2, 1.9, 1.8, 1.7, 1.6, 1.5, 1.4, 1.3 + vector> test_bd_vector { + {6.859, 5.832, 4.913, 4.096, 3.375, 2.744, 2.197}, + {3.61, 3.24, 2.89, 2.56, 2.25, 1.96, 1.69}}; + vector test_rhs_bd {0.0, 0.0}; + for (int i = 0; i < 6; i++) { + int o = i + 1; + double d0 = rhs_backwards_difference(test_bd_vector[0], o, dt); + double d1 = rhs_backwards_difference(test_bd_vector[1], o, dt); + test_rhs_bd[0] = d0; + test_rhs_bd[1] = d1; + REQUIRE_THAT( + test_rhs_bd, Catch::Matchers::Approx(ref_rhs_bd_first_order[i])); + } + for (int i = 0; i < 6; i++) { + int o = i + 1; + double rhs_d0 = + rhs_backwards_difference(test_bd_vector[0], o, dt, 2); + double rhs_d1 = + rhs_backwards_difference(test_bd_vector[1], o, dt, 2); + test_rhs_bd[0] = rhs_d0; + test_rhs_bd[1] = rhs_d1; + REQUIRE_THAT( + test_rhs_bd, Catch::Matchers::Approx(ref_rhs_bd_second_order[i])); + } +} diff --git a/tests/regression_tests/random_ray_auto_convert/test.py b/tests/regression_tests/random_ray_auto_convert/test.py index 99a931dce86..ea25ffbd2da 100644 --- a/tests/regression_tests/random_ray_auto_convert/test.py +++ b/tests/regression_tests/random_ray_auto_convert/test.py @@ -27,7 +27,7 @@ def test_random_ray_auto_convert(method): # Convert to a multi-group model model.convert_to_multigroup( - method=method, groups='CASMO-2', nparticles=100, + method=method, energy_groups='CASMO-2', nparticles=100, overwrite_mgxs_library=False, mgxs_path="mgxs.h5" ) diff --git a/tests/regression_tests/random_ray_auto_convert_kappa_fission/test.py b/tests/regression_tests/random_ray_auto_convert_kappa_fission/test.py index 6decf165a7f..0bbe58e13b6 100644 --- a/tests/regression_tests/random_ray_auto_convert_kappa_fission/test.py +++ b/tests/regression_tests/random_ray_auto_convert_kappa_fission/test.py @@ -18,7 +18,7 @@ def _cleanup(self): @pytest.mark.parametrize("method", ["material_wise", "stochastic_slab", "infinite_medium"]) -def test_random_ray_auto_convert(method): +def test_random_ray_auto_convert_kappa_fission(method): with change_directory(method): openmc.reset_auto_ids() @@ -27,7 +27,7 @@ def test_random_ray_auto_convert(method): # Convert to a multi-group model model.convert_to_multigroup( - method=method, groups='CASMO-2', nparticles=100, + method=method, energy_groups='CASMO-2', nparticles=100, overwrite_mgxs_library=False, mgxs_path="mgxs.h5" ) diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/__init__.py b/tests/regression_tests/random_ray_auto_convert_kinetic/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/inputs_true.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/inputs_true.dat new file mode 100644 index 00000000000..e048a7d4dc1 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/inputs_true.dat @@ -0,0 +1,70 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + true + +
0.01
+ s + 5 +
+ + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + multi-group + + + + -0.63 -0.63 -1.0 0.63 0.63 1.0 + + + 30.0 + 150.0 + + + + + + isotropic + + + 8 8 + -0.63 -0.63 + 0.63 0.63 + +
+
diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_0.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_0.dat new file mode 100644 index 00000000000..3c8808a0c82 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_0.dat @@ -0,0 +1,2 @@ +k-combined: +5.824689E-01 1.309494E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_1.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_1.dat new file mode 100644 index 00000000000..3c8808a0c82 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_1.dat @@ -0,0 +1,2 @@ +k-combined: +5.824689E-01 1.309494E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_2.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_2.dat new file mode 100644 index 00000000000..3c8808a0c82 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_2.dat @@ -0,0 +1,2 @@ +k-combined: +5.824689E-01 1.309494E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_3.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_3.dat new file mode 100644 index 00000000000..3c8808a0c82 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_3.dat @@ -0,0 +1,2 @@ +k-combined: +5.824689E-01 1.309494E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_4.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_4.dat new file mode 100644 index 00000000000..3c8808a0c82 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_4.dat @@ -0,0 +1,2 @@ +k-combined: +5.824689E-01 1.309494E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_5.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_5.dat new file mode 100644 index 00000000000..3c8808a0c82 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/isotropic/results_true_5.dat @@ -0,0 +1,2 @@ +k-combined: +5.824689E-01 1.309494E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/inputs_true.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/inputs_true.dat new file mode 100644 index 00000000000..092e3952f5e --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/inputs_true.dat @@ -0,0 +1,70 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + true + +
0.01
+ s + 5 +
+ + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + multi-group + + + + -0.63 -0.63 -1.0 0.63 0.63 1.0 + + + 30.0 + 150.0 + + + + + + propagation + + + 8 8 + -0.63 -0.63 + 0.63 0.63 + +
+
diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_0.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_0.dat new file mode 100644 index 00000000000..3c8808a0c82 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_0.dat @@ -0,0 +1,2 @@ +k-combined: +5.824689E-01 1.309494E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_1.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_1.dat new file mode 100644 index 00000000000..3c8808a0c82 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_1.dat @@ -0,0 +1,2 @@ +k-combined: +5.824689E-01 1.309494E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_2.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_2.dat new file mode 100644 index 00000000000..3c8808a0c82 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_2.dat @@ -0,0 +1,2 @@ +k-combined: +5.824689E-01 1.309494E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_3.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_3.dat new file mode 100644 index 00000000000..3c8808a0c82 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_3.dat @@ -0,0 +1,2 @@ +k-combined: +5.824689E-01 1.309494E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_4.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_4.dat new file mode 100644 index 00000000000..3c8808a0c82 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_4.dat @@ -0,0 +1,2 @@ +k-combined: +5.824689E-01 1.309494E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_5.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_5.dat new file mode 100644 index 00000000000..3c8808a0c82 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/infinite_medium/propagation/results_true_5.dat @@ -0,0 +1,2 @@ +k-combined: +5.824689E-01 1.309494E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/inputs_true.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/inputs_true.dat new file mode 100644 index 00000000000..e048a7d4dc1 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/inputs_true.dat @@ -0,0 +1,70 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + true + +
0.01
+ s + 5 +
+ + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + multi-group + + + + -0.63 -0.63 -1.0 0.63 0.63 1.0 + + + 30.0 + 150.0 + + + + + + isotropic + + + 8 8 + -0.63 -0.63 + 0.63 0.63 + +
+
diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_0.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_0.dat new file mode 100644 index 00000000000..df7374c145b --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_0.dat @@ -0,0 +1,2 @@ +k-combined: +7.247257E-01 3.323600E-03 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_1.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_1.dat new file mode 100644 index 00000000000..df7374c145b --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_1.dat @@ -0,0 +1,2 @@ +k-combined: +7.247257E-01 3.323600E-03 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_2.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_2.dat new file mode 100644 index 00000000000..df7374c145b --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_2.dat @@ -0,0 +1,2 @@ +k-combined: +7.247257E-01 3.323600E-03 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_3.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_3.dat new file mode 100644 index 00000000000..df7374c145b --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_3.dat @@ -0,0 +1,2 @@ +k-combined: +7.247257E-01 3.323600E-03 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_4.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_4.dat new file mode 100644 index 00000000000..df7374c145b --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_4.dat @@ -0,0 +1,2 @@ +k-combined: +7.247257E-01 3.323600E-03 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_5.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_5.dat new file mode 100644 index 00000000000..df7374c145b --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/isotropic/results_true_5.dat @@ -0,0 +1,2 @@ +k-combined: +7.247257E-01 3.323600E-03 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/inputs_true.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/inputs_true.dat new file mode 100644 index 00000000000..092e3952f5e --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/inputs_true.dat @@ -0,0 +1,70 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + true + +
0.01
+ s + 5 +
+ + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + multi-group + + + + -0.63 -0.63 -1.0 0.63 0.63 1.0 + + + 30.0 + 150.0 + + + + + + propagation + + + 8 8 + -0.63 -0.63 + 0.63 0.63 + +
+
diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_0.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_0.dat new file mode 100644 index 00000000000..df7374c145b --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_0.dat @@ -0,0 +1,2 @@ +k-combined: +7.247257E-01 3.323600E-03 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_1.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_1.dat new file mode 100644 index 00000000000..df7374c145b --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_1.dat @@ -0,0 +1,2 @@ +k-combined: +7.247257E-01 3.323600E-03 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_2.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_2.dat new file mode 100644 index 00000000000..df7374c145b --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_2.dat @@ -0,0 +1,2 @@ +k-combined: +7.247257E-01 3.323600E-03 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_3.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_3.dat new file mode 100644 index 00000000000..df7374c145b --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_3.dat @@ -0,0 +1,2 @@ +k-combined: +7.247257E-01 3.323600E-03 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_4.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_4.dat new file mode 100644 index 00000000000..df7374c145b --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_4.dat @@ -0,0 +1,2 @@ +k-combined: +7.247257E-01 3.323600E-03 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_5.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_5.dat new file mode 100644 index 00000000000..df7374c145b --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/material_wise/propagation/results_true_5.dat @@ -0,0 +1,2 @@ +k-combined: +7.247257E-01 3.323600E-03 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/inputs_true.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/inputs_true.dat new file mode 100644 index 00000000000..e048a7d4dc1 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/inputs_true.dat @@ -0,0 +1,70 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + true + +
0.01
+ s + 5 +
+ + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + multi-group + + + + -0.63 -0.63 -1.0 0.63 0.63 1.0 + + + 30.0 + 150.0 + + + + + + isotropic + + + 8 8 + -0.63 -0.63 + 0.63 0.63 + +
+
diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_0.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_0.dat new file mode 100644 index 00000000000..718ed9f63b2 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_0.dat @@ -0,0 +1,2 @@ +k-combined: +4.474813E-01 1.434056E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_1.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_1.dat new file mode 100644 index 00000000000..718ed9f63b2 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_1.dat @@ -0,0 +1,2 @@ +k-combined: +4.474813E-01 1.434056E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_2.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_2.dat new file mode 100644 index 00000000000..718ed9f63b2 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_2.dat @@ -0,0 +1,2 @@ +k-combined: +4.474813E-01 1.434056E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_3.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_3.dat new file mode 100644 index 00000000000..718ed9f63b2 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_3.dat @@ -0,0 +1,2 @@ +k-combined: +4.474813E-01 1.434056E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_4.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_4.dat new file mode 100644 index 00000000000..718ed9f63b2 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_4.dat @@ -0,0 +1,2 @@ +k-combined: +4.474813E-01 1.434056E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_5.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_5.dat new file mode 100644 index 00000000000..718ed9f63b2 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/isotropic/results_true_5.dat @@ -0,0 +1,2 @@ +k-combined: +4.474813E-01 1.434056E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/inputs_true.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/inputs_true.dat new file mode 100644 index 00000000000..092e3952f5e --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/inputs_true.dat @@ -0,0 +1,70 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + true + +
0.01
+ s + 5 +
+ + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + multi-group + + + + -0.63 -0.63 -1.0 0.63 0.63 1.0 + + + 30.0 + 150.0 + + + + + + propagation + + + 8 8 + -0.63 -0.63 + 0.63 0.63 + +
+
diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_0.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_0.dat new file mode 100644 index 00000000000..718ed9f63b2 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_0.dat @@ -0,0 +1,2 @@ +k-combined: +4.474813E-01 1.434056E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_1.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_1.dat new file mode 100644 index 00000000000..718ed9f63b2 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_1.dat @@ -0,0 +1,2 @@ +k-combined: +4.474813E-01 1.434056E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_2.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_2.dat new file mode 100644 index 00000000000..718ed9f63b2 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_2.dat @@ -0,0 +1,2 @@ +k-combined: +4.474813E-01 1.434056E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_3.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_3.dat new file mode 100644 index 00000000000..718ed9f63b2 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_3.dat @@ -0,0 +1,2 @@ +k-combined: +4.474813E-01 1.434056E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_4.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_4.dat new file mode 100644 index 00000000000..718ed9f63b2 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_4.dat @@ -0,0 +1,2 @@ +k-combined: +4.474813E-01 1.434056E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_5.dat b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_5.dat new file mode 100644 index 00000000000..718ed9f63b2 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/stochastic_slab/propagation/results_true_5.dat @@ -0,0 +1,2 @@ +k-combined: +4.474813E-01 1.434056E-02 diff --git a/tests/regression_tests/random_ray_auto_convert_kinetic/test.py b/tests/regression_tests/random_ray_auto_convert_kinetic/test.py new file mode 100644 index 00000000000..df482abc953 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert_kinetic/test.py @@ -0,0 +1,70 @@ +import os + +import openmc +from openmc.examples import pwr_pin_cell +from openmc import RegularMesh +from openmc.utility_funcs import change_directory +import pytest +import numpy as np + +from tests.testing_harness import KineticTolerantPyAPITestHarness + + +class KineticMGXSTestHarness(KineticTolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +@pytest.mark.parametrize("generation_method, time_method", [("material_wise", "isotropic"), + ("stochastic_slab", + "isotropic"), + ("infinite_medium", + "isotropic"), + ("material_wise", + "propagation"), + ("stochastic_slab", + "propagation"), + ("infinite_medium", + "propagation"), + ]) +def test_random_ray_auto_convert_kinetic(generation_method, time_method): + with change_directory(f'{generation_method}/{time_method}'): + openmc.reset_auto_ids() + + # Start with a normal continuous energy model + model = pwr_pin_cell() + + # Convert to a multi-group model + model.convert_to_multigroup( + method=generation_method, energy_groups='CASMO-2', nparticles=30, + overwrite_mgxs_library=False, mgxs_path="mgxs.h5", kinetic=True, + num_delayed_groups=6 + ) + + model.settings.timestep_parameters['n_timesteps'] = 5 + density_timeseries = np.linspace(1, 0.95, 100) + model.materials[2].set_density( + 'macro', density=1.0, density_timeseries=density_timeseries) + + # Convert to a random ray model + model.convert_to_random_ray() + + # Set the number of particles + model.settings.particles = 100 + + # Overlay an basic 8x8 mesh + n = 8 + mesh = RegularMesh() + mesh.dimension = (n, n) + bbox = model.geometry.bounding_box + mesh.lower_left = (bbox.lower_left[0], bbox.lower_left[1]) + mesh.upper_right = (bbox.upper_right[0], bbox.upper_right[1]) + model.settings.random_ray['source_region_meshes'] = [ + (mesh, [model.geometry.root_universe])] + model.settings.random_ray['time_derivative_method'] = time_method + + harness = KineticMGXSTestHarness("statepoint.10", 6, model) + harness.main() diff --git a/tests/regression_tests/random_ray_auto_convert_source_energy/test.py b/tests/regression_tests/random_ray_auto_convert_source_energy/test.py index bb9119d8953..1e0ab315eec 100644 --- a/tests/regression_tests/random_ray_auto_convert_source_energy/test.py +++ b/tests/regression_tests/random_ray_auto_convert_source_energy/test.py @@ -38,7 +38,7 @@ def test_random_ray_auto_convert_source_energy(method, source_type): # Convert to a multi-group model model.convert_to_multigroup( - method=method, groups='CASMO-8', nparticles=100, + method=method, energy_groups='CASMO-8', nparticles=100, overwrite_mgxs_library=False, mgxs_path="mgxs.h5", source_energy=source_energy ) diff --git a/tests/regression_tests/random_ray_auto_convert_temperature/test.py b/tests/regression_tests/random_ray_auto_convert_temperature/test.py index 99c99e6147f..368e8c82b27 100644 --- a/tests/regression_tests/random_ray_auto_convert_temperature/test.py +++ b/tests/regression_tests/random_ray_auto_convert_temperature/test.py @@ -34,7 +34,7 @@ def test_random_ray_auto_convert(method): # Convert to a multi-group model model.convert_to_multigroup( - method=method, groups='CASMO-2', nparticles=100, + method=method, energy_groups='CASMO-2', nparticles=100, overwrite_mgxs_library=False, mgxs_path="mgxs.h5", temperatures=[294.0, 394.0], temperature_settings=temp_settings ) diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/test.py b/tests/regression_tests/random_ray_diagonal_stabilization/test.py index 8d36e1d2581..2acc1915334 100644 --- a/tests/regression_tests/random_ray_diagonal_stabilization/test.py +++ b/tests/regression_tests/random_ray_diagonal_stabilization/test.py @@ -23,7 +23,7 @@ def test_random_ray_diagonal_stabilization(): # MGXS data with some negatives on the diagonal, in order # to trigger diagonal correction. model.convert_to_multigroup( - method='material_wise', groups='CASMO-70', nparticles=13, + method='material_wise', energy_groups='CASMO-70', nparticles=13, overwrite_mgxs_library=True, mgxs_path="mgxs.h5", correction='P0' ) diff --git a/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/__init__.py b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/inputs_true.dat b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/inputs_true.dat new file mode 100644 index 00000000000..3a4b82d3d00 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/inputs_true.dat @@ -0,0 +1,71 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 20 + 15 + true + +
0.01
+ s + 5 +
+ + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + multi-group + + + + -0.63 -0.63 -1.0 0.63 0.63 1.0 + + + 30.0 + 150.0 + + + + + + isotropic + 0.5 + + + 8 8 + -0.63 -0.63 + 0.63 0.63 + +
+
diff --git a/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_0.dat b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_0.dat new file mode 100644 index 00000000000..ccadbb12596 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_0.dat @@ -0,0 +1,2 @@ +k-combined: +1.072871E+00 6.955844E-03 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_1.dat b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_1.dat new file mode 100644 index 00000000000..ccadbb12596 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_1.dat @@ -0,0 +1,2 @@ +k-combined: +1.072871E+00 6.955844E-03 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_2.dat b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_2.dat new file mode 100644 index 00000000000..ccadbb12596 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_2.dat @@ -0,0 +1,2 @@ +k-combined: +1.072871E+00 6.955844E-03 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_3.dat b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_3.dat new file mode 100644 index 00000000000..ccadbb12596 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_3.dat @@ -0,0 +1,2 @@ +k-combined: +1.072871E+00 6.955844E-03 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_4.dat b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_4.dat new file mode 100644 index 00000000000..ccadbb12596 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_4.dat @@ -0,0 +1,2 @@ +k-combined: +1.072871E+00 6.955844E-03 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_5.dat b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_5.dat new file mode 100644 index 00000000000..ccadbb12596 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/isotropic/results_true_5.dat @@ -0,0 +1,2 @@ +k-combined: +1.072871E+00 6.955844E-03 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/inputs_true.dat b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/inputs_true.dat new file mode 100644 index 00000000000..e85b97b2685 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/inputs_true.dat @@ -0,0 +1,71 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 20 + 15 + true + +
0.01
+ s + 5 +
+ + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + multi-group + + + + -0.63 -0.63 -1.0 0.63 0.63 1.0 + + + 30.0 + 150.0 + + + + + + propagation + 0.5 + + + 8 8 + -0.63 -0.63 + 0.63 0.63 + +
+
diff --git a/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_0.dat b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_0.dat new file mode 100644 index 00000000000..ccadbb12596 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_0.dat @@ -0,0 +1,2 @@ +k-combined: +1.072871E+00 6.955844E-03 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_1.dat b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_1.dat new file mode 100644 index 00000000000..ccadbb12596 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_1.dat @@ -0,0 +1,2 @@ +k-combined: +1.072871E+00 6.955844E-03 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_2.dat b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_2.dat new file mode 100644 index 00000000000..ccadbb12596 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_2.dat @@ -0,0 +1,2 @@ +k-combined: +1.072871E+00 6.955844E-03 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_3.dat b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_3.dat new file mode 100644 index 00000000000..ccadbb12596 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_3.dat @@ -0,0 +1,2 @@ +k-combined: +1.072871E+00 6.955844E-03 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_4.dat b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_4.dat new file mode 100644 index 00000000000..ccadbb12596 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_4.dat @@ -0,0 +1,2 @@ +k-combined: +1.072871E+00 6.955844E-03 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_5.dat b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_5.dat new file mode 100644 index 00000000000..ccadbb12596 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/propagation/results_true_5.dat @@ -0,0 +1,2 @@ +k-combined: +1.072871E+00 6.955844E-03 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/test.py b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/test.py new file mode 100644 index 00000000000..7fe140d5a13 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization_kinetic/test.py @@ -0,0 +1,74 @@ +import os + +import openmc +from openmc.examples import pwr_pin_cell +from openmc import RegularMesh +from openmc.utility_funcs import change_directory +import numpy as np +import pytest + +from tests.testing_harness import KineticTolerantPyAPITestHarness + + +class KineticMGXSTestHarness(KineticTolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +@pytest.mark.parametrize("time_method", ["isotropic", + "propagation"]) +def test_random_ray_diagonal_stabilization(time_method): + with change_directory(time_method): + openmc.reset_auto_ids() + + # Start with a normal continuous energy model + model = pwr_pin_cell() + + # Convert to a multi-group model, with 70 group XS + # and transport correction enabled. This will generate + # MGXS data with some negatives on the diagonal, in order + # to trigger diagonal correction. + model.convert_to_multigroup( + method='material_wise', energy_groups='CASMO-70', nparticles=30, + overwrite_mgxs_library=True, mgxs_path="mgxs.h5", correction='P0', + kinetic=True, num_delayed_groups=6 + ) + + model.settings.timestep_parameters['n_timesteps'] = 5 + density_timeseries = np.linspace(1, 0.95, 100) + model.materials[2].set_density( + 'macro', density=1.0, density_timeseries=density_timeseries) + + # Convert to a random ray model + model.convert_to_random_ray() + + # Set the number of particles + model.settings.particles = 100 + + # Overlay an 8x8 mesh + n = 8 + mesh = RegularMesh() + mesh.dimension = (n, n) + bbox = model.geometry.bounding_box + mesh.lower_left = (bbox.lower_left[0], bbox.lower_left[1]) + mesh.upper_right = (bbox.upper_right[0], bbox.upper_right[1]) + model.settings.random_ray['source_region_meshes'] = [ + (mesh, [model.geometry.root_universe])] + model.settings.random_ray['time_derivative_method'] = time_method + + # Explicitly set the diagonal stabilization rho (default is otherwise 1.0). + # Note that if we set this to 0.0 (thus distabling stabilization), the + # problem should fail due to instability, so this is actually a good test + # problem. + model.settings.random_ray['diagonal_stabilization_rho'] = 0.5 + + # If rho was 0.0, the instability would cause failure after iteration 14, + # so we go a little past that. + model.settings.inactive = 15 + model.settings.batches = 20 + + harness = KineticMGXSTestHarness('statepoint.20', 6, model) + harness.main() diff --git a/tests/regression_tests/random_ray_k_eff_kinetic/__init__.py b/tests/regression_tests/random_ray_k_eff_kinetic/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/inputs_true.dat b/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/inputs_true.dat new file mode 100644 index 00000000000..503ecabaa68 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/inputs_true.dat @@ -0,0 +1,86 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 400 + 200 + true + +
0.01
+ 5 + s +
+ multi-group + + 100.0 + 20.0 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + 3 + isotropic + +
+ + + 1 2 3 4 5 6 7 8 + + + flux fission nu-fission + analog + + + 1 + precursors + + +
diff --git a/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_0.dat b/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_0.dat new file mode 100644 index 00000000000..3a18ad9c134 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_0.dat @@ -0,0 +1,26 @@ +k-combined: +1.325787E+00 5.102917E-04 +tally 1: +2.630331E+03 +3.459322E+04 +1.079948E+02 +5.831610E+01 +2.651573E+02 +3.515523E+02 +tally 2: +1.077582E+00 +5.805919E-03 +2.327022E+00 +2.707515E-02 +8.947059E-01 +4.002493E-03 +6.344423E-01 +2.012585E-03 +4.939693E-01 +1.220028E-03 +6.664308E-02 +2.220650E-05 +2.312960E-02 +2.674892E-06 +3.680705E-03 +6.773796E-08 diff --git a/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_1.dat b/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_1.dat new file mode 100644 index 00000000000..99af0b925e9 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_1.dat @@ -0,0 +1,26 @@ +k-combined: +1.325787E+00 5.102917E-04 +tally 1: +2.630191E+03 +3.458954E+04 +1.079890E+02 +5.830991E+01 +2.651432E+02 +3.515150E+02 +tally 2: +1.077582E+00 +5.805916E-03 +2.327021E+00 +2.707513E-02 +8.947056E-01 +4.002491E-03 +6.344421E-01 +2.012584E-03 +4.939691E-01 +1.220027E-03 +6.664304E-02 +2.220648E-05 +2.312958E-02 +2.674887E-06 +3.680701E-03 +6.773779E-08 diff --git a/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_2.dat b/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_2.dat new file mode 100644 index 00000000000..2adcc7d60e5 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_2.dat @@ -0,0 +1,26 @@ +k-combined: +1.325787E+00 5.102917E-04 +tally 1: +2.628846E+03 +3.455416E+04 +1.078947E+02 +5.820806E+01 +2.649123E+02 +3.509029E+02 +tally 2: +1.077582E+00 +5.805915E-03 +2.327021E+00 +2.707512E-02 +8.947054E-01 +4.002489E-03 +6.344416E-01 +2.012581E-03 +4.939683E-01 +1.220024E-03 +6.664281E-02 +2.220632E-05 +2.312938E-02 +2.674842E-06 +3.680634E-03 +6.773532E-08 diff --git a/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_3.dat b/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_3.dat new file mode 100644 index 00000000000..8c1780809f6 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_3.dat @@ -0,0 +1,26 @@ +k-combined: +1.325787E+00 5.102917E-04 +tally 1: +2.626025E+03 +3.448006E+04 +1.077396E+02 +5.804082E+01 +2.645322E+02 +3.498966E+02 +tally 2: +1.077582E+00 +5.805913E-03 +2.327020E+00 +2.707510E-02 +8.947048E-01 +4.002483E-03 +6.344403E-01 +2.012572E-03 +4.939660E-01 +1.220012E-03 +6.664210E-02 +2.220585E-05 +2.312878E-02 +2.674703E-06 +3.680429E-03 +6.772778E-08 diff --git a/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_4.dat b/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_4.dat new file mode 100644 index 00000000000..c6990b4cfe5 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_4.dat @@ -0,0 +1,26 @@ +k-combined: +1.325787E+00 5.102917E-04 +tally 1: +2.621902E+03 +3.437185E+04 +1.075310E+02 +5.781629E+01 +2.640207E+02 +3.485449E+02 +tally 2: +1.077581E+00 +5.805908E-03 +2.327017E+00 +2.707505E-02 +8.947036E-01 +4.002472E-03 +6.344375E-01 +2.012555E-03 +4.939613E-01 +1.219989E-03 +6.664066E-02 +2.220489E-05 +2.312757E-02 +2.674422E-06 +3.680018E-03 +6.771266E-08 diff --git a/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_5.dat b/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_5.dat new file mode 100644 index 00000000000..15c1bf1d97e --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_kinetic/isotropic/results_true_5.dat @@ -0,0 +1,26 @@ +k-combined: +1.325787E+00 5.102917E-04 +tally 1: +2.616591E+03 +3.423275E+04 +1.072737E+02 +5.753995E+01 +2.633897E+02 +3.468809E+02 +tally 2: +1.077581E+00 +5.805900E-03 +2.327014E+00 +2.707497E-02 +8.947015E-01 +4.002454E-03 +6.344330E-01 +2.012526E-03 +4.939535E-01 +1.219950E-03 +6.663827E-02 +2.220330E-05 +2.312557E-02 +2.673959E-06 +3.679344E-03 +6.768788E-08 diff --git a/tests/regression_tests/random_ray_k_eff_kinetic/propagation/inputs_true.dat b/tests/regression_tests/random_ray_k_eff_kinetic/propagation/inputs_true.dat new file mode 100644 index 00000000000..55e6e96a613 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_kinetic/propagation/inputs_true.dat @@ -0,0 +1,86 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 400 + 200 + true + +
0.01
+ 5 + s +
+ multi-group + + 100.0 + 20.0 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + 3 + propagation + +
+ + + 1 2 3 4 5 6 7 8 + + + flux fission nu-fission + analog + + + 1 + precursors + + +
diff --git a/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_0.dat b/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_0.dat new file mode 100644 index 00000000000..3a18ad9c134 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_0.dat @@ -0,0 +1,26 @@ +k-combined: +1.325787E+00 5.102917E-04 +tally 1: +2.630331E+03 +3.459322E+04 +1.079948E+02 +5.831610E+01 +2.651573E+02 +3.515523E+02 +tally 2: +1.077582E+00 +5.805919E-03 +2.327022E+00 +2.707515E-02 +8.947059E-01 +4.002493E-03 +6.344423E-01 +2.012585E-03 +4.939693E-01 +1.220028E-03 +6.664308E-02 +2.220650E-05 +2.312960E-02 +2.674892E-06 +3.680705E-03 +6.773796E-08 diff --git a/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_1.dat b/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_1.dat new file mode 100644 index 00000000000..99af0b925e9 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_1.dat @@ -0,0 +1,26 @@ +k-combined: +1.325787E+00 5.102917E-04 +tally 1: +2.630191E+03 +3.458954E+04 +1.079890E+02 +5.830991E+01 +2.651432E+02 +3.515150E+02 +tally 2: +1.077582E+00 +5.805916E-03 +2.327021E+00 +2.707513E-02 +8.947056E-01 +4.002491E-03 +6.344421E-01 +2.012584E-03 +4.939691E-01 +1.220027E-03 +6.664304E-02 +2.220648E-05 +2.312958E-02 +2.674887E-06 +3.680701E-03 +6.773779E-08 diff --git a/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_2.dat b/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_2.dat new file mode 100644 index 00000000000..2adcc7d60e5 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_2.dat @@ -0,0 +1,26 @@ +k-combined: +1.325787E+00 5.102917E-04 +tally 1: +2.628846E+03 +3.455416E+04 +1.078947E+02 +5.820806E+01 +2.649123E+02 +3.509029E+02 +tally 2: +1.077582E+00 +5.805915E-03 +2.327021E+00 +2.707512E-02 +8.947054E-01 +4.002489E-03 +6.344416E-01 +2.012581E-03 +4.939683E-01 +1.220024E-03 +6.664281E-02 +2.220632E-05 +2.312938E-02 +2.674842E-06 +3.680634E-03 +6.773532E-08 diff --git a/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_3.dat b/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_3.dat new file mode 100644 index 00000000000..8c1780809f6 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_3.dat @@ -0,0 +1,26 @@ +k-combined: +1.325787E+00 5.102917E-04 +tally 1: +2.626025E+03 +3.448006E+04 +1.077396E+02 +5.804082E+01 +2.645322E+02 +3.498966E+02 +tally 2: +1.077582E+00 +5.805913E-03 +2.327020E+00 +2.707510E-02 +8.947048E-01 +4.002483E-03 +6.344403E-01 +2.012572E-03 +4.939660E-01 +1.220012E-03 +6.664210E-02 +2.220585E-05 +2.312878E-02 +2.674703E-06 +3.680429E-03 +6.772778E-08 diff --git a/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_4.dat b/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_4.dat new file mode 100644 index 00000000000..c6990b4cfe5 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_4.dat @@ -0,0 +1,26 @@ +k-combined: +1.325787E+00 5.102917E-04 +tally 1: +2.621902E+03 +3.437185E+04 +1.075310E+02 +5.781629E+01 +2.640207E+02 +3.485449E+02 +tally 2: +1.077581E+00 +5.805908E-03 +2.327017E+00 +2.707505E-02 +8.947036E-01 +4.002472E-03 +6.344375E-01 +2.012555E-03 +4.939613E-01 +1.219989E-03 +6.664066E-02 +2.220489E-05 +2.312757E-02 +2.674422E-06 +3.680018E-03 +6.771266E-08 diff --git a/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_5.dat b/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_5.dat new file mode 100644 index 00000000000..15c1bf1d97e --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_kinetic/propagation/results_true_5.dat @@ -0,0 +1,26 @@ +k-combined: +1.325787E+00 5.102917E-04 +tally 1: +2.616591E+03 +3.423275E+04 +1.072737E+02 +5.753995E+01 +2.633897E+02 +3.468809E+02 +tally 2: +1.077581E+00 +5.805900E-03 +2.327014E+00 +2.707497E-02 +8.947015E-01 +4.002454E-03 +6.344330E-01 +2.012526E-03 +4.939535E-01 +1.219950E-03 +6.663827E-02 +2.220330E-05 +2.312557E-02 +2.673959E-06 +3.679344E-03 +6.768788E-08 diff --git a/tests/regression_tests/random_ray_k_eff_kinetic/test.py b/tests/regression_tests/random_ray_k_eff_kinetic/test.py new file mode 100644 index 00000000000..c8fdc8ea083 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_kinetic/test.py @@ -0,0 +1,30 @@ +import os + +import openmc +from openmc.utility_funcs import change_directory +from openmc.examples import random_ray_pin_cell +import pytest + +from tests.testing_harness import KineticTolerantPyAPITestHarness + + +class KineticMGXSTestHarness(KineticTolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +@pytest.mark.parametrize("time_method", ["isotropic", + "propagation"]) +def test_random_ray_basic_kinetic(time_method): + with change_directory(time_method): + openmc.reset_auto_ids() + model = random_ray_pin_cell(kinetic=True) + model.settings.timestep_parameters['n_timesteps'] = 5 + model.settings.random_ray['time_method'] = time_method + model.settings.batches = 400 + model.settings.inactive = 200 + harness = KineticMGXSTestHarness('statepoint.400', 6, model) + harness.main() diff --git a/tests/regression_tests/random_ray_k_eff_mesh_kinetic/__init__.py b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/inputs_true.dat b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/inputs_true.dat new file mode 100644 index 00000000000..1790a1edbeb --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/inputs_true.dat @@ -0,0 +1,138 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.126 0.126 + 10 10 + -0.63 -0.63 + +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 + + + 1.26 1.26 + 2 2 + -1.26 -1.26 + +2 2 +2 5 + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 400 + 200 + true + +
0.01
+ 5 + s +
+ multi-group + + 100.0 + 20.0 + + + -1.26 -1.26 -1 1.26 1.26 1 + + + true + 3 + isotropic + + + + + + + + 40 40 + -1.26 -1.26 + 1.26 1.26 + +
+ + + 2 2 + -1.26 -1.26 + 1.26 1.26 + + + 1 + + + 1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0 + + + 1 2 3 4 5 6 7 8 + + + 1 2 + flux fission nu-fission + analog + + + 1 3 + precursors + + +
diff --git a/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_0.dat b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_0.dat new file mode 100644 index 00000000000..b7863f78005 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_0.dat @@ -0,0 +1,236 @@ +k-combined: +1.311387E+00 6.434473E-04 +tally 1: +6.829734E+01 +2.332560E+01 +2.535005E+01 +3.214213E+00 +6.169695E+01 +1.903901E+01 +4.338057E+01 +9.409840E+00 +6.449747E+00 +2.080191E-01 +1.569739E+01 +1.232177E+00 +2.952526E+01 +4.358735E+00 +9.562385E-01 +4.572024E-03 +2.327293E+00 +2.708184E-02 +3.764580E+01 +7.086116E+00 +1.253550E+00 +7.857205E-03 +3.050891E+00 +4.654123E-02 +9.736564E+01 +4.740043E+01 +1.143250E+00 +6.535133E-03 +2.782476E+00 +3.871104E-02 +2.108671E+02 +2.223325E+02 +3.217847E-01 +5.177688E-04 +7.962336E-01 +3.170197E-03 +1.121239E+02 +6.287031E+01 +1.520419E+00 +1.156430E-02 +4.228968E+00 +8.946678E-02 +1.128474E+02 +6.367345E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.341703E+01 +1.426702E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.110513E+01 +4.837669E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.101197E+01 +8.409994E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.815385E+01 +4.817107E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.887447E+02 +1.781287E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.671216E+01 +4.678026E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.892637E+01 +1.736282E+01 +2.179942E+01 +2.376644E+00 +5.305542E+01 +1.407777E+01 +4.076286E+01 +8.308305E+00 +6.059136E+00 +1.835802E-01 +1.474673E+01 +1.087416E+00 +2.909448E+01 +4.232468E+00 +9.431274E-01 +4.447499E-03 +2.295383E+00 +2.634424E-02 +3.699617E+01 +6.843656E+00 +1.232522E+00 +7.595798E-03 +2.999711E+00 +4.499282E-02 +9.745207E+01 +4.748461E+01 +1.145047E+00 +6.555693E-03 +2.786851E+00 +3.883283E-02 +2.151446E+02 +2.314420E+02 +3.282013E-01 +5.386136E-04 +8.121113E-01 +3.297825E-03 +1.139091E+02 +6.488702E+01 +1.540814E+00 +1.187500E-02 +4.285697E+00 +9.187046E-02 +6.816479E+01 +2.323491E+01 +2.537715E+01 +3.221014E+00 +6.176291E+01 +1.907929E+01 +4.333813E+01 +9.391406E+00 +6.462978E+00 +2.088716E-01 +1.572960E+01 +1.237227E+00 +2.951601E+01 +4.356011E+00 +9.587693E-01 +4.596260E-03 +2.333452E+00 +2.722540E-02 +3.763313E+01 +7.081357E+00 +1.256873E+00 +7.898901E-03 +3.058977E+00 +4.678821E-02 +9.737405E+01 +4.740862E+01 +1.146696E+00 +6.574588E-03 +2.790864E+00 +3.894475E-02 +2.109852E+02 +2.225806E+02 +3.228462E-01 +5.211850E-04 +7.988604E-01 +3.191114E-03 +1.121527E+02 +6.290039E+01 +1.524613E+00 +1.162754E-02 +4.240634E+00 +8.995597E-02 +tally 2: +3.727239E-01 +6.947125E-04 +8.048913E-01 +3.239702E-03 +3.094690E-01 +4.789219E-04 +2.194466E-01 +2.408177E-04 +1.708586E-01 +1.459836E-04 +2.305111E-02 +2.657139E-06 +8.000274E-03 +3.200666E-07 +1.273116E-03 +8.105247E-09 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.327663E-01 +5.537155E-04 +7.186034E-01 +2.582180E-03 +2.762925E-01 +3.817212E-04 +1.959210E-01 +1.919420E-04 +1.525418E-01 +1.163551E-04 +2.057993E-02 +2.117853E-06 +7.142610E-03 +2.551067E-07 +1.136632E-03 +6.460228E-09 +3.721919E-01 +6.927141E-04 +8.037423E-01 +3.230382E-03 +3.090272E-01 +4.775443E-04 +2.191334E-01 +2.401250E-04 +1.706146E-01 +1.455636E-04 +2.301820E-02 +2.649495E-06 +7.988854E-03 +3.191459E-07 +1.271298E-03 +8.081932E-09 diff --git a/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_1.dat b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_1.dat new file mode 100644 index 00000000000..5200ac7ec65 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_1.dat @@ -0,0 +1,236 @@ +k-combined: +1.311387E+00 6.434473E-04 +tally 1: +6.835756E+01 +2.336674E+01 +2.537239E+01 +3.219879E+00 +6.175132E+01 +1.907257E+01 +4.341878E+01 +9.426419E+00 +6.455425E+00 +2.083855E-01 +1.571121E+01 +1.234347E+00 +2.955122E+01 +4.366403E+00 +9.570792E-01 +4.580066E-03 +2.329339E+00 +2.712948E-02 +3.767889E+01 +7.098579E+00 +1.254652E+00 +7.871025E-03 +3.053572E+00 +4.662309E-02 +9.745117E+01 +4.748374E+01 +1.144254E+00 +6.546620E-03 +2.784921E+00 +3.877908E-02 +2.110520E+02 +2.227226E+02 +3.220668E-01 +5.186772E-04 +7.969317E-01 +3.175759E-03 +1.122227E+02 +6.298109E+01 +1.521758E+00 +1.158468E-02 +4.232692E+00 +8.962442E-02 +1.129470E+02 +6.378591E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.346409E+01 +1.429217E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.113248E+01 +4.846180E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.104802E+01 +8.424783E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.824006E+01 +4.825572E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.889103E+02 +1.784414E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.679731E+01 +4.686268E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.897831E+01 +1.739344E+01 +2.181862E+01 +2.380832E+00 +5.310216E+01 +1.410258E+01 +4.079875E+01 +8.322942E+00 +6.064470E+00 +1.839036E-01 +1.475971E+01 +1.089331E+00 +2.912006E+01 +4.239914E+00 +9.439565E-01 +4.455323E-03 +2.297401E+00 +2.639058E-02 +3.702869E+01 +6.855693E+00 +1.233605E+00 +7.609159E-03 +3.002348E+00 +4.507196E-02 +9.753767E+01 +4.756807E+01 +1.146053E+00 +6.567215E-03 +2.789299E+00 +3.890108E-02 +2.153332E+02 +2.318480E+02 +3.284890E-01 +5.395582E-04 +8.128231E-01 +3.303609E-03 +1.140094E+02 +6.500133E+01 +1.542171E+00 +1.189592E-02 +4.289470E+00 +9.203230E-02 +6.822489E+01 +2.327589E+01 +2.539951E+01 +3.226691E+00 +6.181733E+01 +1.911292E+01 +4.337629E+01 +9.407953E+00 +6.468668E+00 +2.092395E-01 +1.574345E+01 +1.239406E+00 +2.954196E+01 +4.363674E+00 +9.596122E-01 +4.604345E-03 +2.335504E+00 +2.727329E-02 +3.766621E+01 +7.093812E+00 +1.257978E+00 +7.912794E-03 +3.061666E+00 +4.687051E-02 +9.745958E+01 +4.749194E+01 +1.147704E+00 +6.586143E-03 +2.793315E+00 +3.901320E-02 +2.111701E+02 +2.229709E+02 +3.231291E-01 +5.220987E-04 +7.995603E-01 +3.196708E-03 +1.122514E+02 +6.301119E+01 +1.525955E+00 +1.164802E-02 +4.244367E+00 +9.011441E-02 +tally 2: +3.727210E-01 +6.946049E-04 +8.048851E-01 +3.239200E-03 +3.094666E-01 +4.788479E-04 +2.194451E-01 +2.407807E-04 +1.708575E-01 +1.459614E-04 +2.305100E-02 +2.656744E-06 +8.000275E-03 +3.200220E-07 +1.273127E-03 +8.104266E-09 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.327638E-01 +5.536587E-04 +7.185980E-01 +2.581916E-03 +2.762905E-01 +3.816822E-04 +1.959196E-01 +1.919225E-04 +1.525408E-01 +1.163435E-04 +2.057984E-02 +2.117649E-06 +7.142612E-03 +2.550845E-07 +1.136643E-03 +6.459783E-09 +3.721891E-01 +6.926236E-04 +8.037363E-01 +3.229960E-03 +3.090249E-01 +4.774820E-04 +2.191319E-01 +2.400939E-04 +1.706136E-01 +1.455450E-04 +2.301810E-02 +2.649166E-06 +7.988856E-03 +3.191091E-07 +1.271310E-03 +8.081148E-09 diff --git a/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_2.dat b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_2.dat new file mode 100644 index 00000000000..97167c4acb6 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_2.dat @@ -0,0 +1,236 @@ +k-combined: +1.311387E+00 6.434473E-04 +tally 1: +6.841014E+01 +2.340271E+01 +2.539213E+01 +3.224892E+00 +6.179937E+01 +1.910227E+01 +4.345803E+01 +9.443470E+00 +6.461275E+00 +2.087633E-01 +1.572545E+01 +1.236585E+00 +2.958154E+01 +4.375368E+00 +9.580618E-01 +4.589475E-03 +2.331731E+00 +2.718521E-02 +3.771765E+01 +7.113193E+00 +1.255944E+00 +7.887240E-03 +3.056716E+00 +4.671914E-02 +9.755580E+01 +4.758576E+01 +1.145483E+00 +6.560687E-03 +2.787911E+00 +3.886241E-02 +2.112850E+02 +2.232146E+02 +3.224207E-01 +5.198176E-04 +7.978074E-01 +3.182742E-03 +1.123400E+02 +6.311292E+01 +1.523337E+00 +1.160873E-02 +4.237083E+00 +8.981046E-02 +1.130417E+02 +6.389290E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.351363E+01 +1.431867E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.116483E+01 +4.856257E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.109119E+01 +8.442515E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.834643E+01 +4.836027E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.891195E+02 +1.788369E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.689946E+01 +4.696163E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.902258E+01 +1.741956E+01 +2.183519E+01 +2.384450E+00 +5.314249E+01 +1.412401E+01 +4.083560E+01 +8.337984E+00 +6.069963E+00 +1.842369E-01 +1.477308E+01 +1.091305E+00 +2.914984E+01 +4.248591E+00 +9.449228E-01 +4.464449E-03 +2.299753E+00 +2.644464E-02 +3.706654E+01 +6.869716E+00 +1.234868E+00 +7.624744E-03 +3.005421E+00 +4.516427E-02 +9.764206E+01 +4.766994E+01 +1.147280E+00 +6.581283E-03 +2.792284E+00 +3.898441E-02 +2.155707E+02 +2.323596E+02 +3.288495E-01 +5.407431E-04 +8.137152E-01 +3.310864E-03 +1.141285E+02 +6.513725E+01 +1.543769E+00 +1.192058E-02 +4.293915E+00 +9.222311E-02 +6.827732E+01 +2.331168E+01 +2.541926E+01 +3.231710E+00 +6.186540E+01 +1.914265E+01 +4.341549E+01 +9.424964E+00 +6.474528E+00 +2.096188E-01 +1.575771E+01 +1.241652E+00 +2.957227E+01 +4.372632E+00 +9.605972E-01 +4.613802E-03 +2.337901E+00 +2.732931E-02 +3.770496E+01 +7.108413E+00 +1.259272E+00 +7.929092E-03 +3.064817E+00 +4.696704E-02 +9.756421E+01 +4.759397E+01 +1.148936E+00 +6.600293E-03 +2.796314E+00 +3.909702E-02 +2.114033E+02 +2.234634E+02 +3.234842E-01 +5.232469E-04 +8.004390E-01 +3.203738E-03 +1.123688E+02 +6.314309E+01 +1.527539E+00 +1.167220E-02 +4.248772E+00 +9.030152E-02 +tally 2: +3.727211E-01 +6.946052E-04 +8.048854E-01 +3.239203E-03 +3.094668E-01 +4.788484E-04 +2.194454E-01 +2.407815E-04 +1.708581E-01 +1.459624E-04 +2.305119E-02 +2.656788E-06 +8.000435E-03 +3.200348E-07 +1.273182E-03 +8.104961E-09 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.327638E-01 +5.536589E-04 +7.185983E-01 +2.581918E-03 +2.762906E-01 +3.816826E-04 +1.959200E-01 +1.919231E-04 +1.525414E-01 +1.163444E-04 +2.058001E-02 +2.117684E-06 +7.142755E-03 +2.550947E-07 +1.136691E-03 +6.460335E-09 +3.721891E-01 +6.926238E-04 +8.037366E-01 +3.229963E-03 +3.090251E-01 +4.774825E-04 +2.191322E-01 +2.400946E-04 +1.706142E-01 +1.455461E-04 +2.301829E-02 +2.649209E-06 +7.989016E-03 +3.191219E-07 +1.271365E-03 +8.081841E-09 diff --git a/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_3.dat b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_3.dat new file mode 100644 index 00000000000..7d455f49fdc --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_3.dat @@ -0,0 +1,236 @@ +k-combined: +1.311387E+00 6.434473E-04 +tally 1: +6.846326E+01 +2.343906E+01 +2.541206E+01 +3.229957E+00 +6.184788E+01 +1.913227E+01 +4.349768E+01 +9.460710E+00 +6.467182E+00 +2.091452E-01 +1.573983E+01 +1.238847E+00 +2.961216E+01 +4.384431E+00 +9.590541E-01 +4.598987E-03 +2.334146E+00 +2.724155E-02 +3.775680E+01 +7.127966E+00 +1.257248E+00 +7.903631E-03 +3.059891E+00 +4.681623E-02 +9.766141E+01 +4.768885E+01 +1.146723E+00 +6.574901E-03 +2.790929E+00 +3.894660E-02 +2.115200E+02 +2.237114E+02 +3.227776E-01 +5.209693E-04 +7.986907E-01 +3.189793E-03 +1.124584E+02 +6.324599E+01 +1.524929E+00 +1.163300E-02 +4.241512E+00 +8.999826E-02 +1.131374E+02 +6.400109E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.356368E+01 +1.434547E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.119750E+01 +4.866444E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.113478E+01 +8.460437E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.845378E+01 +4.846591E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.893306E+02 +1.792363E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.700245E+01 +4.706152E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.906729E+01 +1.744596E+01 +2.185192E+01 +2.388105E+00 +5.318321E+01 +1.414566E+01 +4.087282E+01 +8.353191E+00 +6.075510E+00 +1.845738E-01 +1.478658E+01 +1.093301E+00 +2.917992E+01 +4.257364E+00 +9.458987E-01 +4.473675E-03 +2.302128E+00 +2.649928E-02 +3.710477E+01 +6.883893E+00 +1.236143E+00 +7.640499E-03 +3.008525E+00 +4.525760E-02 +9.774743E+01 +4.777288E+01 +1.148518E+00 +6.595498E-03 +2.795298E+00 +3.906861E-02 +2.158102E+02 +2.328763E+02 +3.292132E-01 +5.419397E-04 +8.146150E-01 +3.318191E-03 +1.142486E+02 +6.527446E+01 +1.545380E+00 +1.194548E-02 +4.298397E+00 +9.241572E-02 +6.833030E+01 +2.334787E+01 +2.543920E+01 +3.236782E+00 +6.191392E+01 +1.917269E+01 +4.345509E+01 +9.442165E+00 +6.480446E+00 +2.100022E-01 +1.577211E+01 +1.243923E+00 +2.960287E+01 +4.381688E+00 +9.615920E-01 +4.623363E-03 +2.340322E+00 +2.738594E-02 +3.774408E+01 +7.123173E+00 +1.260580E+00 +7.945567E-03 +3.067999E+00 +4.706463E-02 +9.766982E+01 +4.769706E+01 +1.150180E+00 +6.614591E-03 +2.799341E+00 +3.918171E-02 +2.116384E+02 +2.239608E+02 +3.238424E-01 +5.244063E-04 +8.013253E-01 +3.210837E-03 +1.124872E+02 +6.327624E+01 +1.529136E+00 +1.169661E-02 +4.253213E+00 +9.049039E-02 +tally 2: +3.727212E-01 +6.946055E-04 +8.048859E-01 +3.239206E-03 +3.094670E-01 +4.788493E-04 +2.194460E-01 +2.407828E-04 +1.708591E-01 +1.459642E-04 +2.305151E-02 +2.656862E-06 +8.000705E-03 +3.200564E-07 +1.273273E-03 +8.106120E-09 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.327639E-01 +5.536592E-04 +7.185987E-01 +2.581921E-03 +2.762909E-01 +3.816833E-04 +1.959205E-01 +1.919242E-04 +1.525423E-01 +1.163458E-04 +2.058029E-02 +2.117743E-06 +7.142994E-03 +2.551118E-07 +1.136772E-03 +6.461254E-09 +3.721892E-01 +6.926241E-04 +8.037371E-01 +3.229967E-03 +3.090254E-01 +4.774834E-04 +2.191328E-01 +2.400960E-04 +1.706153E-01 +1.455479E-04 +2.301861E-02 +2.649283E-06 +7.989286E-03 +3.191434E-07 +1.271456E-03 +8.082996E-09 diff --git a/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_4.dat b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_4.dat new file mode 100644 index 00000000000..6114b3cd064 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_4.dat @@ -0,0 +1,236 @@ +k-combined: +1.311387E+00 6.434473E-04 +tally 1: +6.851773E+01 +2.347638E+01 +2.543250E+01 +3.235154E+00 +6.189761E+01 +1.916305E+01 +4.353818E+01 +9.478338E+00 +6.473217E+00 +2.095358E-01 +1.575452E+01 +1.241161E+00 +2.964337E+01 +4.393677E+00 +9.600653E-01 +4.608690E-03 +2.336607E+00 +2.729903E-02 +3.779670E+01 +7.143037E+00 +1.258578E+00 +7.920353E-03 +3.063126E+00 +4.691528E-02 +9.776897E+01 +4.779395E+01 +1.147986E+00 +6.589392E-03 +2.794003E+00 +3.903244E-02 +2.117592E+02 +2.242177E+02 +3.231411E-01 +5.221432E-04 +7.995900E-01 +3.196980E-03 +1.125791E+02 +6.338177E+01 +1.526551E+00 +1.165777E-02 +4.246026E+00 +9.018990E-02 +1.132353E+02 +6.411189E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.361478E+01 +1.437285E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.123078E+01 +4.876834E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.117919E+01 +8.478714E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.856309E+01 +4.857359E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.895455E+02 +1.796434E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.710741E+01 +4.716341E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.911317E+01 +1.747308E+01 +2.186909E+01 +2.391858E+00 +5.322498E+01 +1.416789E+01 +4.091085E+01 +8.368741E+00 +6.081177E+00 +1.849182E-01 +1.480037E+01 +1.095342E+00 +2.921058E+01 +4.266313E+00 +9.468932E-01 +4.483087E-03 +2.304548E+00 +2.655503E-02 +3.714373E+01 +6.898356E+00 +1.237443E+00 +7.656573E-03 +3.011688E+00 +4.535281E-02 +9.785474E+01 +4.787783E+01 +1.149780E+00 +6.609991E-03 +2.798368E+00 +3.915446E-02 +2.160541E+02 +2.334029E+02 +3.295834E-01 +5.431594E-04 +8.155311E-01 +3.325659E-03 +1.143711E+02 +6.541445E+01 +1.547023E+00 +1.197088E-02 +4.302966E+00 +9.261227E-02 +6.838461E+01 +2.338501E+01 +2.545964E+01 +3.241986E+00 +6.196367E+01 +1.920352E+01 +4.349554E+01 +9.459752E+00 +6.486491E+00 +2.103942E-01 +1.578682E+01 +1.246245E+00 +2.963407E+01 +4.390927E+00 +9.626057E-01 +4.633116E-03 +2.342790E+00 +2.744371E-02 +3.778395E+01 +7.138229E+00 +1.261912E+00 +7.962373E-03 +3.071242E+00 +4.716418E-02 +9.777738E+01 +4.780217E+01 +1.151446E+00 +6.629169E-03 +2.802424E+00 +3.926806E-02 +2.118778E+02 +2.244678E+02 +3.242071E-01 +5.255880E-04 +8.022277E-01 +3.218073E-03 +1.126079E+02 +6.341209E+01 +1.530763E+00 +1.172153E-02 +4.257740E+00 +9.068312E-02 +tally 2: +3.727213E-01 +6.946060E-04 +8.048865E-01 +3.239212E-03 +3.094674E-01 +4.788505E-04 +2.194469E-01 +2.407847E-04 +1.708606E-01 +1.459668E-04 +2.305197E-02 +2.656966E-06 +8.001085E-03 +3.200868E-07 +1.273400E-03 +8.107739E-09 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.327641E-01 +5.536596E-04 +7.185993E-01 +2.581925E-03 +2.762912E-01 +3.816842E-04 +1.959213E-01 +1.919257E-04 +1.525436E-01 +1.163478E-04 +2.058070E-02 +2.117825E-06 +7.143331E-03 +2.551359E-07 +1.136885E-03 +6.462537E-09 +3.721894E-01 +6.926247E-04 +8.037378E-01 +3.229972E-03 +3.090257E-01 +4.774846E-04 +2.191337E-01 +2.400979E-04 +1.706168E-01 +1.455504E-04 +2.301907E-02 +2.649387E-06 +7.989665E-03 +3.191737E-07 +1.271582E-03 +8.084610E-09 diff --git a/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_5.dat b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_5.dat new file mode 100644 index 00000000000..a30dfa88059 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/isotropic/results_true_5.dat @@ -0,0 +1,236 @@ +k-combined: +1.311387E+00 6.434473E-04 +tally 1: +6.857342E+01 +2.351456E+01 +2.545339E+01 +3.240472E+00 +6.194846E+01 +1.919455E+01 +4.357948E+01 +9.496329E+00 +6.479370E+00 +2.099343E-01 +1.576949E+01 +1.243521E+00 +2.967512E+01 +4.403095E+00 +9.610942E-01 +4.618574E-03 +2.339111E+00 +2.735758E-02 +3.783729E+01 +7.158388E+00 +1.259930E+00 +7.937385E-03 +3.066418E+00 +4.701617E-02 +9.787835E+01 +4.790095E+01 +1.149271E+00 +6.604145E-03 +2.797129E+00 +3.911983E-02 +2.120024E+02 +2.247331E+02 +3.235106E-01 +5.233379E-04 +8.005043E-01 +3.204296E-03 +1.127018E+02 +6.352005E+01 +1.528203E+00 +1.168300E-02 +4.250618E+00 +9.038510E-02 +1.133352E+02 +6.422512E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.366687E+01 +1.440079E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.126465E+01 +4.887416E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.122436E+01 +8.497326E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.867425E+01 +4.868321E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.897639E+02 +1.800576E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.721418E+01 +4.726718E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.916011E+01 +1.750084E+01 +2.188664E+01 +2.395700E+00 +5.326771E+01 +1.419065E+01 +4.094962E+01 +8.384611E+00 +6.086955E+00 +1.852698E-01 +1.481443E+01 +1.097424E+00 +2.924177E+01 +4.275430E+00 +9.479052E-01 +4.492675E-03 +2.307011E+00 +2.661183E-02 +3.718338E+01 +6.913090E+00 +1.238765E+00 +7.672947E-03 +3.014906E+00 +4.544980E-02 +9.796388E+01 +4.798469E+01 +1.151062E+00 +6.624747E-03 +2.801490E+00 +3.924187E-02 +2.163020E+02 +2.339388E+02 +3.299598E-01 +5.444008E-04 +8.164626E-01 +3.333259E-03 +1.144957E+02 +6.555703E+01 +1.548694E+00 +1.199676E-02 +4.307614E+00 +9.281247E-02 +6.844016E+01 +2.342301E+01 +2.548054E+01 +3.247311E+00 +6.201454E+01 +1.923506E+01 +4.353678E+01 +9.477701E+00 +6.492655E+00 +2.107942E-01 +1.580182E+01 +1.248615E+00 +2.966581E+01 +4.400337E+00 +9.636372E-01 +4.643051E-03 +2.345300E+00 +2.750256E-02 +3.782452E+01 +7.153566E+00 +1.263268E+00 +7.979492E-03 +3.074542E+00 +4.726558E-02 +9.788675E+01 +4.790918E+01 +1.152734E+00 +6.644009E-03 +2.805559E+00 +3.935597E-02 +2.121212E+02 +2.249837E+02 +3.245778E-01 +5.267908E-04 +8.031451E-01 +3.225437E-03 +1.127307E+02 +6.355044E+01 +1.532419E+00 +1.174690E-02 +4.262347E+00 +9.087943E-02 +tally 2: +3.727215E-01 +6.946067E-04 +8.048874E-01 +3.239219E-03 +3.094679E-01 +4.788521E-04 +2.194480E-01 +2.407872E-04 +1.708625E-01 +1.459701E-04 +2.305256E-02 +2.657102E-06 +8.001574E-03 +3.201259E-07 +1.273562E-03 +8.109804E-09 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.327642E-01 +5.536601E-04 +7.186001E-01 +2.581931E-03 +2.762917E-01 +3.816855E-04 +1.959223E-01 +1.919277E-04 +1.525453E-01 +1.163504E-04 +2.058122E-02 +2.117933E-06 +7.143765E-03 +2.551669E-07 +1.137029E-03 +6.464173E-09 +3.721896E-01 +6.926253E-04 +8.037386E-01 +3.229979E-03 +3.090263E-01 +4.774861E-04 +2.191348E-01 +2.401004E-04 +1.706187E-01 +1.455537E-04 +2.301965E-02 +2.649522E-06 +7.990153E-03 +3.192127E-07 +1.271744E-03 +8.086669E-09 diff --git a/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/inputs_true.dat b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/inputs_true.dat new file mode 100644 index 00000000000..4040f8f18be --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/inputs_true.dat @@ -0,0 +1,138 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.126 0.126 + 10 10 + -0.63 -0.63 + +10 10 10 10 10 10 10 10 10 10 +10 10 10 10 10 10 10 10 10 10 +10 10 10 10 10 10 10 10 10 10 +10 10 10 10 10 10 10 10 10 10 +10 10 10 10 10 10 10 10 10 10 +10 10 10 10 10 10 10 10 10 10 +10 10 10 10 10 10 10 10 10 10 +10 10 10 10 10 10 10 10 10 10 +10 10 10 10 10 10 10 10 10 10 +10 10 10 10 10 10 10 10 10 10 + + + 1.26 1.26 + 2 2 + -1.26 -1.26 + +9 9 +9 12 + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 400 + 200 + true + +
0.01
+ 5 + s +
+ multi-group + + 100.0 + 20.0 + + + -1.26 -1.26 -1 1.26 1.26 1 + + + true + 3 + propagation + + + + + + + + 40 40 + -1.26 -1.26 + 1.26 1.26 + +
+ + + 2 2 + -1.26 -1.26 + 1.26 1.26 + + + 3 + + + 1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0 + + + 1 2 3 4 5 6 7 8 + + + 4 5 + flux fission nu-fission + analog + + + 4 6 + precursors + + +
diff --git a/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_0.dat b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_0.dat new file mode 100644 index 00000000000..b7863f78005 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_0.dat @@ -0,0 +1,236 @@ +k-combined: +1.311387E+00 6.434473E-04 +tally 1: +6.829734E+01 +2.332560E+01 +2.535005E+01 +3.214213E+00 +6.169695E+01 +1.903901E+01 +4.338057E+01 +9.409840E+00 +6.449747E+00 +2.080191E-01 +1.569739E+01 +1.232177E+00 +2.952526E+01 +4.358735E+00 +9.562385E-01 +4.572024E-03 +2.327293E+00 +2.708184E-02 +3.764580E+01 +7.086116E+00 +1.253550E+00 +7.857205E-03 +3.050891E+00 +4.654123E-02 +9.736564E+01 +4.740043E+01 +1.143250E+00 +6.535133E-03 +2.782476E+00 +3.871104E-02 +2.108671E+02 +2.223325E+02 +3.217847E-01 +5.177688E-04 +7.962336E-01 +3.170197E-03 +1.121239E+02 +6.287031E+01 +1.520419E+00 +1.156430E-02 +4.228968E+00 +8.946678E-02 +1.128474E+02 +6.367345E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.341703E+01 +1.426702E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.110513E+01 +4.837669E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.101197E+01 +8.409994E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.815385E+01 +4.817107E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.887447E+02 +1.781287E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.671216E+01 +4.678026E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.892637E+01 +1.736282E+01 +2.179942E+01 +2.376644E+00 +5.305542E+01 +1.407777E+01 +4.076286E+01 +8.308305E+00 +6.059136E+00 +1.835802E-01 +1.474673E+01 +1.087416E+00 +2.909448E+01 +4.232468E+00 +9.431274E-01 +4.447499E-03 +2.295383E+00 +2.634424E-02 +3.699617E+01 +6.843656E+00 +1.232522E+00 +7.595798E-03 +2.999711E+00 +4.499282E-02 +9.745207E+01 +4.748461E+01 +1.145047E+00 +6.555693E-03 +2.786851E+00 +3.883283E-02 +2.151446E+02 +2.314420E+02 +3.282013E-01 +5.386136E-04 +8.121113E-01 +3.297825E-03 +1.139091E+02 +6.488702E+01 +1.540814E+00 +1.187500E-02 +4.285697E+00 +9.187046E-02 +6.816479E+01 +2.323491E+01 +2.537715E+01 +3.221014E+00 +6.176291E+01 +1.907929E+01 +4.333813E+01 +9.391406E+00 +6.462978E+00 +2.088716E-01 +1.572960E+01 +1.237227E+00 +2.951601E+01 +4.356011E+00 +9.587693E-01 +4.596260E-03 +2.333452E+00 +2.722540E-02 +3.763313E+01 +7.081357E+00 +1.256873E+00 +7.898901E-03 +3.058977E+00 +4.678821E-02 +9.737405E+01 +4.740862E+01 +1.146696E+00 +6.574588E-03 +2.790864E+00 +3.894475E-02 +2.109852E+02 +2.225806E+02 +3.228462E-01 +5.211850E-04 +7.988604E-01 +3.191114E-03 +1.121527E+02 +6.290039E+01 +1.524613E+00 +1.162754E-02 +4.240634E+00 +8.995597E-02 +tally 2: +3.727239E-01 +6.947125E-04 +8.048913E-01 +3.239702E-03 +3.094690E-01 +4.789219E-04 +2.194466E-01 +2.408177E-04 +1.708586E-01 +1.459836E-04 +2.305111E-02 +2.657139E-06 +8.000274E-03 +3.200666E-07 +1.273116E-03 +8.105247E-09 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.327663E-01 +5.537155E-04 +7.186034E-01 +2.582180E-03 +2.762925E-01 +3.817212E-04 +1.959210E-01 +1.919420E-04 +1.525418E-01 +1.163551E-04 +2.057993E-02 +2.117853E-06 +7.142610E-03 +2.551067E-07 +1.136632E-03 +6.460228E-09 +3.721919E-01 +6.927141E-04 +8.037423E-01 +3.230382E-03 +3.090272E-01 +4.775443E-04 +2.191334E-01 +2.401250E-04 +1.706146E-01 +1.455636E-04 +2.301820E-02 +2.649495E-06 +7.988854E-03 +3.191459E-07 +1.271298E-03 +8.081932E-09 diff --git a/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_1.dat b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_1.dat new file mode 100644 index 00000000000..5200ac7ec65 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_1.dat @@ -0,0 +1,236 @@ +k-combined: +1.311387E+00 6.434473E-04 +tally 1: +6.835756E+01 +2.336674E+01 +2.537239E+01 +3.219879E+00 +6.175132E+01 +1.907257E+01 +4.341878E+01 +9.426419E+00 +6.455425E+00 +2.083855E-01 +1.571121E+01 +1.234347E+00 +2.955122E+01 +4.366403E+00 +9.570792E-01 +4.580066E-03 +2.329339E+00 +2.712948E-02 +3.767889E+01 +7.098579E+00 +1.254652E+00 +7.871025E-03 +3.053572E+00 +4.662309E-02 +9.745117E+01 +4.748374E+01 +1.144254E+00 +6.546620E-03 +2.784921E+00 +3.877908E-02 +2.110520E+02 +2.227226E+02 +3.220668E-01 +5.186772E-04 +7.969317E-01 +3.175759E-03 +1.122227E+02 +6.298109E+01 +1.521758E+00 +1.158468E-02 +4.232692E+00 +8.962442E-02 +1.129470E+02 +6.378591E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.346409E+01 +1.429217E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.113248E+01 +4.846180E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.104802E+01 +8.424783E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.824006E+01 +4.825572E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.889103E+02 +1.784414E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.679731E+01 +4.686268E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.897831E+01 +1.739344E+01 +2.181862E+01 +2.380832E+00 +5.310216E+01 +1.410258E+01 +4.079875E+01 +8.322942E+00 +6.064470E+00 +1.839036E-01 +1.475971E+01 +1.089331E+00 +2.912006E+01 +4.239914E+00 +9.439565E-01 +4.455323E-03 +2.297401E+00 +2.639058E-02 +3.702869E+01 +6.855693E+00 +1.233605E+00 +7.609159E-03 +3.002348E+00 +4.507196E-02 +9.753767E+01 +4.756807E+01 +1.146053E+00 +6.567215E-03 +2.789299E+00 +3.890108E-02 +2.153332E+02 +2.318480E+02 +3.284890E-01 +5.395582E-04 +8.128231E-01 +3.303609E-03 +1.140094E+02 +6.500133E+01 +1.542171E+00 +1.189592E-02 +4.289470E+00 +9.203230E-02 +6.822489E+01 +2.327589E+01 +2.539951E+01 +3.226691E+00 +6.181733E+01 +1.911292E+01 +4.337629E+01 +9.407953E+00 +6.468668E+00 +2.092395E-01 +1.574345E+01 +1.239406E+00 +2.954196E+01 +4.363674E+00 +9.596122E-01 +4.604345E-03 +2.335504E+00 +2.727329E-02 +3.766621E+01 +7.093812E+00 +1.257978E+00 +7.912794E-03 +3.061666E+00 +4.687051E-02 +9.745958E+01 +4.749194E+01 +1.147704E+00 +6.586143E-03 +2.793315E+00 +3.901320E-02 +2.111701E+02 +2.229709E+02 +3.231291E-01 +5.220987E-04 +7.995603E-01 +3.196708E-03 +1.122514E+02 +6.301119E+01 +1.525955E+00 +1.164802E-02 +4.244367E+00 +9.011441E-02 +tally 2: +3.727210E-01 +6.946049E-04 +8.048851E-01 +3.239200E-03 +3.094666E-01 +4.788479E-04 +2.194451E-01 +2.407807E-04 +1.708575E-01 +1.459614E-04 +2.305100E-02 +2.656744E-06 +8.000275E-03 +3.200220E-07 +1.273127E-03 +8.104266E-09 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.327638E-01 +5.536587E-04 +7.185980E-01 +2.581916E-03 +2.762905E-01 +3.816822E-04 +1.959196E-01 +1.919225E-04 +1.525408E-01 +1.163435E-04 +2.057984E-02 +2.117649E-06 +7.142612E-03 +2.550845E-07 +1.136643E-03 +6.459783E-09 +3.721891E-01 +6.926236E-04 +8.037363E-01 +3.229960E-03 +3.090249E-01 +4.774820E-04 +2.191319E-01 +2.400939E-04 +1.706136E-01 +1.455450E-04 +2.301810E-02 +2.649166E-06 +7.988856E-03 +3.191091E-07 +1.271310E-03 +8.081148E-09 diff --git a/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_2.dat b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_2.dat new file mode 100644 index 00000000000..97167c4acb6 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_2.dat @@ -0,0 +1,236 @@ +k-combined: +1.311387E+00 6.434473E-04 +tally 1: +6.841014E+01 +2.340271E+01 +2.539213E+01 +3.224892E+00 +6.179937E+01 +1.910227E+01 +4.345803E+01 +9.443470E+00 +6.461275E+00 +2.087633E-01 +1.572545E+01 +1.236585E+00 +2.958154E+01 +4.375368E+00 +9.580618E-01 +4.589475E-03 +2.331731E+00 +2.718521E-02 +3.771765E+01 +7.113193E+00 +1.255944E+00 +7.887240E-03 +3.056716E+00 +4.671914E-02 +9.755580E+01 +4.758576E+01 +1.145483E+00 +6.560687E-03 +2.787911E+00 +3.886241E-02 +2.112850E+02 +2.232146E+02 +3.224207E-01 +5.198176E-04 +7.978074E-01 +3.182742E-03 +1.123400E+02 +6.311292E+01 +1.523337E+00 +1.160873E-02 +4.237083E+00 +8.981046E-02 +1.130417E+02 +6.389290E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.351363E+01 +1.431867E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.116483E+01 +4.856257E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.109119E+01 +8.442515E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.834643E+01 +4.836027E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.891195E+02 +1.788369E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.689946E+01 +4.696163E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.902258E+01 +1.741956E+01 +2.183519E+01 +2.384450E+00 +5.314249E+01 +1.412401E+01 +4.083560E+01 +8.337984E+00 +6.069963E+00 +1.842369E-01 +1.477308E+01 +1.091305E+00 +2.914984E+01 +4.248591E+00 +9.449228E-01 +4.464449E-03 +2.299753E+00 +2.644464E-02 +3.706654E+01 +6.869716E+00 +1.234868E+00 +7.624744E-03 +3.005421E+00 +4.516427E-02 +9.764206E+01 +4.766994E+01 +1.147280E+00 +6.581283E-03 +2.792284E+00 +3.898441E-02 +2.155707E+02 +2.323596E+02 +3.288495E-01 +5.407431E-04 +8.137152E-01 +3.310864E-03 +1.141285E+02 +6.513725E+01 +1.543769E+00 +1.192058E-02 +4.293915E+00 +9.222311E-02 +6.827732E+01 +2.331168E+01 +2.541926E+01 +3.231710E+00 +6.186540E+01 +1.914265E+01 +4.341549E+01 +9.424964E+00 +6.474528E+00 +2.096188E-01 +1.575771E+01 +1.241652E+00 +2.957227E+01 +4.372632E+00 +9.605972E-01 +4.613802E-03 +2.337901E+00 +2.732931E-02 +3.770496E+01 +7.108413E+00 +1.259272E+00 +7.929092E-03 +3.064817E+00 +4.696704E-02 +9.756421E+01 +4.759397E+01 +1.148936E+00 +6.600293E-03 +2.796314E+00 +3.909702E-02 +2.114033E+02 +2.234634E+02 +3.234842E-01 +5.232469E-04 +8.004390E-01 +3.203738E-03 +1.123688E+02 +6.314309E+01 +1.527539E+00 +1.167220E-02 +4.248772E+00 +9.030152E-02 +tally 2: +3.727211E-01 +6.946052E-04 +8.048854E-01 +3.239203E-03 +3.094668E-01 +4.788484E-04 +2.194454E-01 +2.407815E-04 +1.708581E-01 +1.459624E-04 +2.305119E-02 +2.656788E-06 +8.000435E-03 +3.200348E-07 +1.273182E-03 +8.104961E-09 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.327638E-01 +5.536589E-04 +7.185983E-01 +2.581918E-03 +2.762906E-01 +3.816826E-04 +1.959200E-01 +1.919231E-04 +1.525414E-01 +1.163444E-04 +2.058001E-02 +2.117684E-06 +7.142755E-03 +2.550947E-07 +1.136691E-03 +6.460335E-09 +3.721891E-01 +6.926238E-04 +8.037366E-01 +3.229963E-03 +3.090251E-01 +4.774825E-04 +2.191322E-01 +2.400946E-04 +1.706142E-01 +1.455461E-04 +2.301829E-02 +2.649209E-06 +7.989016E-03 +3.191219E-07 +1.271365E-03 +8.081841E-09 diff --git a/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_3.dat b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_3.dat new file mode 100644 index 00000000000..7d455f49fdc --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_3.dat @@ -0,0 +1,236 @@ +k-combined: +1.311387E+00 6.434473E-04 +tally 1: +6.846326E+01 +2.343906E+01 +2.541206E+01 +3.229957E+00 +6.184788E+01 +1.913227E+01 +4.349768E+01 +9.460710E+00 +6.467182E+00 +2.091452E-01 +1.573983E+01 +1.238847E+00 +2.961216E+01 +4.384431E+00 +9.590541E-01 +4.598987E-03 +2.334146E+00 +2.724155E-02 +3.775680E+01 +7.127966E+00 +1.257248E+00 +7.903631E-03 +3.059891E+00 +4.681623E-02 +9.766141E+01 +4.768885E+01 +1.146723E+00 +6.574901E-03 +2.790929E+00 +3.894660E-02 +2.115200E+02 +2.237114E+02 +3.227776E-01 +5.209693E-04 +7.986907E-01 +3.189793E-03 +1.124584E+02 +6.324599E+01 +1.524929E+00 +1.163300E-02 +4.241512E+00 +8.999826E-02 +1.131374E+02 +6.400109E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.356368E+01 +1.434547E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.119750E+01 +4.866444E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.113478E+01 +8.460437E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.845378E+01 +4.846591E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.893306E+02 +1.792363E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.700245E+01 +4.706152E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.906729E+01 +1.744596E+01 +2.185192E+01 +2.388105E+00 +5.318321E+01 +1.414566E+01 +4.087282E+01 +8.353191E+00 +6.075510E+00 +1.845738E-01 +1.478658E+01 +1.093301E+00 +2.917992E+01 +4.257364E+00 +9.458987E-01 +4.473675E-03 +2.302128E+00 +2.649928E-02 +3.710477E+01 +6.883893E+00 +1.236143E+00 +7.640499E-03 +3.008525E+00 +4.525760E-02 +9.774743E+01 +4.777288E+01 +1.148518E+00 +6.595498E-03 +2.795298E+00 +3.906861E-02 +2.158102E+02 +2.328763E+02 +3.292132E-01 +5.419397E-04 +8.146150E-01 +3.318191E-03 +1.142486E+02 +6.527446E+01 +1.545380E+00 +1.194548E-02 +4.298397E+00 +9.241572E-02 +6.833030E+01 +2.334787E+01 +2.543920E+01 +3.236782E+00 +6.191392E+01 +1.917269E+01 +4.345509E+01 +9.442165E+00 +6.480446E+00 +2.100022E-01 +1.577211E+01 +1.243923E+00 +2.960287E+01 +4.381688E+00 +9.615920E-01 +4.623363E-03 +2.340322E+00 +2.738594E-02 +3.774408E+01 +7.123173E+00 +1.260580E+00 +7.945567E-03 +3.067999E+00 +4.706463E-02 +9.766982E+01 +4.769706E+01 +1.150180E+00 +6.614591E-03 +2.799341E+00 +3.918171E-02 +2.116384E+02 +2.239608E+02 +3.238424E-01 +5.244063E-04 +8.013253E-01 +3.210837E-03 +1.124872E+02 +6.327624E+01 +1.529136E+00 +1.169661E-02 +4.253213E+00 +9.049039E-02 +tally 2: +3.727212E-01 +6.946055E-04 +8.048859E-01 +3.239206E-03 +3.094670E-01 +4.788493E-04 +2.194460E-01 +2.407828E-04 +1.708591E-01 +1.459642E-04 +2.305151E-02 +2.656862E-06 +8.000705E-03 +3.200564E-07 +1.273273E-03 +8.106120E-09 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.327639E-01 +5.536592E-04 +7.185987E-01 +2.581921E-03 +2.762909E-01 +3.816833E-04 +1.959205E-01 +1.919242E-04 +1.525423E-01 +1.163458E-04 +2.058029E-02 +2.117743E-06 +7.142994E-03 +2.551118E-07 +1.136772E-03 +6.461254E-09 +3.721892E-01 +6.926241E-04 +8.037371E-01 +3.229967E-03 +3.090254E-01 +4.774834E-04 +2.191328E-01 +2.400960E-04 +1.706153E-01 +1.455479E-04 +2.301861E-02 +2.649283E-06 +7.989286E-03 +3.191434E-07 +1.271456E-03 +8.082996E-09 diff --git a/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_4.dat b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_4.dat new file mode 100644 index 00000000000..6114b3cd064 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_4.dat @@ -0,0 +1,236 @@ +k-combined: +1.311387E+00 6.434473E-04 +tally 1: +6.851773E+01 +2.347638E+01 +2.543250E+01 +3.235154E+00 +6.189761E+01 +1.916305E+01 +4.353818E+01 +9.478338E+00 +6.473217E+00 +2.095358E-01 +1.575452E+01 +1.241161E+00 +2.964337E+01 +4.393677E+00 +9.600653E-01 +4.608690E-03 +2.336607E+00 +2.729903E-02 +3.779670E+01 +7.143037E+00 +1.258578E+00 +7.920353E-03 +3.063126E+00 +4.691528E-02 +9.776897E+01 +4.779395E+01 +1.147986E+00 +6.589392E-03 +2.794003E+00 +3.903244E-02 +2.117592E+02 +2.242177E+02 +3.231411E-01 +5.221432E-04 +7.995900E-01 +3.196980E-03 +1.125791E+02 +6.338177E+01 +1.526551E+00 +1.165777E-02 +4.246026E+00 +9.018990E-02 +1.132353E+02 +6.411189E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.361478E+01 +1.437285E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.123078E+01 +4.876834E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.117919E+01 +8.478714E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.856309E+01 +4.857359E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.895455E+02 +1.796434E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.710741E+01 +4.716341E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.911317E+01 +1.747308E+01 +2.186909E+01 +2.391858E+00 +5.322498E+01 +1.416789E+01 +4.091085E+01 +8.368741E+00 +6.081177E+00 +1.849182E-01 +1.480037E+01 +1.095342E+00 +2.921058E+01 +4.266313E+00 +9.468932E-01 +4.483087E-03 +2.304548E+00 +2.655503E-02 +3.714373E+01 +6.898356E+00 +1.237443E+00 +7.656573E-03 +3.011688E+00 +4.535281E-02 +9.785474E+01 +4.787783E+01 +1.149780E+00 +6.609991E-03 +2.798368E+00 +3.915446E-02 +2.160541E+02 +2.334029E+02 +3.295834E-01 +5.431594E-04 +8.155311E-01 +3.325659E-03 +1.143711E+02 +6.541445E+01 +1.547023E+00 +1.197088E-02 +4.302966E+00 +9.261227E-02 +6.838461E+01 +2.338501E+01 +2.545964E+01 +3.241986E+00 +6.196367E+01 +1.920352E+01 +4.349554E+01 +9.459752E+00 +6.486491E+00 +2.103942E-01 +1.578682E+01 +1.246245E+00 +2.963407E+01 +4.390927E+00 +9.626057E-01 +4.633116E-03 +2.342790E+00 +2.744371E-02 +3.778395E+01 +7.138229E+00 +1.261912E+00 +7.962373E-03 +3.071242E+00 +4.716418E-02 +9.777738E+01 +4.780217E+01 +1.151446E+00 +6.629169E-03 +2.802424E+00 +3.926806E-02 +2.118778E+02 +2.244678E+02 +3.242071E-01 +5.255880E-04 +8.022277E-01 +3.218073E-03 +1.126079E+02 +6.341209E+01 +1.530763E+00 +1.172153E-02 +4.257740E+00 +9.068312E-02 +tally 2: +3.727213E-01 +6.946060E-04 +8.048865E-01 +3.239212E-03 +3.094674E-01 +4.788505E-04 +2.194469E-01 +2.407847E-04 +1.708606E-01 +1.459668E-04 +2.305197E-02 +2.656966E-06 +8.001085E-03 +3.200868E-07 +1.273400E-03 +8.107739E-09 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.327641E-01 +5.536596E-04 +7.185993E-01 +2.581925E-03 +2.762912E-01 +3.816842E-04 +1.959213E-01 +1.919257E-04 +1.525436E-01 +1.163478E-04 +2.058070E-02 +2.117825E-06 +7.143331E-03 +2.551359E-07 +1.136885E-03 +6.462537E-09 +3.721894E-01 +6.926247E-04 +8.037378E-01 +3.229972E-03 +3.090257E-01 +4.774846E-04 +2.191337E-01 +2.400979E-04 +1.706168E-01 +1.455504E-04 +2.301907E-02 +2.649387E-06 +7.989665E-03 +3.191737E-07 +1.271582E-03 +8.084610E-09 diff --git a/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_5.dat b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_5.dat new file mode 100644 index 00000000000..a30dfa88059 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/propagation/results_true_5.dat @@ -0,0 +1,236 @@ +k-combined: +1.311387E+00 6.434473E-04 +tally 1: +6.857342E+01 +2.351456E+01 +2.545339E+01 +3.240472E+00 +6.194846E+01 +1.919455E+01 +4.357948E+01 +9.496329E+00 +6.479370E+00 +2.099343E-01 +1.576949E+01 +1.243521E+00 +2.967512E+01 +4.403095E+00 +9.610942E-01 +4.618574E-03 +2.339111E+00 +2.735758E-02 +3.783729E+01 +7.158388E+00 +1.259930E+00 +7.937385E-03 +3.066418E+00 +4.701617E-02 +9.787835E+01 +4.790095E+01 +1.149271E+00 +6.604145E-03 +2.797129E+00 +3.911983E-02 +2.120024E+02 +2.247331E+02 +3.235106E-01 +5.233379E-04 +8.005043E-01 +3.204296E-03 +1.127018E+02 +6.352005E+01 +1.528203E+00 +1.168300E-02 +4.250618E+00 +9.038510E-02 +1.133352E+02 +6.422512E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.366687E+01 +1.440079E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.126465E+01 +4.887416E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.122436E+01 +8.497326E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.867425E+01 +4.868321E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.897639E+02 +1.800576E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.721418E+01 +4.726718E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.916011E+01 +1.750084E+01 +2.188664E+01 +2.395700E+00 +5.326771E+01 +1.419065E+01 +4.094962E+01 +8.384611E+00 +6.086955E+00 +1.852698E-01 +1.481443E+01 +1.097424E+00 +2.924177E+01 +4.275430E+00 +9.479052E-01 +4.492675E-03 +2.307011E+00 +2.661183E-02 +3.718338E+01 +6.913090E+00 +1.238765E+00 +7.672947E-03 +3.014906E+00 +4.544980E-02 +9.796388E+01 +4.798469E+01 +1.151062E+00 +6.624747E-03 +2.801490E+00 +3.924187E-02 +2.163020E+02 +2.339388E+02 +3.299598E-01 +5.444008E-04 +8.164626E-01 +3.333259E-03 +1.144957E+02 +6.555703E+01 +1.548694E+00 +1.199676E-02 +4.307614E+00 +9.281247E-02 +6.844016E+01 +2.342301E+01 +2.548054E+01 +3.247311E+00 +6.201454E+01 +1.923506E+01 +4.353678E+01 +9.477701E+00 +6.492655E+00 +2.107942E-01 +1.580182E+01 +1.248615E+00 +2.966581E+01 +4.400337E+00 +9.636372E-01 +4.643051E-03 +2.345300E+00 +2.750256E-02 +3.782452E+01 +7.153566E+00 +1.263268E+00 +7.979492E-03 +3.074542E+00 +4.726558E-02 +9.788675E+01 +4.790918E+01 +1.152734E+00 +6.644009E-03 +2.805559E+00 +3.935597E-02 +2.121212E+02 +2.249837E+02 +3.245778E-01 +5.267908E-04 +8.031451E-01 +3.225437E-03 +1.127307E+02 +6.355044E+01 +1.532419E+00 +1.174690E-02 +4.262347E+00 +9.087943E-02 +tally 2: +3.727215E-01 +6.946067E-04 +8.048874E-01 +3.239219E-03 +3.094679E-01 +4.788521E-04 +2.194480E-01 +2.407872E-04 +1.708625E-01 +1.459701E-04 +2.305256E-02 +2.657102E-06 +8.001574E-03 +3.201259E-07 +1.273562E-03 +8.109804E-09 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.327642E-01 +5.536601E-04 +7.186001E-01 +2.581931E-03 +2.762917E-01 +3.816855E-04 +1.959223E-01 +1.919277E-04 +1.525453E-01 +1.163504E-04 +2.058122E-02 +2.117933E-06 +7.143765E-03 +2.551669E-07 +1.137029E-03 +6.464173E-09 +3.721896E-01 +6.926253E-04 +8.037386E-01 +3.229979E-03 +3.090263E-01 +4.774861E-04 +2.191348E-01 +2.401004E-04 +1.706187E-01 +1.455537E-04 +2.301965E-02 +2.649522E-06 +7.990153E-03 +3.192127E-07 +1.271744E-03 +8.086669E-09 diff --git a/tests/regression_tests/random_ray_k_eff_mesh_kinetic/test.py b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/test.py new file mode 100644 index 00000000000..d0b8ff53ad4 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh_kinetic/test.py @@ -0,0 +1,45 @@ +import os + +import openmc +from openmc.utility_funcs import change_directory +from openmc.examples import random_ray_lattice +import pytest + +from tests.testing_harness import KineticTolerantPyAPITestHarness + + +class KineticMGXSTestHarness(KineticTolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +@pytest.mark.parametrize("time_method", ["isotropic", + "propagation"]) +def test_random_ray_k_eff_mesh_kinetic(time_method): + with change_directory(time_method): + model = random_ray_lattice(kinetic=True) + model.settings.timestep_parameters['n_timesteps'] = 5 + model.settings.random_ray['time_method'] = time_method + model.settings.batches = 400 + model.settings.inactive = 200 + + # The model already has some geometrical subdivisions + # up to a 10x10 grid in the moderator region. So, we + # increase the resolution 40x40 applied over the full + # 2x2 lattice. + pitch = 1.26 + dim = 40 + mesh = openmc.RegularMesh() + mesh.dimension = (dim, dim) + mesh.lower_left = (-pitch, -pitch) + mesh.upper_right = (pitch, pitch) + + root = model.geometry.root_universe + + model.settings.random_ray['source_region_meshes'] = [(mesh, [root])] + + harness = KineticMGXSTestHarness('statepoint.400', 6, model) + harness.main() diff --git a/tests/testing_harness.py b/tests/testing_harness.py index 1ad91b7a89f..0adcc6c2825 100644 --- a/tests/testing_harness.py +++ b/tests/testing_harness.py @@ -87,6 +87,11 @@ def _get_results(self, hash_output=False): """Digest info in the statepoint and return as a string.""" # Read the statepoint file. statepoint = glob.glob(self._sp_name)[0] + return self._write_tallies(statepoint, hash_output) + + def _write_tallies(self, statepoint, hash_output): + """Get tally sum and sum_sq data from statepoint file + as a string""" with openmc.StatePoint(statepoint) as sp: outstr = '' if sp.run_mode == 'eigenvalue': @@ -285,6 +290,110 @@ def _cleanup(self): os.remove(f) +class KineticTestHarness(TestHarness): + """General class for running OpenMC regression tests for kinetic simulations.""" + + def __init__(self, statepoint_base, n_timesteps): + self._sp_base = statepoint_base + self._n_timesteps = n_timesteps + + def main(self): + """Accept commandline arguments and either run or update tests.""" + if config['update']: + self.update_results() + else: + self.execute_test() + + def execute_test(self): + """Run kinetic OpenMC test with the appropriate arguments and check the outputs.""" + try: + self._run_openmc() + self._harness_timestep_loop("test") + finally: + self._cleanup() + + def update_results(self): + """Update the results_true for each timestep using the current version of OpenMC.""" + try: + self._run_openmc() + self._harness_timestep_loop("update") + finally: + self._cleanup() + + def _harness_timestep_loop(self, loop_type): + """Test the results of each timestep""" + statepoint = glob.glob(self._sp_base + "*") + assert len(statepoint) == self._n_timesteps, f"Found {len(statepoint)} statepoint files exist" \ + f" but expected {self._n_timesteps}." + for i in range(self._n_timesteps): + self._test_output_created(i) + results = self._get_results(i) + self._write_results(results, i) + if loop_type == "test": + self._compare_results(i) + elif loop_type == "update": + self._overwrite_results(i) + else: + raise ValueError( + f"Invalid loop_type ({loop_type}) passed to _harness_timestep_loop") + + def _test_output_created(self, index): + """Make sure statepoint.* and tallies.out have been created for the current timestep.""" + statepoint = glob.glob(self._sp_base + f"_{index}*") + assert statepoint[0].endswith('h5'), \ + f"Statepoint file {statepoint[0]} is not a HDF5 file." + if os.path.exists('tallies.xml'): + assert os.path.exists(f"tallies_{index}.out"), \ + f"Tally output file tallies_{index}.out does not exist." + + def _get_results(self, index, hash_output=False): + """Digest info in the statepoints and return as a string.""" + # Read the statepoint files. + statepoint = glob.glob(self._sp_base + f"_{index}*")[0] + return self._write_tallies(statepoint, hash_output) + + @property + def statepoint_name(self, i): + return self._sp_base + f"_{i}.h5" + + def _write_results(self, results_string, index): + """Write the results to an ASCII file.""" + with open(f'results_test_{index}.dat', 'w') as fh: + fh.write(results_string) + + def _overwrite_results(self, index): + """Overwrite the results_true with the results_test.""" + shutil.copyfile(f'results_test_{index}.dat', + f'results_true_{index}.dat') + + def _compare_results(self, index): + """Make sure the current results agree with the reference.""" + compare = filecmp.cmp( + f'results_test_{index}.dat', 'results_true_{index}.dat') + if not compare: + expected = open(f'results_true_{index}.dat').readlines() + actual = open(f'results_test_{index}.dat').readlines() + diff = unified_diff(expected, actual, f'results_true_{index}.dat', + f'results_test_{index}.dat') + print(f'Timestep {index} result differences:') + print(''.join(colorize(diff))) + os.rename(f'results_test_{index}.dat', + f'results_error_{index}.dat') + assert compare, 'Results do not agree' + + def _cleanup(self): + """Delete statepoints, tally, and test files.""" + output = glob.glob('statepoint.*.h5') + output += ['summary.h5', 'tallies.out'] + output += glob.glob('tallies_*.out') + output += glob.glob('results_test_*.dat') + output += glob.glob('volume_*.h5') + output += glob.glob(f'{self._sp_base}_*.h5') + for f in output: + if os.path.exists(f): + os.remove(f) + + class PyAPITestHarness(TestHarness): def __init__(self, statepoint_name, model=None, inputs_true=None): super().__init__(statepoint_name) @@ -487,6 +596,74 @@ def _cleanup(self): os.remove(f) +class KineticPyAPITestHarness(KineticTestHarness, PyAPITestHarness): + def __init__(self, statepoint_base, n_timesteps, model, inputs_true=None): + super().__init__(statepoint_base, n_timesteps) + self._model = model + self._model.plots = [] + + self.inputs_true = "inputs_true.dat" if not inputs_true else inputs_true + + def execute_test(self): + """Build input XMLs, run OpenMC, and verify correct results.""" + try: + self._build_inputs() + inputs = self._get_inputs() + self._write_inputs(inputs) + self._compare_inputs() + self._run_openmc() + self._harness_timestep_loop("test") + finally: + self._cleanup() + + def update_results(self): + """Update results_true.dat and inputs_true.dat""" + try: + self._build_inputs() + inputs = self._get_inputs() + self._write_inputs(inputs) + self._overwrite_inputs() + self._run_openmc() + self._harness_timestep_loop("update") + finally: + self._cleanup() + + def _cleanup(self): + """Delete XMLs, statepoints, tally, and test files.""" + super()._cleanup() + output = ['materials.xml', 'geometry.xml', 'settings.xml', + 'tallies.xml', 'plots.xml', 'inputs_test.dat', 'model.xml'] + for f in output: + if os.path.exists(f): + os.remove(f) + + +class KineticTolerantPyAPITestHarness(KineticPyAPITestHarness, TolerantPyAPITestHarness): + """Specialized harness for running kinetic simulation tests in that involve + significant levels of floating point non-associativity when using shared + memory parallelism due to single precision usage (e.g., as in the random + ray solver). + + """ + + def _compare_results(self, i): + """Make sure the current results agree with the reference.""" + self._compare_files( + f'results_test_{i}.dat', f'results_true_{i}.dat', 1e-6, i) + + def _compare_files(self, file_test, file_true, tol, index): + compare = self._are_files_equal(file_test, file_true, 1e-6) + if not compare: + expected = open(file_true).readlines() + actual = open(file_test).readlines() + diff = unified_diff(expected, actual, file_true, + file_test) + print('Result differences:') + print(''.join(colorize(diff))) + os.rename(file_test, f'results_error_{index}.dat') + assert compare, 'Results do not agree' + + class PlotTestHarness(TestHarness): """Specialized TestHarness for running OpenMC plotting tests.""" diff --git a/tests/unit_tests/dagmc/test_convert_to_multigroup.py b/tests/unit_tests/dagmc/test_convert_to_multigroup.py index 069dc658e52..d76c4eff215 100644 --- a/tests/unit_tests/dagmc/test_convert_to_multigroup.py +++ b/tests/unit_tests/dagmc/test_convert_to_multigroup.py @@ -16,7 +16,7 @@ def test_convert_to_multigroup_without_particles_batches(run_in_tmpdir): """Test that convert_to_multigroup works with DAGMC model without setting particles/batches beforehand.""" openmc.reset_auto_ids() - + mat = openmc.Material(name="mat") mat.add_nuclide("Fe56", 1.0) mat.set_density("g/cm3", 7.0) @@ -33,7 +33,7 @@ def test_convert_to_multigroup_without_particles_batches(run_in_tmpdir): model.settings = openmc.Settings() # Note: no particles or batches set! model.settings.run_mode = 'fixed source' - + # Create a point source my_source = openmc.IndependentSource() my_source.space = openmc.stats.Point((0.25, 0.25, 0.25)) @@ -44,7 +44,7 @@ def test_convert_to_multigroup_without_particles_batches(run_in_tmpdir): # convert_to_multigroup handles initialization internally using non-transport mode model.convert_to_multigroup( method='material_wise', - groups='CASMO-2', + energy_groups='CASMO-2', nparticles=10, overwrite_mgxs_library=True ) diff --git a/tests/unit_tests/test_material.py b/tests/unit_tests/test_material.py index 911b8867f96..1dc1ea010f5 100644 --- a/tests/unit_tests/test_material.py +++ b/tests/unit_tests/test_material.py @@ -259,6 +259,8 @@ def test_density(): m.set_density(unit, 1.0) with pytest.raises(ValueError): m.set_density('g/litre', 1.0) + with pytest.raises(ValueError): + m.set_density('sum', 1.0, [1,1,1]) def test_salphabeta(): @@ -506,14 +508,18 @@ def test_from_xml(run_in_tmpdir): m2.set_density('kg/m3', 10.0) m3 = openmc.Material(3) m3.add_nuclide('N14', 0.02) + m4 = openmc.Material(4) + m4.add_nuclide('C12', 1.0) + m4.set_density('g/cc', 12, [12, 14, 14, 14, 12]) + - mats = openmc.Materials([m1, m2, m3]) + mats = openmc.Materials([m1, m2, m3, m4]) mats.cross_sections = 'fake_path.xml' mats.export_to_xml() # Regenerate materials from XML mats = openmc.Materials.from_xml() - assert len(mats) == 3 + assert len(mats) == 4 m1 = mats[0] assert m1.id == 1 assert m1.name == 'water' @@ -526,6 +532,7 @@ def test_from_xml(run_in_tmpdir): assert m2.density == 10.0 assert m2.density_units == 'kg/m3' assert mats[2].density_units == 'sum' + assert np.all(m4.density_timeseries == [12, 14, 14, 14, 12]) def test_mix_materials(): diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index bdb3ea8fe9f..2b81c3f0d03 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -7,8 +7,8 @@ import openmc.lib import openmc.stats - -def test_export_to_xml(run_in_tmpdir): +@pytest.mark.parametrize("kinetic_simulation", [True, False]) +def test_export_to_xml(run_in_tmpdir, kinetic_simulation): tmp_properties_file = 'properties_test.h5' @@ -19,6 +19,13 @@ def test_export_to_xml(run_in_tmpdir): s.max_lost_particles = 5 s.rel_max_lost_particles = 1e-4 s.keff_trigger = {'type': 'std_dev', 'threshold': 0.001} + s.kinetic_simulation = kinetic_simulation + if kinetic_simulation: + s.timestep_parameters = { + 'dt': 0.1, + 'n_timesteps': 41, + 'timestep_units': 's', + } s.energy_mode = 'continuous-energy' s.max_order = 5 s.max_tracks = 1234 @@ -90,6 +97,9 @@ def test_export_to_xml(run_in_tmpdir): 'adjoint': False, 'sample_method': 'halton' } + if kinetic_simulation: + s.random_ray['bd_order'] = 3 + s.random_ray['time_derivative_method'] = 'propagation' s.max_particle_events = 100 s.max_secondaries = 1_000_000 s.source_rejection_fraction = 0.01 @@ -109,6 +119,11 @@ def test_export_to_xml(run_in_tmpdir): assert s.max_lost_particles == 5 assert s.rel_max_lost_particles == 1e-4 assert s.keff_trigger == {'type': 'std_dev', 'threshold': 0.001} + assert s.kinetic_simulation == kinetic_simulation + if kinetic_simulation: + assert s.timestep_parameters['dt'] == 0.1 + assert s.timestep_parameters['n_timesteps'] == 41 + assert s.timestep_parameters['timestep_units'] == 's' assert s.energy_mode == 'continuous-energy' assert s.max_order == 5 assert s.max_tracks == 1234 @@ -187,6 +202,9 @@ def test_export_to_xml(run_in_tmpdir): assert s.random_ray['volume_normalized_flux_tallies'] assert not s.random_ray['adjoint'] assert s.random_ray['sample_method'] == 'halton' + if kinetic_simulation: + assert s.random_ray['bd_order'] == 3 + assert s.random_ray['time_derivative_method'] == 'propagation' assert s.max_secondaries == 1_000_000 assert s.source_rejection_fraction == 0.01 assert s.free_gas_threshold == 800.0 diff --git a/tests/unit_tests/weightwindows/test_ww_gen.py b/tests/unit_tests/weightwindows/test_ww_gen.py index a4456e6809a..928596832c0 100644 --- a/tests/unit_tests/weightwindows/test_ww_gen.py +++ b/tests/unit_tests/weightwindows/test_ww_gen.py @@ -367,7 +367,7 @@ def test_ww_generation_with_dagmc(run_in_tmpdir): method="stochastic_slab", overwrite_mgxs_library=True, nparticles=10, - groups="CASMO-2" + energy_groups="CASMO-2" ) rr_model.convert_to_random_ray()