Exemple #1
0
 def background(self, background):
     cv.check_type('plot background', background, Iterable, Integral)
     cv.check_length('plot background', background, 3)
     for rgb in background:
         cv.check_greater_than('plot background',rgb, 0, True)
         cv.check_less_than('plot background', rgb, 256)
     self._background = background
Exemple #2
0
    def get_group_bounds(self, group):
        """Returns the energy boundaries for the energy group of interest.

        Parameters
        ----------
        group : Integral
            The energy group index, starting at 1 for the highest energies

        Returns
        -------
        2-tuple
            The low and high energy bounds for the group in MeV

        Raises
        ------
        ValueError
            If the group edges have not yet been set.

        """

        if self.group_edges is None:
            msg = 'Unable to get energy group bounds for group "{0}" since ' \
                  'the group edges have not yet been set'.format(group)
            raise ValueError(msg)

        cv.check_greater_than('group', group, 0)
        cv.check_less_than('group', group, self.num_groups, equality=True)

        lower = self.group_edges[self.num_groups-group]
        upper = self.group_edges[self.num_groups-group+1]
        return lower, upper
Exemple #3
0
 def mask_background(self, mask_background):
     cv.check_type('plot mask background', mask_background, Iterable, Integral)
     cv.check_length('plot mask background', mask_background, 3)
     for rgb in mask_background:
         cv.check_greater_than('plot mask background', rgb, 0, True)
         cv.check_less_than('plot mask background', rgb, 256)
     self._mask_background = mask_background
Exemple #4
0
 def num_l(self, num_l):
     if num_l is not None:
         cv.check_type('num_l', num_l, Integral)
         cv.check_greater_than('num_l', num_l, 1, equality=True)
         cv.check_less_than('num_l', num_l, 4, equality=True)
         # There is an if block in _evaluate that assumes num_l <= 4.
     self._num_l = num_l
Exemple #5
0
 def probability(self, probability):
     cv.check_type('mixture distribution probabilities', probability,
                   Iterable, Real)
     for p in probability:
         cv.check_greater_than('mixture distribution probabilities',
                               p, 0.0, True)
     self._probability = probability
Exemple #6
0
 def p(self, p):
     if isinstance(p, Real):
         p = [p]
     cv.check_type('discrete probabilities', p, Iterable, Real)
     for pk in p:
         cv.check_greater_than('discrete probability', pk, 0.0, True)
     self._p = p
Exemple #7
0
 def fit_order(self, fit_order):
     if fit_order is not None:
         cv.check_type('fit_order', fit_order, Integral)
         cv.check_greater_than('fit_order', fit_order, 2, equality=True)
         # _broaden_wmp_polynomials assumes the curve fit has at least 3
         # terms.
     self._fit_order = fit_order
Exemple #8
0
    def colorize(self, geometry, seed=1):
        """Generate a color scheme for each domain in the plot.

        This routine may be used to generate random, reproducible color schemes.
        The colors generated are based upon cell/material IDs in the geometry.

        Parameters
        ----------
        geometry : openmc.Geometry
            The geometry for which the plot is defined
        seed : Integral
            The random number seed used to generate the color scheme

        """

        cv.check_type('geometry', geometry, openmc.Geometry)
        cv.check_type('seed', seed, Integral)
        cv.check_greater_than('seed', seed, 1, equality=True)

        # Get collections of the domains which will be plotted
        if self.color_by == 'material':
            domains = geometry.get_all_materials().values()
        else:
            domains = geometry.get_all_cells().values()

        # Set the seed for the random number generator
        np.random.seed(seed)

        # Generate random colors for each feature
        for domain in domains:
            self.colors[domain] = np.random.randint(0, 256, (3,))
Exemple #9
0
 def albedo(self, albedo):
     check_type('CMFD mesh albedo', albedo, Iterable, Real)
     check_length('CMFD mesh albedo', albedo, 6)
     for a in albedo:
         check_greater_than('CMFD mesh albedo', a, 0, True)
         check_less_than('CMFD mesh albedo', a, 1, True)
     self._albedo = albedo
Exemple #10
0
    def bins(self, bins):
        if bins is None:
            self.num_bins = 0
        elif self._type is None:
            msg = 'Unable to set bins for Filter to "{0}" since ' \
                  'the Filter type has not yet been set'.format(bins)
            raise ValueError(msg)

        # If the bin edge is a single value, it is a Cell, Material, etc. ID
        if not isinstance(bins, Iterable):
            bins = [bins]

        # If the bins are in a collection, convert it to a list
        else:
            bins = list(bins)

        if self.type in ['cell', 'cellborn', 'surface', 'material',
                          'universe', 'distribcell']:
            check_iterable_type('filter bins', bins, Integral)
            for edge in bins:
                check_greater_than('filter bin', edge, 0, equality=True)

        elif self._type in ['energy', 'energyout']:
            for edge in bins:
                if not isinstance(edge, Real):
                    msg = 'Unable to add bin edge "{0}" to a "{1}" Filter ' \
                          'since it is a non-integer or floating point ' \
                          'value'.format(edge, self.type)
                    raise ValueError(msg)
                elif edge < 0.:
                    msg = 'Unable to add bin edge "{0}" to a "{1}" Filter ' \
                          'since it is a negative value'.format(edge, self.type)
                    raise ValueError(msg)

            # Check that bin edges are monotonically increasing
            for index in range(len(bins)):
                if index > 0 and bins[index] < bins[index-1]:
                    msg = 'Unable to add bin edges "{0}" to a "{1}" Filter ' \
                          'since they are not monotonically ' \
                          'increasing'.format(bins, self.type)
                    raise ValueError(msg)

        # mesh filters
        elif self._type == 'mesh':
            if not len(bins) == 1:
                msg = 'Unable to add bins "{0}" to a mesh Filter since ' \
                      'only a single mesh can be used per tally'.format(bins)
                raise ValueError(msg)
            elif not isinstance(bins[0], Integral):
                msg = 'Unable to add bin "{0}" to mesh Filter since it ' \
                       'is a non-integer'.format(bins[0])
                raise ValueError(msg)
            elif bins[0] < 0:
                msg = 'Unable to add bin "{0}" to mesh Filter since it ' \
                       'is a negative integer'.format(bins[0])
                raise ValueError(msg)

        # If all error checks passed, add bin edges
        self._bins = np.array(bins)
Exemple #11
0
 def id(self, lattice_id):
     if lattice_id is None:
         self._id = openmc.universe.AUTO_UNIVERSE_ID
         openmc.universe.AUTO_UNIVERSE_ID += 1
     else:
         cv.check_type('lattice ID', lattice_id, Integral)
         cv.check_greater_than('lattice ID', lattice_id, 0, equality=True)
         self._id = lattice_id
Exemple #12
0
 def temperature(self, temperature):
     cv.check_type('cell temperature', temperature, (Iterable, Real))
     if isinstance(temperature, Iterable):
         cv.check_type('cell temperature', temperature, Iterable, Real)
         for T in temperature:
             cv.check_greater_than('cell temperature', T, 0.0, True)
     else:
         cv.check_greater_than('cell temperature', temperature, 0.0, True)
     self._temperature = temperature
Exemple #13
0
 def id(self, universe_id):
     if universe_id is None:
         global AUTO_UNIVERSE_ID
         self._id = AUTO_UNIVERSE_ID
         AUTO_UNIVERSE_ID += 1
     else:
         cv.check_type('universe ID', universe_id, Integral)
         cv.check_greater_than('universe ID', universe_id, 0, equality=True)
         self._id = universe_id
