Skip to content

Commit

Permalink
Refactor neutronics pka generators to include averaged number densiti…
Browse files Browse the repository at this point in the history
…es as input. Add documentation (idaholab#302)
  • Loading branch information
Sebastian Schunert committed Jun 5, 2018
1 parent 6da5ec7 commit b4e8478
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 98 deletions.
8 changes: 8 additions & 0 deletions doc/content/bib/magpie.bib
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

@inproceedings{SchunertTREATHeatSource,
author={S. Schunert and others},
title={{Heat Source Characterization In A TREAT Fuel Particle Using Coupled Neutronics Binary Collision Monte-Carlo Calculations}},
booktitle={{M\&C 2017 International Conference on Mathematics \& Computational Methods Applied to Nuclear Science \& Engineering}},
month={April},
year={2017}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
<!-- MOOSE Documentation Stub: Remove this when content is added. -->

# PKAFissionFragmentNeutronics

!alert construction title=Undocumented Class
The PKAFissionFragmentNeutronics has not been documented, if you would like to contribute to MOOSE by
writing documentation, please see [/generate.md]. The content contained on this page explains
the typical documentation associated with a MooseObject; however, what is contained is ultimately
determined by what is necessary to make the documentation clear for users.

!syntax description /UserObjects/PKAFissionFragmentNeutronics
`PKAFissionFragmentNeutronics` generates primary knock-on atoms (PKA) originating
from fission reactions. The difference to PKAFissionFragmentEmpirical is that
it uses isotopic fission rates computed by neutronics calculations and ENDF data
for sampling fission product species.

!syntax parameters /UserObjects/PKAFissionFragmentNeutronics
The partial fission rate for isotope $$i$$ is denoted by $$F_i(\vec{r}, \vec{rho})$$.
It is computed by:
$$$ F_i(\vec{r}, \vec{rho}) = \sum\limits_{g=1}^G N_i(\vec{r},\vec{rho})\sigma_{f,g,i} \phi_g(\vec{r}),$$$
where $$\vec{r}$$ is the location in the macroscopic, neutronics domain and $$\vec{\rho}$$
is the location in the microscopic domain. These two locations are separated because their
scale is significantly separated by 3-4 orders of magnitude. Changes with $$\vec{r}$$ are
understood to be smooth changes of the average composition of the microscopic domain (orders of centimeters), while changes with $$\vec{\rho}$$ captures compositional changes between grains on the microscopic domain, i.e. on the orders of micro-meters. The notation of separating the spatial dependence into two scales follows homogenization theory. $$N_i$$ is the number density of isotope $$i$$, $$\sigma_{f,g,i}$$ is the microscopic fission cross section of isotope $$i$$ in group $$g$$, and $$\phi_g$$ is the scalar flux in group $$g$$.

!syntax inputs /UserObjects/PKAFissionFragmentNeutronics
Denoting the microscopic domain as $$\Omega_m$$ located at $$\vec{r}_m$$, we can define a slowly varying average of the number densities:
$$$ N_i(\vec{r}) = \frac{1}{\Omega_m} \int_{\Omega_m} N_i(\vec{r},\vec{rho}) d\vec{\rho}. $$$
$$N_i(\vec{r})$$ is the number density provided to neutronics calculations. The nuclide fission rates computed by the neutronics calculation is consequently the slowly varying average
given by:
$$$ F_i (\vec{r}) = \sum\limits_{g=1}^G N_i(\vec{r})\sigma_{f,g,i}.$$$
The `PKAFissionFragmentNeutronics` accepts the values of $F_i (\vec{r}_m)$ as the `partial_reaction_rates` parameter. In addition, it accepts the values of $$N_i(\vec{r}_m)$$
as the `averaged_number_densities` parameter.

!syntax children /UserObjects/PKAFissionFragmentNeutronics
The fission rate density at a location in the microscopic domain is computed by:
$$$ F_i (\vec{r}_m, \vec{\rho}) = \frac{N_i(\vec{r}_m, \vec{\rho})}{N_i(\vec{r}_m)} F_i(\vec{r}_m), $$$
where $$ N_i(\vec{r}_m, \vec{\rho}) $$ are the number densities provided by the rasterizer.

!bibtex bibliography
The expected number of fissions in a mesh element with index $j$ and volume $$V_j$$ is given by:
$$$ C_i = \Delta t \int_{V_j} F_i (\vec{r}_m, \vec{\rho}) d\vec{\rho}. $$$
Non-integer results are rounded up with a probability of the $$ C_i - \text{int}(C_i) $$; otherwise
rounded down. Two PKAs are created from each fission event. The algorithm for sampling their type, energy, and direction of motion is described in [cite:SchunertTREATHeatSource].
1 change: 0 additions & 1 deletion doc/hidden.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
- /UserObjects/NeutronicsSpectrumSamplerFission
- /UserObjects/PKAConstant
- /UserObjects/PKAFissionFragmentEmpirical
- /UserObjects/PKAFissionFragmentNeutronics
- /UserObjects/PKAFixedPointGenerator
- /UserObjects/PKAFunction
- /UserObjects/PKAGeneratorAlphaDecay
Expand Down
11 changes: 0 additions & 11 deletions include/userobjects/PKAFissionFragmentNeutronics.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,6 @@ class PKAFissionFragmentNeutronics : public PKAGeneratorNeutronicsBase
* will be sampled to determine the initial state of the ions passed to MyTRIM
*/
DiscreteFissionPKAPDF _pdf;

/**
* _partial_fission_rates: the fission rate per nuclide is:
* f_i = N_i * (sum_g sigma_{f,g,i} phi_g),
* where i is the nuclide ID and g is the energy group.
* N_i is provided as variable in the Rasterizer, but the (sum_g sigma_{f,g,i} phi_g) must
* be provided as partial fission rate here
* Note: the fission rate densities are in units of [ fissions / ( mesh-length-unit^3 sec ) ]
*/
std::vector<const Real *> _partial_fission_rates;
std::vector<Real> _stored_pps;
};

#endif //PKAFISSIONFRAGMENTNEUTRONICS_H
14 changes: 14 additions & 0 deletions include/userobjects/PKAGeneratorNeutronicsBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ class PKAGeneratorNeutronicsBase : public PKAGeneratorBase

/// helper function to set pdf based on neutronics information from Mammoth
virtual void setPDF(const std::vector<unsigned int> & ZAID, const std::vector<Real> & energies, const MultiIndex<Real> & probabilities) = 0;

protected:
/**
* The partial reaction rates are reaction rates of a certain type from the neutronics calculation
* for a single nculide, e.g. fission rate of U-235. NOTE: it multiplies the smoothly varying _average_
* of the U-235 number density already
*
* In Magpie, the rate of PKA creation is obtained by multiplying with the _local_ number density. To obtain a
* proper PKA creation rate, we need to divide by the smooth average. This is what _averaged_number_densities is for.
*/
std::vector<const Real *> _partial_neutronics_reaction_rates;
std::vector<Real> _stored_reaction_rates;
std::vector<const Real *> _averaged_number_densities;
std::vector<Real> _stored_densities;
};