Exemple #14
0
    def get_condensed_groups(self, coarse_groups):
        """Return a coarsened version of this EnergyGroups object.

        This method merges together energy groups in this object into wider
        energy groups as defined by the list of groups specified by the user,
        and returns a new, coarse EnergyGroups object.

        Parameters
        ----------
        coarse_groups : Iterable of 2-tuple
            The energy groups of interest - a list of 2-tuples, each directly
            corresponding to one of the new coarse groups. The values in the
            2-tuples are upper/lower energy groups used to construct a new
            coarse group. For example, if [(1,2), (3,4)] was used as the coarse
            groups, fine groups 1 and 2 would be merged into coarse group 1
            while fine groups 3 and 4 would be merged into coarse group 2.

        Returns
        -------
        EnergyGroups
            A coarsened version of this EnergyGroups object.

        Raises
        ------
        ValueError
            If the group edges have not yet been set.
        """

        cv.check_type('group edges', coarse_groups, Iterable)
        for group in coarse_groups:
            cv.check_type('group edges', group, Iterable)
            cv.check_length('group edges', group, 2)
            cv.check_greater_than('lower group', group[0], 1, True)
            cv.check_less_than('lower group', group[0], self.num_groups, True)
            cv.check_greater_than('upper group', group[0], 1, True)
            cv.check_less_than('upper group', group[0], self.num_groups, True)
            cv.check_less_than('lower group', group[0], group[1], False)

        # Compute the group indices into the coarse group
        group_bounds = [group[1] for group in coarse_groups]
        group_bounds.insert(0, coarse_groups[0][0])

        # Determine the indices mapping the fine-to-coarse energy groups
        group_bounds = np.asarray(group_bounds)
        group_indices = np.flipud(self.num_groups - group_bounds)
        group_indices[-1] += 1

        # Determine the edges between coarse energy groups and sort
        # in increasing order in case the user passed in unordered groups
        group_edges = self.group_edges[group_indices]
        group_edges = np.sort(group_edges)

        # Create a new condensed EnergyGroups object
        condensed_groups = EnergyGroups()
        condensed_groups.group_edges = group_edges

        return condensed_groups
Exemple #15
0
 def id(self, cell_id):
     if cell_id is None:
         global AUTO_CELL_ID
         self._id = AUTO_CELL_ID
         AUTO_CELL_ID += 1
     else:
         cv.check_type('cell ID', cell_id, Integral)
         cv.check_greater_than('cell ID', cell_id, 0, equality=True)
         self._id = cell_id
Exemple #16
0
 def id(self, lattice_id):
     if lattice_id is None:
         global AUTO_UNIVERSE_ID
         self._id = AUTO_UNIVERSE_ID
         AUTO_UNIVERSE_ID += 1
     else:
         cv.check_type('lattice ID', lattice_id, Integral)
         cv.check_greater_than('lattice ID', lattice_id, 0, equality=True)
         self._id = lattice_id
Exemple #17
0
 def id(self, plot_id):
     if plot_id is None:
         global AUTO_PLOT_ID
         self._id = AUTO_PLOT_ID
         AUTO_PLOT_ID += 1
     else:
         cv.check_type('plot ID', plot_id, Integral)
         cv.check_greater_than('plot ID', plot_id, 0, equality=True)
         self._id = plot_id
Exemple #18
0
 def id(self, surface_id):
     if surface_id is None:
         global AUTO_SURFACE_ID
         self._id = AUTO_SURFACE_ID
         AUTO_SURFACE_ID += 1
     else:
         check_type('surface ID', surface_id, Integral)
         check_greater_than('surface ID', surface_id, 0)
         self._id = surface_id
Exemple #19
0
 def id(self, mesh_id):
     if mesh_id is None:
         global AUTO_MESH_ID
         self._id = AUTO_MESH_ID
         AUTO_MESH_ID += 1
     else:
         cv.check_type('mesh ID', mesh_id, Integral)
         cv.check_greater_than('mesh ID', mesh_id, 0, equality=True)
         self._id = mesh_id
Exemple #20
0
 def id(self, deriv_id):
     if deriv_id is None:
         global AUTO_TALLY_DERIV_ID
         self._id = AUTO_TALLY_DERIV_ID
         AUTO_TALLY_DERIV_ID += 1
     else:
         cv.check_type('tally derivative ID', deriv_id, Integral)
         cv.check_greater_than('tally derivative ID', deriv_id, 0,
                               equality=True)
         self._id = deriv_id
Exemple #21
0
 def branching_ratio(self, branching_ratio):
     cv.check_type('branching ratio', branching_ratio, UFloat)
     cv.check_greater_than('branching ratio',
                           branching_ratio.nominal_value, 0.0, True)
     if branching_ratio.nominal_value == 0.0:
         warn('Decay mode {} of parent {} has a zero branching ratio.'
              .format(self.modes, self.parent))
     cv.check_greater_than('branching ratio uncertainty',
                           branching_ratio.std_dev, 0.0, True)
     self._branching_ratio = branching_ratio
Exemple #22
0
    def id(self, material_id):

        if material_id is None:
            global AUTO_MATERIAL_ID
            self._id = AUTO_MATERIAL_ID
            AUTO_MATERIAL_ID += 1
        else:
            cv.check_type('material ID', material_id, Integral)
            cv.check_greater_than('material ID', material_id, 0, equality=True)
            self._id = material_id
Exemple #23
0
    def highlight_domains(self, geometry, domains, seed=1,
                          alpha=0.5, background='gray'):
        """Use alpha compositing to highlight one or more domains in the plot.

        This routine generates a color scheme and applies alpha compositing
        to make all domains except the highlighted ones appear partially
        transparent.

        Parameters
        ----------
        geometry : openmc.Geometry
            The geometry for which the plot is defined
        domains : Iterable of Integral
            A collection of the domain IDs to highlight in the plot
        seed : Integral
            The random number seed used to generate the color scheme
        alpha : Real in [0,1]
            The value to apply in alpha compisiting
        background : 3-tuple of Integral or 'white' or 'black' or 'gray'
            The background color to apply in alpha compisiting

        """

        cv.check_iterable_type('domains', domains, Integral)
        cv.check_type('alpha', alpha, Real)
        cv.check_greater_than('alpha', alpha, 0., equality=True)
        cv.check_less_than('alpha', alpha, 1., equality=True)

        # Get a background (R,G,B) tuple to apply in alpha compositing
        if isinstance(background, basestring):
            if background == 'white':
                background = (255, 255, 255)
            elif background == 'black':
                background = (0, 0, 0)
            elif background == 'gray':
                background = (160, 160, 160)
            else:
                msg = 'The background "{}" is not defined'.format(background)
                raise ValueError(msg)

        cv.check_iterable_type('background', background, Integral)

        # Generate a color scheme
        self.colorize(geometry, seed)

        # Apply alpha compositing to the colors for all domains
        # other than those the user wishes to highlight
        for domain_id in self.col_spec:
            if domain_id not in domains:
                r, g, b = self.col_spec[domain_id]
                r = int(((1-alpha) * background[0]) + (alpha * r))
                g = int(((1-alpha) * background[1]) + (alpha * g))
                b = int(((1-alpha) * background[2]) + (alpha * b))
                self._col_spec[domain_id] = (r, g, b)
Exemple #24
0
 def tabular_legendre(self, tabular_legendre):
     cv.check_type('tabular_legendre settings', tabular_legendre, Mapping)
     for key, value in tabular_legendre.items():
         cv.check_value('tabular_legendre key', key,
                        ['enable', 'num_points'])
         if key == 'enable':
             cv.check_type('enable tabular_legendre', value, bool)
         elif key == 'num_points':
             cv.check_type('num_points tabular_legendre', value, Integral)
             cv.check_greater_than('num_points tabular_legendre', value, 0)
     self._tabular_legendre = tabular_legendre
Exemple #25
0
 def statepoint(self, statepoint):
     cv.check_type('statepoint options', statepoint, Mapping)
     for key, value in statepoint.items():
         if key == 'batches':
             cv.check_type('statepoint batches', value, Iterable, Integral)
             for batch in value:
                 cv.check_greater_than('statepoint batch', batch, 0)
         else:
             raise ValueError("Unknown key '{}' encountered when setting "
                              "statepoint options.".format(key))
     self._statepoint = statepoint
Exemple #26
0
 def mask_background(self, mask_background):
     cv.check_type('plot mask background', mask_background, Iterable)
     if isinstance(mask_background, string_types):
         if mask_background.lower() not in _SVG_COLORS:
             raise ValueError("'{}' is not a valid color.".format(mask_background))
     else:
         cv.check_length('plot mask_background', mask_background, 3)
         for rgb in mask_background:
             cv.check_greater_than('plot mask background', rgb, 0, True)
             cv.check_less_than('plot mask background', rgb, 256)
     self._mask_background = mask_background
Exemple #27
0
    def legendre_order(self, legendre_order):
        cv.check_type('legendre_order', legendre_order, Integral)
        cv.check_greater_than('legendre_order', legendre_order, 0, equality=True)
        cv.check_less_than('legendre_order', legendre_order, 10, equality=True)

        if self.correction == 'P0' and legendre_order > 0:
            msg = 'The P0 correction will be ignored since the scattering ' \
                  'order {} is greater than zero'.format(self.legendre_order)
            warn(msg, RuntimeWarning)
            self.correction = None

        self._legendre_order = legendre_order
Exemple #28
0
    def highlight_domains(self, geometry, domains, seed=1,
                          alpha=0.5, background='gray'):
        """Use alpha compositing to highlight one or more domains in the plot.

        This routine generates a color scheme and applies alpha compositing to
        make all domains except the highlighted ones appear partially
        transparent.

        Parameters
        ----------
        geometry : openmc.Geometry
            The geometry for which the plot is defined
        domains : Iterable of openmc.Cell or openmc.Material
            A collection of the domain IDs to highlight in the plot
        seed : int
            The random number seed used to generate the color scheme
        alpha : float
            The value between 0 and 1 to apply in alpha compisiting
        background : 3-tuple of int or str
            The background color to apply in alpha compisiting

        """

        cv.check_type('domains', domains, Iterable,
                      (openmc.Cell, openmc.Material))
        cv.check_type('alpha', alpha, Real)
        cv.check_greater_than('alpha', alpha, 0., equality=True)
        cv.check_less_than('alpha', alpha, 1., equality=True)
        cv.check_type('background', background, Iterable)

        # Get a background (R,G,B) tuple to apply in alpha compositing
        if isinstance(background, string_types):
            if background.lower() not in _SVG_COLORS:
                raise ValueError("'{}' is not a valid color.".format(background))
            background = _SVG_COLORS[background.lower()]

        # Generate a color scheme
        self.colorize(geometry, seed)

        # Apply alpha compositing to the colors for all domains
        # other than those the user wishes to highlight
        for domain, color in self.colors.items():
            if domain not in domains:
                if isinstance(color, string_types):
                    color = _SVG_COLORS[color.lower()]
                r, g, b = color
                r = int(((1-alpha) * background[0]) + (alpha * r))
                g = int(((1-alpha) * background[1]) + (alpha * g))
                b = int(((1-alpha) * background[2]) + (alpha * b))
                self._colors[domain] = (r, g, b)
Exemple #29
0
    def colors(self, colors):
        cv.check_type('plot colors', colors, Mapping)
        for key, value in colors.items():
            cv.check_type('plot color key', key, (openmc.Cell, openmc.Material))
            cv.check_type('plot color value', value, Iterable)
            if isinstance(value, string_types):
                if value.lower() not in _SVG_COLORS:
                    raise ValueError("'{}' is not a valid color.".format(value))
            else:
                cv.check_length('plot color (RGB)', value, 3)
                for component in value:
                    cv.check_type('RGB component', component, Real)
                    cv.check_greater_than('RGB component', component, 0, True)
                    cv.check_less_than('RGB component', component, 255, True)

        self._colors = colors
Exemple #30
0
 def sourcepoint(self, sourcepoint):
     cv.check_type('sourcepoint options', sourcepoint, Mapping)
     for key, value in sourcepoint.items():
         if key == 'batches':
             cv.check_type('sourcepoint batches', value, Iterable, Integral)
             for batch in value:
                 cv.check_greater_than('sourcepoint batch', batch, 0)
         elif key == 'separate':
             cv.check_type('sourcepoint separate', value, bool)
         elif key == 'write':
             cv.check_type('sourcepoint write', value, bool)
         elif key == 'overwrite':
             cv.check_type('sourcepoint overwrite', value, bool)
         else:
             raise ValueError("Unknown key '{}' encountered when setting "
                              "sourcepoint options.".format(key))
     self._sourcepoint = sourcepoint
Exemple #31
0
 def trace(self, trace):
     cv.check_type('trace', trace, Iterable, Integral)
     cv.check_length('trace', trace, 3)
     cv.check_greater_than('trace batch', trace[0], 0)
     cv.check_greater_than('trace generation', trace[1], 0)
     cv.check_greater_than('trace particle', trace[2], 0)
     self._trace = trace
Exemple #32
0
 def resonance_scattering(self, res):
     cv.check_type('resonance scattering settings', res, Mapping)
     keys = ('enable', 'method', 'energy_min', 'energy_max', 'nuclides')
     for key, value in res.items():
         cv.check_value('resonance scattering dictionary key', key, keys)
         if key == 'enable':
             cv.check_type('resonance scattering enable', value, bool)
         elif key == 'method':
             cv.check_value('resonance scattering method', value,
                            _RES_SCAT_METHODS)
         elif key == 'energy_min':
             name = 'resonance scattering minimum energy'
             cv.check_type(name, value, Real)
             cv.check_greater_than(name, value, 0)
         elif key == 'energy_max':
             name = 'resonance scattering minimum energy'
             cv.check_type(name, value, Real)
             cv.check_greater_than(name, value, 0)
         elif key == 'nuclides':
             cv.check_type('resonance scattering nuclides', value, Iterable,
                           string_types)
     self._resonance_scattering = res
Exemple #33
0
    def get_group_indices(self, groups='all'):
        """Returns the array indices for one or more energy groups.

        Parameters
        ----------
        groups : str, tuple
            The energy groups of interest - a tuple of the energy group indices,
            starting at 1 for the highest energies (default is 'all')

        Returns
        -------
        numpy.ndarray
            The ndarray array indices for each energy group of interest

        Raises
        ------
        ValueError
            If the group edges have not yet been set, or if a group is requested
            that is outside the bounds of the number of energy groups.

        """

        if self.group_edges is None:
            msg = 'Unable to get energy group indices for groups "{0}" since ' \
                  'the group edges have not yet been set'.format(groups)
            raise ValueError(msg)

        if groups == 'all':
            return np.arange(self.num_groups)
        else:
            indices = np.zeros(len(groups), dtype=np.int)

        for i, group in enumerate(groups):
            cv.check_greater_than('group', group, 0)
            cv.check_less_than('group', group, self.num_groups, equality=True)
            indices[i] = group - 1

        return indices
Exemple #34
0
    def add_s_alpha_beta(self, name, fraction=1.0):
        r"""Add an :math:`S(\alpha,\beta)` table to the material

        Parameters
        ----------
        name : str
            Name of the :math:`S(\alpha,\beta)` table
        fraction : float
            The fraction of relevant nuclei that are affected by the
            :math:`S(\alpha,\beta)` table.  For example, if the material is a
            block of carbon that is 60% graphite and 40% amorphous then add a
            graphite :math:`S(\alpha,\beta)` table with fraction=0.6.

        """

        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)
            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)
            raise ValueError(msg)

        cv.check_type('S(a,b) fraction', fraction, Real)
        cv.check_greater_than('S(a,b) fraction', fraction, 0.0, True)
        cv.check_less_than('S(a,b) fraction', fraction, 1.0, True)

        new_name = openmc.data.get_thermal_name(name)
        if new_name != name:
            msg = 'OpenMC S(a,b) tables follow the GND naming convention. ' \
                  'Table "{}" is being renamed as "{}".'.format(name, new_name)
            warnings.warn(msg)

        self._sab.append((new_name, fraction))
Exemple #35
0
 def track(self, track):
     check_type('track', track, Iterable, Integral)
     if len(track) % 3 != 0:
         msg = 'Unable to set the track to "{0}" since its length is ' \
               'not a multiple of 3'.format(track)
         raise ValueError(msg)
     for t in zip(track[::3], track[1::3], track[2::3]):
         check_greater_than('track batch', t[0], 0)
         check_greater_than('track generation', t[0], 0)
         check_greater_than('track particle', t[0], 0)
     self._track = track