#endif // PKAGENERATORNEUTRONICSBASE_H
7 changes: 0 additions & 7 deletions include/userobjects/PKAGeneratorRecoil.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,6 @@ class PKAGeneratorRecoil : public PKAGeneratorNeutronicsBase
* are sampled
*/
DiscretePKAPDF _pdf;

/**
* The partial reaction rates [# reactions / time / volume] of the particular
* recoil reaction for each nuclide
*/
std::vector<const Real *> _partial_recoil_rates;
std::vector<Real> _stored_pps;
};

#endif //PKAGeneratorRecoil_H
38 changes: 6 additions & 32 deletions src/userobjects/PKAFissionFragmentNeutronics.C
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,13 @@ template<>
InputParameters validParams<PKAFissionFragmentNeutronics>()
{
InputParameters params = validParams<PKAGeneratorNeutronicsBase>();
params.addParam<std::vector<PostprocessorName>>("partial_fission_rates", "Partial fission rates per unit volume [sum_g sigma_{f,g,i} * phi_g]. "
"Provide number density as variable in rasterizer!");
params.addClassDescription("PKA generator (fission) user object.\n Takes pdf and samples PKAs due to fission.");
return params;
}

PKAFissionFragmentNeutronics::PKAFissionFragmentNeutronics(const InputParameters & parameters):
PKAGeneratorNeutronicsBase(parameters)
{
if (isParamValid("partial_fission_rates"))
{
std::vector<PostprocessorName> names = getParam<std::vector<PostprocessorName>>("partial_fission_rates");
_partial_fission_rates.resize(names.size());
_stored_pps.resize(names.size());
for (unsigned int j = 0; j < names.size(); ++j)
if (_fe_problem.hasPostprocessor(names[j]))
_partial_fission_rates[j] = &getPostprocessorValueByName(names[j]);
else
{
Real real_value = -std::numeric_limits<Real>::max();
std::istringstream ss(names[j]);

if (ss >> real_value && ss.eof())
_stored_pps[j] = real_value;
else
mooseError("Illegal entry in partial_fission_rates: ", names[j]);

_partial_fission_rates[j] = &_stored_pps[j];
}
}
else
{
_stored_pps = {1.0e-8};
_partial_fission_rates = {&_stored_pps[0]};
}
}

void
Expand All @@ -66,12 +38,14 @@ PKAFissionFragmentNeutronics::appendPKAs(std::vector<MyTRIM_NS::IonBase> & ion_l
mooseAssert(dt >= 0, "Passed a negative time window into PKAFissionFragmentNeutronics::appendPKAs");
mooseAssert(vol >= 0, "Passed a negative volume into PKAFissionFragmentNeutronics::appendPKAs");

if (averaged_data._elements.size() != _partial_fission_rates.size())
mooseError("Size of averaged_data and partial_fission_rates must be equal");
if (averaged_data._elements.size() != _partial_neutronics_reaction_rates.size())
mooseError("Size of averaged_data and partial_reaction_rates must be equal");

for (unsigned int nuclide = 0; nuclide < _partial_fission_rates.size(); ++nuclide)
for (unsigned int nuclide = 0; nuclide < _partial_neutronics_reaction_rates.size(); ++nuclide)
{
unsigned int num_fission = std::floor(recoil_rate_scaling * dt * vol * (*_partial_fission_rates[nuclide]) * averaged_data._elements[nuclide] + getRandomReal());
unsigned int num_fission = std::floor(recoil_rate_scaling * dt * vol *
(*_partial_neutronics_reaction_rates[nuclide]) / (*_averaged_number_densities[nuclide])
* averaged_data._elements[nuclide] + getRandomReal());

for (unsigned i = 0; i < num_fission; ++i)
{
Expand Down
64 changes: 64 additions & 0 deletions src/userobjects/PKAGeneratorNeutronicsBase.C
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,75 @@ template<>
InputParameters validParams<PKAGeneratorNeutronicsBase>()
{
InputParameters params = validParams<PKAGeneratorBase>();
params.addParam<std::vector<PostprocessorName>>("partial_reaction_rates", "Partial neutronic reaction rates per unit volume [sum_g xs_{r,g,i} * phi_g], "
"r: reaction type, g: energy group, i: nuclide id.Provide number density as variable in rasterizer!");
params.addParam<std::vector<PostprocessorName>>("averaged_number_densities", "The number density of the species averaged over the domain.");
params.addClassDescription("PKA generator (neutronics) user object base class.\n Takes pdf and samples PKAs due to various interactions.");
return params;
}

PKAGeneratorNeutronicsBase::PKAGeneratorNeutronicsBase(const InputParameters & parameters) :
PKAGeneratorBase(parameters)
{
if (isParamValid("partial_reaction_rates"))
{
std::vector<PostprocessorName> names = getParam<std::vector<PostprocessorName>>("partial_reaction_rates");
_partial_neutronics_reaction_rates.resize(names.size());
_stored_reaction_rates.resize(names.size());
for (unsigned int j = 0; j < names.size(); ++j)
if (_fe_problem.hasPostprocessor(names[j]))
_partial_neutronics_reaction_rates[j] = &getPostprocessorValueByName(names[j]);
else
{
Real real_value = -std::numeric_limits<Real>::max();
std::istringstream ss(names[j]);

if (ss >> real_value && ss.eof())
_stored_reaction_rates[j] = real_value;
else
mooseError("Illegal entry in _partial_neutronics_reaction_rates: ", names[j]);

_partial_neutronics_reaction_rates[j] = &_stored_reaction_rates[j];
}
}
else
{
_stored_reaction_rates = {1.0e-8};
_partial_neutronics_reaction_rates = {&_stored_reaction_rates[0]};
}

if (isParamValid("averaged_number_densities"))
{
std::vector<PostprocessorName> names = getParam<std::vector<PostprocessorName>>("averaged_number_densities");
_averaged_number_densities.resize(names.size());
_stored_densities.resize(names.size());
for (unsigned int j = 0; j < names.size(); ++j)
if (_fe_problem.hasPostprocessor(names[j]))
_averaged_number_densities[j] = &getPostprocessorValueByName(names[j]);
else
{
Real real_value = -std::numeric_limits<Real>::max();
std::istringstream ss(names[j]);

if (ss >> real_value && ss.eof())
_stored_densities[j] = real_value;
else
mooseError("Illegal entry in _partial_neutronics_reaction_rates: ", names[j]);

_averaged_number_densities[j] = &_stored_reaction_rates[j];
}
}
else
{
_averaged_number_densities.resize(_stored_reaction_rates.size());
_stored_densities.resize(_stored_reaction_rates.size());
for (unsigned int j = 0; j < _stored_densities.size(); ++j)
{
_stored_densities[j] = 1;
_averaged_number_densities[j] = &_stored_densities[j];
}
}

if (_averaged_number_densities.size() != _partial_neutronics_reaction_rates.size())
mooseError("partial_reaction_rates and averaged_number_densities must have the same number of entries.");
}
38 changes: 6 additions & 32 deletions src/userobjects/PKAGeneratorRecoil.C
Original file line number Diff line number Diff line change
Expand Up @@ -16,41 +16,13 @@ template<>
InputParameters validParams<PKAGeneratorRecoil>()
{
InputParameters params = validParams<PKAGeneratorNeutronicsBase>();
params.addParam<std::vector<PostprocessorName>>("partial_recoil_rates", "Partial recoil rates per unit volume [recoil reaction rate per nuclide]. "
"Provide number density as variable in rasterizer!");
params.addClassDescription("PKA recoil generator user object.\n Takes pdf and samples PKAs due to recoil reaction.");
return params;
}

PKAGeneratorRecoil::PKAGeneratorRecoil(const InputParameters & parameters):
PKAGeneratorNeutronicsBase(parameters)
{
if (isParamValid("partial_recoil_rates"))
{
std::vector<PostprocessorName> names = getParam<std::vector<PostprocessorName>>("partial_recoil_rates");
_partial_recoil_rates.resize(names.size());
_stored_pps.resize(names.size());
for (unsigned int j = 0; j < names.size(); ++j)
if (_fe_problem.hasPostprocessor(names[j]))
_partial_recoil_rates[j] = &getPostprocessorValueByName(names[j]);
else
{
Real real_value = -std::numeric_limits<Real>::max();
std::istringstream ss(names[j]);

if (ss >> real_value && ss.eof())
_stored_pps[j] = real_value;
else
mooseError("Illegal entry in partial_recoil_rates: ", names[j]);

_partial_recoil_rates[j] = &_stored_pps[j];
}
}
else
{
_stored_pps = {1.0e-8};
_partial_recoil_rates = {&_stored_pps[0]};
}
}

void
Expand All @@ -68,12 +40,14 @@ PKAGeneratorRecoil::appendPKAs(std::vector<MyTRIM_NS::IonBase> & ion_list, Real
mooseAssert(dt >= 0, "Passed a negative time window into PKAGeneratorRecoil::appendPKAs");
mooseAssert(vol >= 0, "Passed a negative volume into PKAGeneratorRecoil::appendPKAs");

if (averaged_data._elements.size() != _partial_recoil_rates.size())
mooseError("Size of averaged_data and partial_recoil_rates must be equal");
if (averaged_data._elements.size() != _partial_neutronics_reaction_rates.size())
mooseError("Size of averaged_data and partial_reaction_rates must be equal");

for (unsigned int nuclide = 0; nuclide < _partial_recoil_rates.size(); ++nuclide)
for (unsigned int nuclide = 0; nuclide < _partial_neutronics_reaction_rates.size(); ++nuclide)
{
unsigned int num_recoils = std::floor(recoil_rate_scaling * dt * vol * (*_partial_recoil_rates[nuclide]) * averaged_data._elements[nuclide] + getRandomReal());
unsigned int num_recoils = std::floor(recoil_rate_scaling * dt * vol *
(*_partial_neutronics_reaction_rates[nuclide]) / (*_averaged_number_densities[nuclide])
* averaged_data._elements[nuclide] + getRandomReal());

for (unsigned i = 0; i < num_recoils; ++i)
{
Expand Down
2 changes: 1 addition & 1 deletion tests/radiation_damage/coupled_fission_mockup/damage_sub.i
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@
[./neutronics_fission_generator]
type = PKAFissionFragmentNeutronics
relative_density = 1
partial_fission_rates = '2.0e-11 0 0'
partial_reaction_rates = '2.0e-11 0 0'
[../]
[./rasterizer]
type = MyTRIMRasterizer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@
[./neutronics_fission_generator]
type = PKAFissionFragmentNeutronics
relative_density = 1
partial_fission_rates = '1.0e-14 0 0'
partial_reaction_rates = '1.0e-14 0 0'
[../]
[./rasterizer]
type = MyTRIMRasterizer
Expand Down

0 comments on commit b4e8478

Please sign in to comment.