Exemple #36
0
 def __init__(self,
              chain_nuclides,
              n_bmats,
              cutoff=112.0,
              thermal_energy=0.0253,
              fast_energy=500.0e3):
     check_type("cutoff", cutoff, Real)
     check_type("thermal_energy", thermal_energy, Real)
     check_type("fast_energy", fast_energy, Real)
     check_greater_than("thermal_energy",
                        thermal_energy,
                        0.0,
                        equality=True)
     check_greater_than("cutoff", cutoff, thermal_energy, equality=False)
     check_greater_than("fast_energy", fast_energy, cutoff, equality=False)
     self.n_bmats = n_bmats
     super().__init__(chain_nuclides)
     self._cutoff = cutoff
     self._thermal_yields = {}
     self._fast_yields = {}
     convert_to_constant = set()
     for name, nuc in self._chain_nuclides.items():
         yields = nuc.yield_data
         energies = nuc.yield_energies
         thermal = yields.get(thermal_energy)
         fast = yields.get(fast_energy)
         if thermal is None or fast is None:
             if cutoff <= energies[0]:
                 # use lowest energy yields as constant
                 self._constant_yields[name] = yields[energies[0]]
                 convert_to_constant.add(name)
                 continue
             if cutoff >= energies[-1]:
                 # use highest energy yields as constant
                 self._constant_yields[name] = yields[energies[-1]]
                 convert_to_constant.add(name)
                 continue
             cutoff_ix = bisect.bisect_left(energies, cutoff)
             # find closest energy to requested thermal, fast energies
             if thermal is None:
                 min_E = min(energies[:cutoff_ix],
                             key=lambda e: abs(e - thermal_energy))
                 thermal = yields[min_E]
             if fast is None:
                 min_E = min(energies[cutoff_ix:],
                             key=lambda e: abs(e - fast_energy))
                 fast = yields[min_E]
         self._thermal_yields[name] = thermal
         self._fast_yields[name] = fast
     for name in convert_to_constant:
         self._chain_nuclides.pop(name)
Exemple #37
0
    def cutoff(self, cutoff):
        if not isinstance(cutoff, Mapping):
            msg = 'Unable to set cutoff from "{0}" which is not a '\
                  ' Python dictionary'.format(cutoff)
            raise ValueError(msg)
        for key in cutoff:
            if key == 'weight':
                cv.check_type('weight cutoff', cutoff['weight'], Real)
                cv.check_greater_than('weight cutoff', cutoff['weight'], 0.0)
            elif key == 'weight_avg':
                cv.check_type('average survival weight', cutoff['weight_avg'],
                              Real)
                cv.check_greater_than('average survival weight',
                                      cutoff['weight_avg'], 0.0)
            elif key == 'energy':
                cv.check_type('energy cutoff', cutoff['energy'], Real)
                cv.check_greater_than('energy cutoff', cutoff['energy'], 0.0)
            else:
                msg = 'Unable to set cutoff to "{0}" which is unsupported by '\
                      'OpenMC'.format(key)

        self._cutoff = cutoff
Exemple #38
0
    def meshlines(self, meshlines):
        cv.check_type('plot meshlines', meshlines, dict)
        if 'type' not in meshlines:
            msg = 'Unable to set on plot the meshlines "{0}" which ' \
                  'does not have a "type" key'.format(meshlines)
            raise ValueError(msg)

        elif meshlines['type'] not in ['tally', 'entropy', 'ufs', 'cmfd']:
            msg = 'Unable to set the meshlines with ' \
                  'type "{0}"'.format(meshlines['type'])
            raise ValueError(msg)

        if 'id' in meshlines:
            cv.check_type('plot meshlines id', meshlines['id'], Integral)
            cv.check_greater_than('plot meshlines id',
                                  meshlines['id'],
                                  0,
                                  equality=True)

        if 'linewidth' in meshlines:
            cv.check_type('plot mesh linewidth', meshlines['linewidth'],
                          Integral)
            cv.check_greater_than('plot mesh linewidth',
                                  meshlines['linewidth'],
                                  0,
                                  equality=True)

        if 'color' in meshlines:
            cv.check_type('plot meshlines color', meshlines['color'], Iterable,
                          Integral)
            cv.check_length('plot meshlines color', meshlines['color'], 3)
            for rgb in meshlines['color']:
                cv.check_greater_than('plot meshlines color', rgb, 0, True)
                cv.check_less_than('plot meshlines color', rgb, 256)

        self._meshlines = meshlines
Exemple #39
0
 def spacing(self, spacing):
     if spacing is not None:
         cv.check_type('spacing', spacing, Real)
         cv.check_greater_than('spacing', spacing, 0.0, equality=False)
     self._spacing = spacing
Exemple #40
0
    def __init__(self, operator, timesteps, power=None, power_density=None,
                 timestep_units='s', solver="cram48"):
        # Check number of stages previously used
        if operator.prev_res is not None:
            res = operator.prev_res[-1]
            if res.data.shape[0] != self._num_stages:
                raise ValueError(
                    "{} incompatible with previous restart calculation. "
                    "Previous scheme used {} intermediate solutions, while "
                    "this uses {}".format(
                        self.__class__.__name__, res.data.shape[0],
                        self._num_stages))
        self.operator = operator
        self.chain = operator.chain

        # Determine power and normalize units to W
        if power is None:
            if power_density is None:
                raise ValueError("Either power or power density must be set")
            if not isinstance(power_density, Iterable):
                power = power_density * operator.heavy_metal
            else:
                power = [p*operator.heavy_metal for p in power_density]
        if not isinstance(power, Iterable):
            # Ensure that power is single value if that is the case
            power = [power] * len(timesteps)

        if len(power) != len(timesteps):
            raise ValueError(
                "Number of time steps ({}) != number of powers ({})".format(
                    len(timesteps), len(power)))

        # Get list of times / units
        if isinstance(timesteps[0], Iterable):
            times, units = zip(*timesteps)
        else:
            times = timesteps
            units = [timestep_units] * len(timesteps)

        # Determine number of seconds for each timestep
        seconds = []
        for timestep, unit, watts in zip(times, units, power):
            # Make sure values passed make sense
            check_type('timestep', timestep, Real)
            check_greater_than('timestep', timestep, 0.0, False)
            check_type('timestep units', unit, str)
            check_type('power', watts, Real)
            check_greater_than('power', watts, 0.0, True)

            if unit in ('s', 'sec'):
                seconds.append(timestep)
            elif unit in ('min', 'minute'):
                seconds.append(timestep*_SECONDS_PER_MINUTE)
            elif unit in ('h', 'hr', 'hour'):
                seconds.append(timestep*_SECONDS_PER_HOUR)
            elif unit in ('d', 'day'):
                seconds.append(timestep*_SECONDS_PER_DAY)
            elif unit.lower() == 'mwd/kg':
                watt_days_per_kg = 1e6*timestep
                kilograms = 1e-3*operator.heavy_metal
                days = watt_days_per_kg * kilograms / watts
                seconds.append(days*_SECONDS_PER_DAY)
            else:
                raise ValueError("Invalid timestep unit '{}'".format(unit))

        self.timesteps = asarray(seconds)
        self.power = asarray(power)

        if isinstance(solver, str):
            # Delay importing of cram module, which requires this file
            if solver == "cram48":
                from .cram import CRAM48
                self._solver = CRAM48
            elif solver == "cram16":
                from .cram import CRAM16
                self._solver = CRAM16
            else:
                raise ValueError(
                    "Solver {} not understood. Expected 'cram48' or "
                    "'cram16'".format(solver))
        else:
            self.solver = solver
Exemple #41
0
 def dilute_initial(self, value):
     check_type("dilute_initial", value, Real)
     check_greater_than("dilute_initial", value, 0.0, equality=True)
     self._dilute_initial = value
Exemple #42
0
def _calculate_cexs_nuclide(this, types, temperature=294., sab_name=None,
                            cross_sections=None):
    """Calculates continuous-energy cross sections of a requested type.

    Parameters
    ----------
    this : openmc.Nuclide
        Nuclide object to source data from
    types : Iterable of str or Integral
        The type of cross sections to calculate; values can either be those
        in openmc.PLOT_TYPES or keys from openmc.data.REACTION_MT which
        correspond to a reaction description e.g '(n,2n)' or integers which
        correspond to reaction channel (MT) numbers.
    temperature : float, optional
        Temperature in Kelvin to plot. If not specified, a default
        temperature of 294K will be plotted. Note that the nearest
        temperature in the library for each nuclide will be used as opposed
        to using any interpolation.
    sab_name : str, optional
        Name of S(a,b) library to apply to MT=2 data when applicable.
    cross_sections : str, optional
        Location of cross_sections.xml file. Default is None.

    Returns
    -------
    energy_grid : numpy.ndarray
        Energies at which cross sections are calculated, in units of eV
    data : Iterable of Callable
        Requested cross section functions

    """

    # Load the library
    library = openmc.data.DataLibrary.from_xml(cross_sections)

    # Convert temperature to format needed for access in the library
    strT = f"{int(round(temperature))}K"
    T = temperature

    # Now we can create the data sets to be plotted
    energy_grid = []
    xs = []
    lib = library.get_by_material(this)
    if lib is not None:
        nuc = openmc.data.IncidentNeutron.from_hdf5(lib['path'])
        # Obtain the nearest temperature
        if strT in nuc.temperatures:
            nucT = strT
        else:
            delta_T = np.array(nuc.kTs) - T * openmc.data.K_BOLTZMANN
            closest_index = np.argmin(np.abs(delta_T))
            nucT = nuc.temperatures[closest_index]

        # Prep S(a,b) data if needed
        if sab_name:
            sab = openmc.data.ThermalScattering.from_hdf5(sab_name)
            # Obtain the nearest temperature
            if strT in sab.temperatures:
                sabT = strT
            else:
                delta_T = np.array(sab.kTs) - T * openmc.data.K_BOLTZMANN
                closest_index = np.argmin(np.abs(delta_T))
                sabT = sab.temperatures[closest_index]

            # Create an energy grid composed the S(a,b) and the nuclide's grid
            grid = nuc.energy[nucT]
            sab_Emax = 0.
            sab_funcs = []
            if sab.elastic is not None:
                elastic = sab.elastic.xs[sabT]
                if isinstance(elastic, openmc.data.CoherentElastic):
                    grid = np.union1d(grid, elastic.bragg_edges)
                    if elastic.bragg_edges[-1] > sab_Emax:
                        sab_Emax = elastic.bragg_edges[-1]
                elif isinstance(elastic, openmc.data.Tabulated1D):
                    grid = np.union1d(grid, elastic.x)
                    if elastic.x[-1] > sab_Emax:
                        sab_Emax = elastic.x[-1]
                sab_funcs.append(elastic)
            if sab.inelastic is not None:
                inelastic = sab.inelastic.xs[sabT]
                grid = np.union1d(grid, inelastic.x)
                if inelastic.x[-1] > sab_Emax:
                        sab_Emax = inelastic.x[-1]
                sab_funcs.append(inelastic)
            energy_grid = grid
        else:
            energy_grid = nuc.energy[nucT]

        # Parse the types
        mts = []
        ops = []
        yields = []
        for line in types:
            if line in PLOT_TYPES:
                tmp_mts = [mtj for mti in PLOT_TYPES_MT[line] for mtj in
                           nuc.get_reaction_components(mti)]
                mts.append(tmp_mts)
                if line.startswith('nu'):
                    yields.append(True)
                else:
                    yields.append(False)
                if XI_MT in tmp_mts:
                    ops.append((np.add,) * (len(tmp_mts) - 2) + (np.multiply,))
                else:
                    ops.append((np.add,) * (len(tmp_mts) - 1))
            elif line in openmc.data.REACTION_MT:
                mt_number = openmc.data.REACTION_MT[line]
                cv.check_type('MT in types', mt_number, Integral)
                cv.check_greater_than('MT in types', mt_number, 0)
                tmp_mts = nuc.get_reaction_components(mt_number)
                mts.append(tmp_mts)
                ops.append((np.add,) * (len(tmp_mts) - 1))
                yields.append(False)
            elif isinstance(line, int):
                # Not a built-in type, we have to parse it ourselves
                cv.check_type('MT in types', line, Integral)
                cv.check_greater_than('MT in types', line, 0)
                tmp_mts = nuc.get_reaction_components(line)
                mts.append(tmp_mts)
                ops.append((np.add,) * (len(tmp_mts) - 1))
                yields.append(False)
            else:
                raise TypeError("Invalid type", line)

        for i, mt_set in enumerate(mts):
            # Get the reaction xs data from the nuclide
            funcs = []
            op = ops[i]
            for mt in mt_set:
                if mt == 2:
                    if sab_name:
                        # Then we need to do a piece-wise function of
                        # The S(a,b) and non-thermal data
                        sab_sum = openmc.data.Sum(sab_funcs)
                        pw_funcs = openmc.data.Regions1D(
                            [sab_sum, nuc[mt].xs[nucT]],
                            [sab_Emax])
                        funcs.append(pw_funcs)
                    else:
                        funcs.append(nuc[mt].xs[nucT])
                elif mt in nuc:
                    if yields[i]:
                        # Get the total yield first if available. This will be
                        # used primarily for fission.
                        for prod in chain(nuc[mt].products,
                                          nuc[mt].derived_products):
                            if prod.particle == 'neutron' and \
                                prod.emission_mode == 'total':
                                func = openmc.data.Combination(
                                    [nuc[mt].xs[nucT], prod.yield_],
                                    [np.multiply])
                                funcs.append(func)
                                break
                        else:
                            # Total doesn't exist so we have to create from
                            # prompt and delayed. This is used for scatter
                            # multiplication.
                            func = None
                            for prod in chain(nuc[mt].products,
                                              nuc[mt].derived_products):
                                if prod.particle == 'neutron' and \
                                    prod.emission_mode != 'total':
                                    if func:
                                        func = openmc.data.Combination(
                                            [prod.yield_, func], [np.add])
                                    else:
                                        func = prod.yield_
                            if func:
                                funcs.append(openmc.data.Combination(
                                    [func, nuc[mt].xs[nucT]], [np.multiply]))
                            else:
                                # If func is still None, then there were no
                                # products. In that case, assume the yield is
                                # one as its not provided for some summed
                                # reactions like MT=4
                                funcs.append(nuc[mt].xs[nucT])
                    else:
                        funcs.append(nuc[mt].xs[nucT])
                elif mt == UNITY_MT:
                    funcs.append(lambda x: 1.)
                elif mt == XI_MT:
                    awr = nuc.atomic_weight_ratio
                    alpha = ((awr - 1.) / (awr + 1.))**2
                    xi = 1. + alpha * np.log(alpha) / (1. - alpha)
                    funcs.append(lambda x: xi)
                else:
                    funcs.append(lambda x: 0.)
            funcs = funcs if funcs else [lambda x: 0.]
            xs.append(openmc.data.Combination(funcs, op))
    else:
        raise ValueError(this + " not in library")

    return energy_grid, xs
Exemple #43
0
 def p(self, p):
     cv.check_type('tabulated probabilities', p, Iterable, Real)
     if not self._ignore_negative:
         for pk in p:
             cv.check_greater_than('tabulated probability', pk, 0.0, True)
     self._p = p
Exemple #44
0
def _calculate_mgxs_nuc_macro(this, types, library, orders=None,
                              temperature=294.):
    """Determines the multi-group cross sections of a nuclide or macroscopic
    object.

    If the data for the nuclide or macroscopic object in the library is
    represented as angle-dependent data then this method will return the
    geometric average cross section over all angles.

    Parameters
    ----------
    this : openmc.Nuclide or openmc.Macroscopic
        Object to source data from
    types : Iterable of str
        The type of cross sections to calculate; values can either be those
        in openmc.PLOT_TYPES_MGXS
    library : openmc.MGXSLibrary
        MGXS Library containing the data of interest
    orders : Iterable of Integral, optional
        The scattering order or delayed group index to use for the
        corresponding entry in types. Defaults to the 0th order for scattering
        and the total delayed neutron data.
    temperature : float, optional
        Temperature in Kelvin to plot. If not specified, a default
        temperature of 294K will be plotted. Note that the nearest
        temperature in the library for each nuclide will be used as opposed
        to using any interpolation.

    Returns
    -------
    data : numpy.ndarray
        Cross sections calculated at the energy grid described by energy_grid

    """

    # Check the parameters and grab order/delayed groups
    if orders:
        cv.check_iterable_type('orders', orders, Integral,
                               min_depth=len(types), max_depth=len(types))
    else:
        orders = [None] * len(types)
    for i, line in enumerate(types):
        cv.check_type("line", line, str)
        cv.check_value("line", line, PLOT_TYPES_MGXS)
        if orders[i]:
            cv.check_greater_than("order value", orders[i], 0, equality=True)

    xsdata = library.get_by_name(this)

    if xsdata is not None:
        # Obtain the nearest temperature
        t = np.abs(xsdata.temperatures - temperature).argmin()

        # Get the data
        data = np.zeros((len(types), library.energy_groups.num_groups))
        for i, line in enumerate(types):
            if 'fission' in line and not xsdata.fissionable:
                continue
            elif line == 'unity':
                data[i, :] = 1.
            else:
                # Now we have to get the cross section data and properly
                # treat it depending on the requested type.
                # First get the data in a generic fashion
                temp_data = getattr(xsdata, _PLOT_MGXS_ATTR[line])[t]
                shape = temp_data.shape[:]
                # If we have angular data, then want the geometric
                # average over all provided angles.  Since the angles are
                # equi-distant, un-weighted averaging will suffice
                if xsdata.representation == 'angle':
                    temp_data = np.mean(temp_data, axis=(0, 1))

                # Now we can look at the shape of the data to identify how
                # it should be modified to produce an array of values
                # with groups.
                if shape in (xsdata.xs_shapes["[G']"],
                             xsdata.xs_shapes["[G]"]):
                    # Then the data is already an array vs groups so copy
                    # and move along
                    data[i, :] = temp_data
                elif shape == xsdata.xs_shapes["[G][G']"]:
                    # Sum the data over outgoing groups to create our array vs
                    # groups
                    data[i, :] = np.sum(temp_data, axis=1)
                elif shape == xsdata.xs_shapes["[DG]"]:
                    # Then we have a constant vs groups with a value for each
                    # delayed group. The user-provided value of orders tells us
                    # which delayed group we want. If none are provided, then
                    # we sum all the delayed groups together.
                    if orders[i]:
                        if orders[i] < len(shape[0]):
                            data[i, :] = temp_data[orders[i]]
                    else:
                        data[i, :] = np.sum(temp_data[:])
                elif shape in (xsdata.xs_shapes["[DG][G']"],
                               xsdata.xs_shapes["[DG][G]"]):
                    # Then we have an array vs groups with values for each
                    # delayed group. The user-provided value of orders tells us
                    # which delayed group we want. If none are provided, then
                    # we sum all the delayed groups together.
                    if orders[i]:
                        if orders[i] < len(shape[0]):
                            data[i, :] = temp_data[orders[i], :]
                    else:
                        data[i, :] = np.sum(temp_data[:, :], axis=0)
                elif shape == xsdata.xs_shapes["[DG][G][G']"]:
                    # Then we have a delayed group matrix. We will first
                    # remove the outgoing group dependency
                    temp_data = np.sum(temp_data, axis=-1)
                    # And then proceed in exactly the same manner as the
                    # "[DG][G']" or "[DG][G]" shapes in the previous block.
                    if orders[i]:
                        if orders[i] < len(shape[0]):
                            data[i, :] = temp_data[orders[i], :]
                    else:
                        data[i, :] = np.sum(temp_data[:, :], axis=0)
                elif shape == xsdata.xs_shapes["[G][G'][Order]"]:
                    # This is a scattering matrix with angular data
                    # First remove the outgoing group dependence
                    temp_data = np.sum(temp_data, axis=1)
                    # The user either provided a specific order or we resort
                    # to the default 0th order
                    if orders[i]:
                        order = orders[i]
                    else:
                        order = 0
                    # If the order is available, store the data for that order
                    # if it is not available, then the expansion coefficient
                    # is zero and thus we already have the correct value.
                    if order < shape[1]:
                        data[i, :] = temp_data[:, order]
    else:
        raise ValueError(f"{this} not present in provided MGXS library")

    return data
Exemple #45
0
 def end_E(self, end_E):
     if end_E is not None:
         cv.check_type('end_E', end_E, Real)
         cv.check_greater_than('end_E', end_E, 0.0, equality=False)
     self._end_E = end_E
Exemple #46
0
 def atomic_weight_ratio(self, atomic_weight_ratio):
     name = 'N-body phase space atomic weight ratio'
     cv.check_type(name, atomic_weight_ratio, Real)
     cv.check_greater_than(name, atomic_weight_ratio, 0.0)
     self._atomic_weight_ratio = atomic_weight_ratio
Exemple #47
0
 def sqrtAWR(self, sqrtAWR):
     if sqrtAWR is not None:
         cv.check_type('sqrtAWR', sqrtAWR, Real)
         cv.check_greater_than('sqrtAWR', sqrtAWR, 0.0, equality=False)
     self._sqrtAWR = sqrtAWR
Exemple #48
0
 def total_mass(self, total_mass):
     name = 'N-body phase space total mass'
     cv.check_type(name, total_mass, Real)
     cv.check_greater_than(name, total_mass, 0.)
     self._total_mass = total_mass
Exemple #49
0
 def start_E(self, start_E):
     if start_E is not None:
         cv.check_type('start_E', start_E, Real)
         cv.check_greater_than('start_E', start_E, 0.0, equality=True)
     self._start_E = start_E
Exemple #50
0
 def pixels(self, pixels):
     cv.check_type('plot pixels', pixels, Iterable, Integral)
     cv.check_length('plot pixels', pixels, 2, 3)
     for dim in pixels:
         cv.check_greater_than('plot pixels', dim, 0)
     self._pixels = pixels
    def add_element(self,
                    element,
                    percent,
                    percent_type='ao',
                    enrichment=None):
        """Add a natural element to the material

        Parameters
        ----------
        element : str
            Element to add, e.g., 'Zr'
        percent : float
            Atom or weight percent
        percent_type : {'ao', 'wo'}, optional
            'ao' for atom percent and 'wo' for weight percent. Defaults to atom
            percent.
        enrichment : float, optional
            Enrichment for U235 in weight percent. For example, input 4.95 for
            4.95 weight percent enriched U. Default is None
            (natural composition).

        """
        cv.check_type('nuclide', element, str)
        cv.check_type('percent', percent, Real)
        cv.check_value('percent type', percent_type, {'ao', 'wo'})

        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)
            raise ValueError(msg)

        if enrichment is not None:
            if not isinstance(enrichment, Real):
                msg = 'Unable to add an Element to Material ID="{}" with a ' \
                      'non-floating point enrichment value "{}"'\
                      .format(self._id, enrichment)
                raise ValueError(msg)

            elif element != 'U':
                msg = 'Unable to use enrichment for element {} which is not ' \
                      'uranium for Material ID="{}"'.format(element, self._id)
                raise ValueError(msg)

            # Check that the enrichment is in the valid range
            cv.check_less_than('enrichment', enrichment, 100. / 1.008)
            cv.check_greater_than('enrichment', enrichment, 0., equality=True)

            if enrichment > 5.0:
                msg = 'A uranium enrichment of {} was given for Material ID='\
                      '"{}". OpenMC assumes the U234/U235 mass ratio is '\
                      'constant at 0.008, which is only valid at low ' \
                      'enrichments. Consider setting the isotopic ' \
                      'composition manually for enrichments over 5%.'.\
                      format(enrichment, self._id)
                warnings.warn(msg)

        # Make sure element name is just that
        if not element.isalpha():
            raise ValueError("Element name should be given by the "
                             "element's symbol, e.g., 'Zr'")

        # Add naturally-occuring isotopes
        element = openmc.Element(element)
        for nuclide in element.expand(percent, percent_type, enrichment):
            self.add_nuclide(*nuclide)
Exemple #52
0
 def __init__(self, operator, timesteps, power=None, power_density=None,
              n_steps=10):
     check_type("n_steps", n_steps, Integral)
     check_greater_than("n_steps", n_steps, 0)
     super().__init__(operator, timesteps, power, power_density)
     self.n_steps = n_steps
Exemple #53
0
    def add_element(self,
                    element,
                    percent,
                    percent_type='ao',
                    enrichment=None,
                    enrichment_target=None,
                    enrichment_type=None):
        """Add a natural element to the material

        Parameters
        ----------
        element : str
            Element to add, e.g., 'Zr' or 'Zirconium'
        percent : float
            Atom or weight percent
        percent_type : {'ao', 'wo'}, optional
            'ao' for atom percent and 'wo' for weight percent. Defaults to atom
            percent.
        enrichment : float, optional
            Enrichment of an enrichment_taget nuclide in percent (ao or wo).
            If enrichment_taget is not supplied then it is enrichment for U235
            in weight percent. For example, input 4.95 for 4.95 weight percent
            enriched U.
            Default is None (natural composition).
        enrichment_target: str, optional
            Single nuclide name to enrich from a natural composition (e.g., 'O16')

            .. versionadded:: 0.12
        enrichment_type: {'ao', 'wo'}, optional
            'ao' for enrichment as atom percent and 'wo' for weight percent.
            Default is: 'ao' for two-isotope enrichment; 'wo' for U enrichment

            .. versionadded:: 0.12

        Notes
        -----
        General enrichment procedure is allowed only for elements composed of
        two isotopes. If `enrichment_target` is given without `enrichment`
        natural composition is added to the material.

        """

        cv.check_type('nuclide', element, str)
        cv.check_type('percent', percent, Real)
        cv.check_value('percent type', percent_type, {'ao', 'wo'})

        # Make sure element name is just that
        if not element.isalpha():
            raise ValueError(
                "Element name should be given by the "
                "element's symbol or name, e.g., 'Zr', 'zirconium'")

        # Allow for element identifier to be given as a symbol or name
        if len(element) > 2:
            el = element.lower()
            element = openmc.data.ELEMENT_SYMBOL.get(el)
            if element is None:
                msg = 'Element name "{}" not recognised'.format(el)
                raise ValueError(msg)
        else:
            if element[0].islower():
                msg = 'Element name "{}" should start with an uppercase ' \
                      'letter'.format(element)
                raise ValueError(msg)
            if len(element) == 2 and element[1].isupper():
                msg = 'Element name "{}" should end with a lowercase ' \
                      'letter'.format(element)
                raise ValueError(msg)
            # skips the first entry of ATOMIC_SYMBOL which is n for neutron
            if element not in list(openmc.data.ATOMIC_SYMBOL.values())[1:]:
                msg = 'Element name "{}" not recognised'.format(element)
                raise ValueError(msg)

        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)
            raise ValueError(msg)

        if enrichment is not None and enrichment_target is None:
            if not isinstance(enrichment, Real):
                msg = 'Unable to add an Element to Material ID="{}" with a ' \
                      'non-floating point enrichment value "{}"'\
                      .format(self._id, enrichment)
                raise ValueError(msg)

            elif element != 'U':
                msg = 'Unable to use enrichment for element {} which is not ' \
                      'uranium for Material ID="{}"'.format(element, self._id)
                raise ValueError(msg)

            # Check that the enrichment is in the valid range
            cv.check_less_than('enrichment', enrichment, 100. / 1.008)
            cv.check_greater_than('enrichment', enrichment, 0., equality=True)

            if enrichment > 5.0:
                msg = 'A uranium enrichment of {} was given for Material ID='\
                      '"{}". OpenMC assumes the U234/U235 mass ratio is '\
                      'constant at 0.008, which is only valid at low ' \
                      'enrichments. Consider setting the isotopic ' \
                      'composition manually for enrichments over 5%.'.\
                      format(enrichment, self._id)
                warnings.warn(msg)

        # Add naturally-occuring isotopes
        element = openmc.Element(element)
        for nuclide in element.expand(percent, percent_type, enrichment,
                                      enrichment_target, enrichment_type):
            self.add_nuclide(*nuclide)
Exemple #54
0
 def theta(self, theta):
     cv.check_type('Maxwell temperature', theta, Real)
     cv.check_greater_than('Maxwell temperature', theta, 0.0)
     self._theta = theta
Exemple #55
0
 def energy(self, energy):
     cv.check_type('decay energy', energy, UFloat)
     cv.check_greater_than('decay energy', energy.nominal_value, 0.0, True)
     cv.check_greater_than('decay energy uncertainty', energy.std_dev, 0.0,
                           True)
     self._energy = energy
Exemple #56
0
 def a(self, a):
     cv.check_type('Watt a', a, Real)
     cv.check_greater_than('Watt a', a, 0.0)
     self._a = a
Exemple #57
0
 def n_particles(self, n_particles):
     name = 'N-body phase space number of particles'
     cv.check_type(name, n_particles, Integral)
     cv.check_greater_than(name, n_particles, 0)
     self._n_particles = n_particles
Exemple #58
0
 def b(self, b):
     cv.check_type('Watt b', b, Real)
     cv.check_greater_than('Watt b', b, 0.0)
     self._b = b
Exemple #59
0
    def expand(self,
               percent,
               percent_type,
               enrichment=None,
               enrichment_target=None,
               enrichment_type=None,
               cross_sections=None):
        """Expand natural element into its naturally-occurring isotopes.

        An optional cross_sections argument or the :envvar:`OPENMC_CROSS_SECTIONS`
        environment variable is used to specify a cross_sections.xml file.
        If the cross_sections.xml file is found, the element is expanded only
        into the isotopes/nuclides present in cross_sections.xml. If no
        cross_sections.xml file is found, the element is expanded based on its
        naturally occurring isotopes.

        Parameters
        ----------
        percent : float
            Atom or weight percent
        percent_type : {'ao', 'wo'}
            'ao' for atom percent and 'wo' for weight percent
        enrichment : float, optional
            Enrichment of an enrichment_taget nuclide in percent (ao or wo).
            If enrichment_taget is not supplied then it is enrichment for U235
            in weight percent. For example, input 4.95 for 4.95 weight percent
            enriched U. Default is None (natural composition).
        enrichment_target: str, optional
            Single nuclide name to enrich from a natural composition (e.g., 'O16')

            .. versionadded:: 0.12
        enrichment_type: {'ao', 'wo'}, optional
            'ao' for enrichment as atom percent and 'wo' for weight percent.
            Default is: 'ao' for two-isotope enrichment; 'wo' for U enrichment

            .. versionadded:: 0.12
        cross_sections : str, optional
            Location of cross_sections.xml file. Default is None.

        Returns
        -------
        isotopes : list
            Naturally-occurring isotopes of the element. Each item of the list
            is a tuple consisting of a nuclide string, the atom/weight percent,
            and the string 'ao' or 'wo'.

        Raises
        ------
        ValueError
            No data is available for any of natural isotopes of the element
        ValueError
            If only some natural isotopes are available in the cross-section data
            library and the element is not O, W, or Ta
        ValueError
            If a non-naturally-occurring isotope is requested
        ValueError
            If enrichment is requested of an element with more than two
            naturally-occurring isotopes.
        ValueError
            If enrichment procedure for Uranium is used when element is not
            Uranium.
        ValueError
            Uranium enrichment is requested with enrichment_type=='ao'

        Notes
        -----
        When the `enrichment` argument is specified, a correlation from
        `ORNL/CSD/TM-244 <https://doi.org/10.2172/5561567>`_ is used to
        calculate the weight fractions of U234, U235, U236, and U238. Namely,
        the weight fraction of U234 and U236 are taken to be 0.89% and 0.46%,
        respectively, of the U235 weight fraction. The remainder of the
        isotopic weight is assigned to U238.

        When the `enrichment` argument is specified with `enrichment_target`, a
        general enrichment procedure is used for elements composed of exactly
        two naturally-occurring isotopes. `enrichment` is interpreted as atom
        percent by default but can be controlled by the `enrichment_type`
        argument.

        """
        # Check input
        if enrichment_type is not None:
            cv.check_value('enrichment_type', enrichment_type, {'ao', 'wo'})

        if enrichment is not None:
            cv.check_less_than('enrichment', enrichment, 100.0, equality=True)
            cv.check_greater_than('enrichment', enrichment, 0., equality=True)

        # Get the nuclides present in nature
        natural_nuclides = {name for name, abundance in natural_isotopes(self)}

        # Create dict to store the expanded nuclides and abundances
        abundances = OrderedDict()

        # If cross_sections is None, get the cross sections from the
        # OPENMC_CROSS_SECTIONS environment variable
        if cross_sections is None:
            cross_sections = os.environ.get('OPENMC_CROSS_SECTIONS')

        # If a cross_sections library is present, check natural nuclides
        # against the nuclides in the library
        if cross_sections is not None:
            library_nuclides = set()
            tree = ET.parse(cross_sections)
            root = tree.getroot()
            for child in root.findall('library'):
                nuclide = child.attrib['materials']
                if re.match(r'{}\d+'.format(self), nuclide) and \
                   '_m' not in nuclide:
                    library_nuclides.add(nuclide)

            # Get a set of the mutual and absent nuclides. Convert to lists
            # and sort to avoid different ordering between Python 2 and 3.
            mutual_nuclides = natural_nuclides.intersection(library_nuclides)
            absent_nuclides = natural_nuclides.difference(mutual_nuclides)
            mutual_nuclides = sorted(list(mutual_nuclides))
            absent_nuclides = sorted(list(absent_nuclides))

            # If all natural nuclides are present in the library,
            # expand element using all natural nuclides
            if len(absent_nuclides) == 0:
                for nuclide in mutual_nuclides:
                    abundances[nuclide] = NATURAL_ABUNDANCE[nuclide]

            # If no natural elements are present in the library, check if the
            # 0 nuclide is present. If so, set the abundance to 1 for this
            # nuclide. Else, raise an error.
            elif len(mutual_nuclides) == 0:
                nuclide_0 = self + '0'
                if nuclide_0 in library_nuclides:
                    abundances[nuclide_0] = 1.0
                else:
                    msg = 'Unable to expand element {0} because the cross '\
                          'section library provided does not contain any of '\
                          'the natural isotopes for that element.'\
                          .format(self)
                    raise ValueError(msg)

            # If some, but not all, natural nuclides are in the library, add
            # the mutual nuclides. For the absent nuclides, add them based on
            # our knowledge of the common cross section libraries
            # (ENDF, JEFF, and JENDL)
            else:
                # Add the mutual isotopes
                for nuclide in mutual_nuclides:
                    abundances[nuclide] = NATURAL_ABUNDANCE[nuclide]

                # Adjust the abundances for the absent nuclides
                for nuclide in absent_nuclides:
                    if nuclide in ['O17', 'O18'] and 'O16' in mutual_nuclides:
                        abundances['O16'] += NATURAL_ABUNDANCE[nuclide]
                    elif nuclide == 'Ta180' and 'Ta181' in mutual_nuclides:
                        abundances['Ta181'] += NATURAL_ABUNDANCE[nuclide]
                    elif nuclide == 'W180' and 'W182' in mutual_nuclides:
                        abundances['W182'] += NATURAL_ABUNDANCE[nuclide]
                    else:
                        msg = 'Unsure how to partition natural abundance of ' \
                              'isotope {0} into other natural isotopes of ' \
                              'this element that are present in the cross ' \
                              'section library provided. Consider adding ' \
                              'the isotopes of this element individually.'
                        raise ValueError(msg)

        # If a cross_section library is not present, expand the element into
        # its natural nuclides
        else:
            for nuclide in natural_nuclides:
                abundances[nuclide] = NATURAL_ABUNDANCE[nuclide]

        # Modify mole fractions if enrichment provided
        # Old treatment for Uranium
        if enrichment is not None and enrichment_target is None:

            # Check that the element is Uranium
            if self.name != 'U':
                msg = ('Enrichment procedure for Uranium was requested, '
                       'but the isotope is {} not U'.format(self))
                raise ValueError(msg)

            # Check that enrichment_type is not 'ao'
            if enrichment_type == 'ao':
                msg = ('Enrichment procedure for Uranium requires that '
                       'enrichment value is provided as wo%.')
                raise ValueError(msg)

            # Calculate the mass fractions of isotopes
            abundances['U234'] = 0.0089 * enrichment
            abundances['U235'] = enrichment
            abundances['U236'] = 0.0046 * enrichment
            abundances['U238'] = 100.0 - 1.0135 * enrichment

            # Convert the mass fractions to mole fractions
            for nuclide in abundances.keys():
                abundances[nuclide] /= atomic_mass(nuclide)

            # Normalize the mole fractions to one
            sum_abundances = sum(abundances.values())
            for nuclide in abundances.keys():
                abundances[nuclide] /= sum_abundances

        # Modify mole fractions if enrichment provided
        # New treatment for arbitrary element
        elif enrichment is not None and enrichment_target is not None:

            # Provide more informative error message for U235
            if enrichment_target == 'U235':
                msg = ("There is a special procedure for enrichment of U235 "
                       "in U. To invoke it, the arguments 'enrichment_target'"
                       "and 'enrichment_type' should be omitted. Provide "
                       "a value only for 'enrichment' in weight percent.")
                raise ValueError(msg)

            # Check if it is two-isotope mixture
            if len(abundances) != 2:
                msg = (
                    'Element {} does not consist of two naturally-occurring '
                    'isotopes. Please enter isotopic abundances manually.'.
                    format(self))
                raise ValueError(msg)

            # Check if the target nuclide is present in the mixture
            if enrichment_target not in abundances:
                msg = (
                    'The target nuclide {} is not one of the naturally-occurring '
                    'isotopes ({})'.format(enrichment_target,
                                           list(abundances)))
                raise ValueError(msg)

            # If weight percent enrichment is requested convert to mass fractions
            if enrichment_type == 'wo':
                # Convert the atomic abundances to weight fractions
                # Compute the element atomic mass
                element_am = sum(
                    atomic_mass(nuc) * abundances[nuc] for nuc in abundances)

                # Convert Molar Fractions to mass fractions
                for nuclide in abundances:
                    abundances[nuclide] *= atomic_mass(nuclide) / element_am

                # Normalize to one
                sum_abundances = sum(abundances.values())
                for nuclide in abundances:
                    abundances[nuclide] /= sum_abundances

            # Enrich the mixture
            # The procedure is more generic that it needs to be. It allows
            # to enrich mixtures of more then 2 isotopes, keeping the ratios
            # of non-enriched nuclides the same as in natural composition

            # Get fraction of non-enriched isotopes in nat. composition
            non_enriched = 1.0 - abundances[enrichment_target]
            tail_fraction = 1.0 - enrichment / 100.0

            # Enrich all nuclides
            # Do bogus operation for enrichment target but overwrite immediatly
            # to avoid if statement in the loop
            for nuclide, fraction in abundances.items():
                abundances[nuclide] = tail_fraction * fraction / non_enriched
            abundances[enrichment_target] = enrichment / 100.0

            # Convert back to atomic fractions if requested
            if enrichment_type == 'wo':
                # Convert the mass fractions to mole fractions
                for nuclide in abundances:
                    abundances[nuclide] /= atomic_mass(nuclide)

                # Normalize the mole fractions to one
                sum_abundances = sum(abundances.values())
                for nuclide in abundances:
                    abundances[nuclide] /= sum_abundances

        # Compute the ratio of the nuclide atomic masses to the element
        # atomic mass
        if percent_type == 'wo':

            # Compute the element atomic mass
            element_am = 0.
            for nuclide in abundances.keys():
                element_am += atomic_mass(nuclide) * abundances[nuclide]

            # Convert the molar fractions to mass fractions
            for nuclide in abundances.keys():
                abundances[nuclide] *= atomic_mass(nuclide) / element_am

            # Normalize the mass fractions to one
            sum_abundances = sum(abundances.values())
            for nuclide in abundances.keys():
                abundances[nuclide] /= sum_abundances

        # Create a list of the isotopes in this element
        isotopes = []
        for nuclide, abundance in abundances.items():
            isotopes.append((nuclide, percent * abundance, percent_type))

        return isotopes
Exemple #60
0
 def level(self, plot_level):
     cv.check_type('plot level', plot_level, Integral)
     cv.check_greater_than('plot level', plot_level, 0, equality=True)
     self._level = plot_level