diff --git a/.travis.yml b/.travis.yml index 6aed183a0..46f5e91bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,10 +41,10 @@ install: true before_script: - cd data - - git clone --branch=master git://github.com/bhermanmit/nndc_xs nndc_xs - - cat nndc_xs/nndc.tar.gza* | tar xzvf - - - rm -rf nndc_xs - - export OPENMC_CROSS_SECTIONS=$PWD/nndc/cross_sections.xml + - git clone --branch=master git://github.com/paulromano/nndc-hdf5 + - cat nndc-hdf5/nndc_hdf5.tar.xz? | tar xJvf - + - rm -rf nndc-hdf5 + - export OPENMC_CROSS_SECTIONS=$PWD/nndc_hdf5/cross_sections.xml - git clone --branch=master git://github.com/smharper/windowed_multipole_library.git wmp_lib - tar xzvf wmp_lib/multipole_lib.tar.gz - export OPENMC_MULTIPOLE_LIBRARY=$PWD/multipole_lib diff --git a/data/convert_hdf5.py b/data/convert_hdf5.py new file mode 100644 index 000000000..3f5fcfdb7 --- /dev/null +++ b/data/convert_hdf5.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python + +import glob +import os +from xml.dom.minidom import getDOMImplementation + +import openmc.data.ace + + +if not os.path.isdir('nndc_hdf5'): + os.mkdir('nndc_hdf5') + +nndc_files = glob.glob('nndc/293.6K/*.ace') +nndc_thermal_files = glob.glob('nndc/tsl/*.acer') + +thermal_names = {'al': 'c_Al27', + 'be': 'c_Be', + 'bebeo': 'c_Be_in_BeO', + 'benzine': 'c_Benzine', + 'dd2o': 'c_D_in_D2O', + 'fe': 'c_Fe56', + 'graphite': 'c_Graphite', + 'hch2': 'c_H_in_CH2', + 'hh2o': 'c_H_in_H2O', + 'hzrh': 'c_H_in_ZrH', + 'lch4': 'c_liquid_CH4', + 'obeo': 'c_O_in_BeO', + 'orthod': 'c_ortho_D', + 'orthoh': 'c_ortho_H', + 'ouo2': 'c_O_in_UO2', + 'parad': 'c_para_D', + 'parah': 'c_para_H', + 'sch4': 'c_solid_CH4', + 'uuo2': 'c_U_in_UO2', + 'zrzrh': 'c_Zr_in_ZrH'} + +impl = getDOMImplementation() +doc = impl.createDocument(None, "cross_sections", None) +doc_root = doc.documentElement + +for f in sorted(nndc_files): + print('Converting {}...'.format(f)) + + # Deterine output file name + dirname, basename = os.path.split(f) + root, ext = os.path.splitext(basename) + outfile = os.path.join('nndc_hdf5', root + '.h5') + if os.path.exists(outfile): + os.remove(outfile) + + # Determine elemental symbol, mass number and metastable state + element, mass_number, temp = basename.split('_') + metastable = int(mass_number[-1]) if 'm' in mass_number else 0 + mass_number = int(mass_number[:3]) + + # Parse ACE file, create HDF5 file + t = openmc.data.ace.get_table(f) + t.export_to_hdf5(outfile, element, mass_number, metastable) + xs = t.name.split('.')[1] + if metastable > 0: + name = "{}{}_m{}.{}".format(element, mass_number, metastable, xs) + else: + name = "{}{}.{}".format(element, mass_number, xs) + + # Add entry to XML listing + libraryNode = doc.createElement("library") + libraryNode.setAttribute("path", root + '.h5') + libraryNode.setAttribute("materials", name) + libraryNode.setAttribute("type", "neutron") + doc_root.appendChild(libraryNode) + +for f in sorted(nndc_thermal_files): + print('Converting {}...'.format(f)) + + # Deterine output file name + dirname, basename = os.path.split(f) + root, ext = os.path.splitext(basename) + outfile = os.path.join('nndc_hdf5', root + '.h5') + if os.path.exists(outfile): + os.remove(outfile) + + # Parse ACE file, create HDF5 file + t = openmc.data.ace.get_table(f) + t.export_to_hdf5(outfile, thermal_names[root]) + xs = t.name.split('.')[1] + + # Add entry to XML listing + libraryNode = doc.createElement("library") + libraryNode.setAttribute("path", root + '.h5') + libraryNode.setAttribute("materials", thermal_names[root] + '.' + xs) + libraryNode.setAttribute("type", "thermal") + doc_root.appendChild(libraryNode) + +# Write cross_sections.xml +lines = doc.toprettyxml(indent=' ') +open(os.path.join('nndc_hdf5', 'cross_sections.xml'), 'w').write(lines) diff --git a/data/get_nndc_data.py b/data/get_nndc_data.py index b9b855a81..844abea81 100755 --- a/data/get_nndc_data.py +++ b/data/get_nndc_data.py @@ -22,7 +22,7 @@ except ImportError: cwd = os.getcwd() sys.path.insert(0, os.path.join(cwd, '..')) -from openmc.ace import ascii_to_binary +from openmc.data.ace import ascii_to_binary baseUrl = 'http://www.nndc.bnl.gov/endf/b7.1/aceFiles/' files = ['ENDF-B-VII.1-neutron-293.6K.tar.gz', diff --git a/docs/source/_static/theme_overrides.css b/docs/source/_static/theme_overrides.css index 7c1a52022..bee03f415 100644 --- a/docs/source/_static/theme_overrides.css +++ b/docs/source/_static/theme_overrides.css @@ -8,3 +8,11 @@ max-width: 100%; overflow: visible; } + +.wy-plain-list-disc, .rst-content .section ul, .rst-content .toctree-wrapper ul, article ul { + margin-bottom: 0px; +} + +.wy-table, .rst-content table.docutils, .rst-content table.field-list { + margin-bottom: 0px; +} diff --git a/docs/source/conf.py b/docs/source/conf.py index 38661cdb3..c9299b424 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -24,7 +24,8 @@ except ImportError: from mock import Mock as MagicMock -MOCK_MODULES = ['numpy', 'h5py', 'pandas', 'opencg'] +MOCK_MODULES = ['numpy', 'numpy.polynomial', 'numpy.polynomial.polynomial', + 'h5py', 'pandas', 'opencg'] sys.modules.update((mod_name, MagicMock()) for mod_name in MOCK_MODULES) diff --git a/docs/source/io_formats/index.rst b/docs/source/io_formats/index.rst index 905cd26cc..acab7e893 100644 --- a/docs/source/io_formats/index.rst +++ b/docs/source/io_formats/index.rst @@ -4,12 +4,26 @@ File Format Specifications ========================== +---------- +Data Files +---------- + .. toctree:: :numbered: - :maxdepth: 3 + :maxdepth: 2 - data_wmp + nuclear_data mgxs_library + data_wmp + +------------ +Output Files +------------ + +.. toctree:: + :numbered: + :maxdepth: 2 + statepoint source summary diff --git a/docs/source/io_formats/nuclear_data.rst b/docs/source/io_formats/nuclear_data.rst new file mode 100644 index 000000000..e7d4f2d32 --- /dev/null +++ b/docs/source/io_formats/nuclear_data.rst @@ -0,0 +1,353 @@ +.. _usersguide_nuclear_data: + +======================== +Nuclear Data File Format +======================== + +--------------------- +Incident Neutron Data +--------------------- + + +**//** + +:Attributes: - **Z** (*int*) -- Atomic number + - **A** (*int*) -- Mass number + - **metastable** (*int*) -- Metastable state + - **atomic_weight_ratio** (*double*) -- Mass in units of neutron masses + - **temperature** (*double*) -- Temperature in MeV + - **n_reaction** (*int*) -- Number of reactions + +:Datasets: - **energy** (*double[]*) -- Energy points at which cross sections are tabulated + +**//reaction_/** + +:Attributes: - **mt** (*int*) -- ENDF MT reaction number + - **label** (*char[]*) -- Name of the reaction + - **Q_value** (*double*) -- Q value in MeV + - **threshold_idx** (*int*) -- Index on the energy grid that the + reaction threshold corresponds to + - **center_of_mass** (*int*) -- Whether the reference frame for + scattering is center-of-mass (1) or laboratory (0) + - **n_product** (*int*) -- Number of reaction products + +:Datasets: - **xs** (*double[]*) -- Cross section values tabulated against the nuclide energy grid + +**//reaction_/product_/** + + Reaction product data is described in :ref:`product`. + +**//urr** + +:Attributes: - **interpolation** (*int*) -- interpolation scheme + - **inelastic** (*int*) -- flag indicating inelastic scattering + - **other_absorb** (*int*) -- flag indicating other absorption + - **factors** (*int*) -- flag indicating whether tables are + absolute or multipliers + +:Datasets: - **energy** (*double[]*) -- Energy at which probability tables exist + - **table** (*double[][][]*) -- Probability tables + +**//total_nu/** + + This special product is used to define the total number of neutrons produced + from fission. It is formatted as a reaction product, described in + :ref:`product`. + +------------------------------- +Thermal Neutron Scattering Data +------------------------------- + +**//** + +:Attributes: - **atomic_weight_ratio** (*double*) -- Mass in units of neutron masses + - **temperature** (*double*) -- Temperature in MeV + - **zaids** (*int[]*) -- ZAID identifiers for which the thermal + scattering data applies to + +**//elastic/** + +:Datasets: - **xs** (:ref:`tabulated <1d_tabulated>`) -- Thermal inelastic + scattering cross section + - **mu_out** (*double[][]*) -- Distribution of outgoing energies + and angles for coherent elastic scattering + +**//inelastic/** + +:Attributes: + - **secondary_mode** (*char[]*) -- Indicates how the inelastic + outgoing angle-energy distributions are represented ('equal', + 'skewed', or 'continuous'). + +:Datasets: - **xs** (:ref:`tabulated <1d_tabulated>`) -- Thermal inelastic + scattering cross section + - **energy_out** (*double[][]*) -- Distribution of outgoing + energies for each incoming energy. Only present if secondary mode + is not continuous. + - **mu_out** (*double[][][]*) -- Distribution of scattering cosines + for each pair of incoming and outgoing energies. Only present if + secondary mode is not continuous. + +If the secondary mode is continuous, the outgoing energy-angle distribution is +given as a :ref:`correlated angle-energy distribution +`. + +.. _product: + +----------------- +Reaction Products +----------------- + +:Object type: Group +:Attributes: - **particle** (*char[]*) -- Type of particle + - **emission_mode** (*char[]*) -- Emission mode (prompt, delayed, + total) + - **decay_rate** (*double*) -- Rate of decay in inverse seconds + - **n_distribution** (*int*) -- Number of angle/energy + distributions +:Datasets: + - **yield** (:ref:`function <1d_functions>`) -- Energy-dependent + yield of the product. + +:Groups: + - **distribution_** -- Formats for angle-energy distributions are + detailed in :ref:`angle_energy`. When multiple angle-energy + distributions occur, one dataset also may appear for each + distribution: + + :Datasets: + - **applicability** (:ref:`function <1d_functions>`) -- + Probability of selecting this distribution as a function + of incident energy + +.. _1d_functions: + +------------------------- +One-dimensional Functions +------------------------- + +Scalar +------ + +:Object type: Dataset +:Datatype: *double* +:Attributes: - **type** (*char[]*) -- 'constant' + +.. _1d_tabulated: + +Tabulated +--------- + +:Object type: Dataset +:Datatype: *double[2][]* +:Description: x-values are listed first followed by corresponding y-values +:Attributes: - **type** (*char[]*) -- 'tabulated' + - **breakpoints** (*int[]*) -- Region breakpoints + - **interpolation** (*int[]*) -- Region interpolation codes + +Polynomial +---------- + +:Object type: Dataset +:Datatype: *double[]* +:Description: Polynomial coefficients listed in order of increasing power +:Attributes: - **type** (*char[]*) -- 'polynomial' + +Coherent elastic scattering +--------------------------- + +:Object type: Dataset +:Datatype: *double[2][]* +:Description: The first row lists Bragg edges and the second row lists structure + factor cumulative sums. +:Attributes: - **type** (*char[]*) -- 'bragg' + +.. _angle_energy: + +-------------------------- +Angle-Energy Distributions +-------------------------- + +Uncorrelated Angle-Energy +------------------------- + +:Object type: Group +:Attributes: - **type** (*char[]*) -- 'uncorrelated' +:Datasets: - **angle/energy** (*double[]*) -- energies at which angle distributions exist + - **angle/mu** (*double[3][]*) -- tabulated angular distributions for + each energy. The first row gives :math:`\mu` values, the second row + gives the probability density, and the third row gives the + cumulative distribution. + + :Attributes: - **offsets** (*int[]*) -- indices indicating where + each angular distribution starts + - **interpolation** (*int[]*) -- interpolation code + for each angular distribution + +:Groups: - **energy/** (:ref:`energy distribution `) + +.. _correlated_angle_energy: + +Correlated Angle-Energy +----------------------- + +:Object type: Group +:Attributes: - **type** (*char[]*) -- 'correlated' +:Datasets: - **energy** (*double[]*) -- Incoming energies at which distributions exist + + :Attributes: + - **interpolation** (*double[2][]*) -- Breakpoints and + interpolation codes for incoming energy regions + + - **energy_out** (*double[5][]*) -- Distribution of outgoing energies + corresponding to each incoming energy. The distributions are + flattened into a single array; the start of a given distribution + can be determined using the ``offsets`` attribute. The first row + gives outgoing energies, the second row gives the probability + density, the third row gives the cumulative distribution, the + fourth row gives interpolation codes for angular distributions, and + the fifth row gives offsets for angular distributions. + + :Attributes: - **offsets** (*double[]*) -- Offset for each + distribution + - **interpolation** (*int[]*) -- Interpolation code + for each distribution + - **n_discrete_lines** (*int[]*) -- Number of discrete + lines in each distribution + + - **mu** (*double[3][]*) -- Distribution of angular cosines + corresponding to each pair of incoming and outgoing energies. The + distributions are flattened into a single array; the start of a + given distribution can be determined using offsets in the fifth row + of the ``energy_out`` dataset. The first row gives angular cosines, + the second row gives the probability density, and the third row + gives the cumulative distribution. + +Kalbach-Mann +------------ + +:Object type: Group +:Attributes: - **type** (*char[]*) -- 'kalbach-mann' +:Datasets: - **energy** (*double[]*) -- Incoming energies at which distributions exist + + :Attributes: + - **interpolation** (*double[2][]*) -- Breakpoints and + interpolation codes for incoming energy regions + + - **distribution** (*double[5][]*) -- Distribution of outgoing + energies and angles corresponding to each incoming energy. The + distributions are flattened into a single array; the start of a + given distribution can be determined using the ``offsets`` + attribute. The first row gives outgoing energies, the second row + gives the probability density, the third row gives the cumulative + distribution, the fourth row gives Kalbach-Mann precompound + factors, and the fifth row gives Kalbach-Mann angular distribution + slopes. + + :Attributes: - **offsets** (*double[]*) -- Offset for each + distribution + - **interpolation** (*int[]*) -- Interpolation code + for each distribution + - **n_discrete_lines** (*int[]*) -- Number of discrete + lines in each distribution + +N-Body Phase Space +------------------ + +:Object type: Group +:Attributes: - **type** (*char[]*) -- 'nbody' + - **total_mass** (*double*) -- Total mass of product particles + - **n_particles** (*int*) -- Number of product particles + - **atomic_weight_ratio** (*double*) -- Atomic weight ratio of the + target nuclide in neutron masses + - **q_value** (*double*) -- Q value for the reaction in MeV + +.. _energy_distribution: + +-------------------- +Energy Distributions +-------------------- + +Maxwell +------- + +:Object type: Group +:Attributes: - **type** (*char[]*) -- 'maxwell' + - **u** (*double*) -- Restriction energy in MeV +:Datasets: + - **theta** (:ref:`tabulated <1d_tabulated>`) -- Maxwellian + temperature as a function of energy + +Evaporation +----------- + +:Object type: Group +:Attributes: - **type** (*char[]*) -- 'evaporation' + - **u** (*double*) -- Restriction energy in MeV +:Datasets: + - **theta** (:ref:`tabulated <1d_tabulated>`) -- Evaporation + temperature as a function of energy + +Watt Fission Spectrum +--------------------- + +:Object type: Group +:Attributes: - **type** (*char[]*) -- 'watt' + - **u** (*double*) -- Restriction energy in MeV +:Datasets: - **a** (:ref:`tabulated <1d_tabulated>`) -- Watt parameter :math:`a` + as a function of incident energy + - **b** (:ref:`tabulated <1d_tabulated>`) -- Watt parameter :math:`b` + as a function of incident energy + +Madland-Nix +----------- + +:Object type: Group +:Attributes: - **type** (*char[]*) -- 'watt' + - **efl** (*double*) -- Average energy of light fragment in eV + - **efh** (*double*) -- Average energy of heavy fragment in eV + +Discrete Photon +--------------- + +:Object type: Group +:Attributes: - **type** (*char[]*) -- 'discrete_photon' + - **primary_flag** (*int*) -- Whether photon is a primary + - **energy** (*double*) -- Photon energy in MeV + - **atomic_weight_ratio** (*double*) -- Atomic weight ratio of + target nuclide in neutron masses + +Level Inelastic +--------------- + +:Object type: Group +:Attributes: - **type** (*char[]*) -- 'level' + - **threshold** (*double*) -- Energy threshold in the laboratory + system in MeV + - **mass_ratio** (*double*) -- :math:`(A/(A + 1))^2` + +Continuous Tabular +------------------ + +:Object type: Group +:Attributes: - **type** (*char[]*) -- 'continuous' +:Datasets: - **energy** (*double[]*) -- Incoming energies at which distributions exist + + :Attributes: + - **interpolation** (*double[2][]*) -- Breakpoints and + interpolation codes for incoming energy regions + + - **distribution** (*double[3][]*) -- Distribution of outgoing + energies corresponding to each incoming energy. The distributions + are flattened into a single array; the start of a given + distribution can be determined using the ``offsets`` attribute. The + first row gives outgoing energies, the second row gives the + probability density, and the third row gives the cumulative + distribution. + + :Attributes: - **offsets** (*double[]*) -- Offset for each + distribution + - **interpolation** (*int[]*) -- Interpolation code + for each distribution + - **n_discrete_lines** (*int[]*) -- Number of discrete + lines in each distribution diff --git a/docs/source/pythonapi/index.rst b/docs/source/pythonapi/index.rst index 09b3d5135..a17af8ac6 100644 --- a/docs/source/pythonapi/index.rst +++ b/docs/source/pythonapi/index.rst @@ -35,9 +35,6 @@ Example Jupyter Notebooks Handling nuclear data --------------------- -Classes -+++++++ - .. autosummary:: :toctree: generated :nosignatures: @@ -46,14 +43,6 @@ Classes openmc.XSdata openmc.MGXSLibrary -Functions -+++++++++ - -.. autosummary:: - :toctree: generated - :nosignatures: - - openmc.ace.ascii_to_binary Simulation Settings ------------------- @@ -224,6 +213,8 @@ Univariate Probability Distributions openmc.stats.Maxwell openmc.stats.Watt openmc.stats.Tabular + openmc.stats.Legendre + openmc.stats.Mixture Angular Distributions --------------------- @@ -325,6 +316,74 @@ Functions openmc.model.create_triso_lattice +-------------------------------------------- +:mod:`openmc.data` -- Nuclear Data Interface +-------------------------------------------- + +Core Classes +------------ + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myclass.rst + + openmc.data.Product + openmc.data.Tabulated1D + openmc.data.CoherentElastic + +Angle-Energy Distributions +-------------------------- + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myclass.rst + + openmc.data.AngleEnergy + openmc.data.KalbachMann + openmc.data.CorrelatedAngleEnergy + openmc.data.UncorrelatedAngleEnergy + openmc.data.NBodyPhaseSpace + openmc.data.AngleDistribution + openmc.data.EnergyDistribution + openmc.data.ArbitraryTabulated + openmc.data.GeneralEvaporation + openmc.data.MaxwellEnergy + openmc.data.Evaporation + openmc.data.WattEnergy + openmc.data.MadlandNix + openmc.data.DiscretePhoton + openmc.data.LevelInelastic + openmc.data.ContinuousTabular + +ACE Format +---------- + +Classes ++++++++ + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myclass.rst + + openmc.data.ace.Library + openmc.data.ace.Table + openmc.data.ace.NeutronTable + openmc.data.ace.SabTable + openmc.data.ace.PhotoatomicTable + openmc.data.ace.PhotonuclearTable + openmc.data.ace.Reaction + +Functions ++++++++++ + +.. autosummary:: + :toctree: generated + :nosignatures: + + openmc.data.ace.ascii_to_binary .. _Jupyter: https://jupyter.org/ .. _NumPy: http://www.numpy.org/ diff --git a/examples/python/basic/build-xml.py b/examples/python/basic/build-xml.py index 81aecc9f9..1bfe50b2e 100644 --- a/examples/python/basic/build-xml.py +++ b/examples/python/basic/build-xml.py @@ -16,16 +16,16 @@ particles = 10000 ############################################################################### # Instantiate some Nuclides -h1 = openmc.Nuclide('H-1') -o16 = openmc.Nuclide('O-16') -u235 = openmc.Nuclide('U-235') +h1 = openmc.Nuclide('H1') +o16 = openmc.Nuclide('O16') +u235 = openmc.Nuclide('U235') # Instantiate some Materials and register the appropriate Nuclides moderator = openmc.Material(material_id=41, name='moderator') moderator.set_density('g/cc', 1.0) moderator.add_nuclide(h1, 2.) moderator.add_nuclide(o16, 1.) -moderator.add_s_alpha_beta('HH2O', '71t') +moderator.add_s_alpha_beta('c_H_in_H2O', '71t') fuel = openmc.Material(material_id=40, name='fuel') fuel.set_density('g/cc', 4.5) diff --git a/examples/python/boxes/build-xml.py b/examples/python/boxes/build-xml.py index 4be33dcf1..3eed4059c 100644 --- a/examples/python/boxes/build-xml.py +++ b/examples/python/boxes/build-xml.py @@ -16,10 +16,10 @@ particles = 10000 ############################################################################### # Instantiate some Nuclides -h1 = openmc.Nuclide('H-1') -o16 = openmc.Nuclide('O-16') -u235 = openmc.Nuclide('U-235') -u238 = openmc.Nuclide('U-238') +h1 = openmc.Nuclide('H1') +o16 = openmc.Nuclide('O16') +u235 = openmc.Nuclide('U235') +u238 = openmc.Nuclide('U238') # Instantiate some Materials and register the appropriate Nuclides fuel1 = openmc.Material(material_id=1, name='fuel') @@ -34,7 +34,7 @@ moderator = openmc.Material(material_id=3, name='moderator') moderator.set_density('g/cc', 1.0) moderator.add_nuclide(h1, 2.) moderator.add_nuclide(o16, 1.) -moderator.add_s_alpha_beta('HH2O', '71t') +moderator.add_s_alpha_beta('c_H_in_H2O', '71t') # Instantiate a Materials collection and export to XML materials_file = openmc.Materials([fuel1, fuel2, moderator]) diff --git a/examples/python/lattice/hexagonal/build-xml.py b/examples/python/lattice/hexagonal/build-xml.py index 05cb2cb01..ba2cac367 100644 --- a/examples/python/lattice/hexagonal/build-xml.py +++ b/examples/python/lattice/hexagonal/build-xml.py @@ -15,10 +15,10 @@ particles = 10000 ############################################################################### # Instantiate some Nuclides -h1 = openmc.Nuclide('H-1') -o16 = openmc.Nuclide('O-16') -u235 = openmc.Nuclide('U-235') -fe56 = openmc.Nuclide('Fe-56') +h1 = openmc.Nuclide('H1') +o16 = openmc.Nuclide('O16') +u235 = openmc.Nuclide('U235') +fe56 = openmc.Nuclide('Fe56') # Instantiate some Materials and register the appropriate Nuclides fuel = openmc.Material(material_id=1, name='fuel') @@ -29,7 +29,7 @@ moderator = openmc.Material(material_id=2, name='moderator') moderator.set_density('g/cc', 1.0) moderator.add_nuclide(h1, 2.) moderator.add_nuclide(o16, 1.) -moderator.add_s_alpha_beta('HH2O', '71t') +moderator.add_s_alpha_beta('c_H_in_H2O', '71t') iron = openmc.Material(material_id=3, name='iron') iron.set_density('g/cc', 7.9) diff --git a/examples/python/lattice/nested/build-xml.py b/examples/python/lattice/nested/build-xml.py index 03cede9dc..edf3ad7b1 100644 --- a/examples/python/lattice/nested/build-xml.py +++ b/examples/python/lattice/nested/build-xml.py @@ -15,9 +15,9 @@ particles = 10000 ############################################################################### # Instantiate some Nuclides -h1 = openmc.Nuclide('H-1') -o16 = openmc.Nuclide('O-16') -u235 = openmc.Nuclide('U-235') +h1 = openmc.Nuclide('H1') +o16 = openmc.Nuclide('O16') +u235 = openmc.Nuclide('U235') # Instantiate some Materials and register the appropriate Nuclides fuel = openmc.Material(material_id=1, name='fuel') @@ -28,7 +28,7 @@ moderator = openmc.Material(material_id=2, name='moderator') moderator.set_density('g/cc', 1.0) moderator.add_nuclide(h1, 2.) moderator.add_nuclide(o16, 1.) -moderator.add_s_alpha_beta('HH2O', '71t') +moderator.add_s_alpha_beta('c_H_in_H2O', '71t') # Instantiate a Materials collection and export to XML materials_file = openmc.Materials((moderator, fuel)) diff --git a/examples/python/lattice/simple/build-xml.py b/examples/python/lattice/simple/build-xml.py index 5a642d308..5ec1b7ee9 100644 --- a/examples/python/lattice/simple/build-xml.py +++ b/examples/python/lattice/simple/build-xml.py @@ -15,9 +15,9 @@ particles = 10000 ############################################################################### # Instantiate some Nuclides -h1 = openmc.Nuclide('H-1') -o16 = openmc.Nuclide('O-16') -u235 = openmc.Nuclide('U-235') +h1 = openmc.Nuclide('H1') +o16 = openmc.Nuclide('O16') +u235 = openmc.Nuclide('U235') # Instantiate some Materials and register the appropriate Nuclides fuel = openmc.Material(material_id=1, name='fuel') @@ -28,7 +28,7 @@ moderator = openmc.Material(material_id=2, name='moderator') moderator.set_density('g/cc', 1.0) moderator.add_nuclide(h1, 2.) moderator.add_nuclide(o16, 1.) -moderator.add_s_alpha_beta('HH2O', '71t') +moderator.add_s_alpha_beta('c_H_in_H2O', '71t') # Instantiate a Materials collection and export to XML materials_file = openmc.Materials([moderator, fuel]) diff --git a/examples/python/pincell/build-xml.py b/examples/python/pincell/build-xml.py index 0afb2527f..3bda05027 100644 --- a/examples/python/pincell/build-xml.py +++ b/examples/python/pincell/build-xml.py @@ -15,39 +15,39 @@ particles = 1000 ############################################################################### # Instantiate some Nuclides -h1 = openmc.Nuclide('H-1') -h2 = openmc.Nuclide('H-2') -he4 = openmc.Nuclide('He-4') -b10 = openmc.Nuclide('B-10') -b11 = openmc.Nuclide('B-11') -o16 = openmc.Nuclide('O-16') -o17 = openmc.Nuclide('O-17') -cr50 = openmc.Nuclide('Cr-50') -cr52 = openmc.Nuclide('Cr-52') -cr53 = openmc.Nuclide('Cr-53') -cr54 = openmc.Nuclide('Cr-54') -fe54 = openmc.Nuclide('Fe-54') -fe56 = openmc.Nuclide('Fe-56') -fe57 = openmc.Nuclide('Fe-57') -fe58 = openmc.Nuclide('Fe-58') -zr90 = openmc.Nuclide('Zr-90') -zr91 = openmc.Nuclide('Zr-91') -zr92 = openmc.Nuclide('Zr-92') -zr94 = openmc.Nuclide('Zr-94') -zr96 = openmc.Nuclide('Zr-96') -sn112 = openmc.Nuclide('Sn-112') -sn114 = openmc.Nuclide('Sn-114') -sn115 = openmc.Nuclide('Sn-115') -sn116 = openmc.Nuclide('Sn-116') -sn117 = openmc.Nuclide('Sn-117') -sn118 = openmc.Nuclide('Sn-118') -sn119 = openmc.Nuclide('Sn-119') -sn120 = openmc.Nuclide('Sn-120') -sn122 = openmc.Nuclide('Sn-122') -sn124 = openmc.Nuclide('Sn-124') -u234 = openmc.Nuclide('U-234') -u235 = openmc.Nuclide('U-235') -u238 = openmc.Nuclide('U-238') +h1 = openmc.Nuclide('H1') +h2 = openmc.Nuclide('H2') +he4 = openmc.Nuclide('He4') +b10 = openmc.Nuclide('B10') +b11 = openmc.Nuclide('B11') +o16 = openmc.Nuclide('O16') +o17 = openmc.Nuclide('O17') +cr50 = openmc.Nuclide('Cr50') +cr52 = openmc.Nuclide('Cr52') +cr53 = openmc.Nuclide('Cr53') +cr54 = openmc.Nuclide('Cr54') +fe54 = openmc.Nuclide('Fe54') +fe56 = openmc.Nuclide('Fe56') +fe57 = openmc.Nuclide('Fe57') +fe58 = openmc.Nuclide('Fe58') +zr90 = openmc.Nuclide('Zr90') +zr91 = openmc.Nuclide('Zr91') +zr92 = openmc.Nuclide('Zr92') +zr94 = openmc.Nuclide('Zr94') +zr96 = openmc.Nuclide('Zr96') +sn112 = openmc.Nuclide('Sn112') +sn114 = openmc.Nuclide('Sn114') +sn115 = openmc.Nuclide('Sn115') +sn116 = openmc.Nuclide('Sn116') +sn117 = openmc.Nuclide('Sn117') +sn118 = openmc.Nuclide('Sn118') +sn119 = openmc.Nuclide('Sn119') +sn120 = openmc.Nuclide('Sn120') +sn122 = openmc.Nuclide('Sn122') +sn124 = openmc.Nuclide('Sn124') +u234 = openmc.Nuclide('U234') +u235 = openmc.Nuclide('U235') +u238 = openmc.Nuclide('U238') # Instantiate some Materials and register the appropriate Nuclides uo2 = openmc.Material(material_id=1, name='UO2 fuel at 2.4% wt enrichment') @@ -98,7 +98,7 @@ borated_water.add_nuclide(h1, 4.9457e-2) borated_water.add_nuclide(h2, 7.4196e-6) borated_water.add_nuclide(o16, 2.4672e-2) borated_water.add_nuclide(o17, 6.0099e-5) -borated_water.add_s_alpha_beta('HH2O', '71t') +borated_water.add_s_alpha_beta('c_H_in_H2O', '71t') # Instantiate a Materials collection and export to XML materials_file = openmc.Materials([uo2, helium, zircaloy, borated_water]) diff --git a/examples/python/reflective/build-xml.py b/examples/python/reflective/build-xml.py index 949e57c8c..0e064ab61 100644 --- a/examples/python/reflective/build-xml.py +++ b/examples/python/reflective/build-xml.py @@ -16,7 +16,7 @@ particles = 10000 ############################################################################### # Instantiate a Nuclides -u235 = openmc.Nuclide('U-235') +u235 = openmc.Nuclide('U235') # Instantiate a Material and register the Nuclide fuel = openmc.Material(material_id=1, name='fuel') diff --git a/examples/xml/basic/materials.xml b/examples/xml/basic/materials.xml index 75ab74dbb..2f88731ff 100644 --- a/examples/xml/basic/materials.xml +++ b/examples/xml/basic/materials.xml @@ -5,14 +5,14 @@ - + - - - + + + diff --git a/examples/xml/boxes/materials.xml b/examples/xml/boxes/materials.xml index 6f6114a7d..c74714a08 100644 --- a/examples/xml/boxes/materials.xml +++ b/examples/xml/boxes/materials.xml @@ -5,19 +5,19 @@ - + - + - - - + + + diff --git a/examples/xml/lattice/nested/materials.xml b/examples/xml/lattice/nested/materials.xml index b64922136..7f8b06bb1 100644 --- a/examples/xml/lattice/nested/materials.xml +++ b/examples/xml/lattice/nested/materials.xml @@ -6,14 +6,14 @@ - + - - - + + + diff --git a/examples/xml/lattice/simple/materials.xml b/examples/xml/lattice/simple/materials.xml index b64922136..7f8b06bb1 100644 --- a/examples/xml/lattice/simple/materials.xml +++ b/examples/xml/lattice/simple/materials.xml @@ -6,14 +6,14 @@ - + - - - + + + diff --git a/examples/xml/pincell/materials.xml b/examples/xml/pincell/materials.xml index 427fc175d..b6af486d1 100644 --- a/examples/xml/pincell/materials.xml +++ b/examples/xml/pincell/materials.xml @@ -12,59 +12,59 @@ - - - - - + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - + + + + + + + diff --git a/examples/xml/reflective/materials.xml b/examples/xml/reflective/materials.xml index 6da53a9b6..13cbf070e 100644 --- a/examples/xml/reflective/materials.xml +++ b/examples/xml/reflective/materials.xml @@ -5,7 +5,7 @@ - + diff --git a/openmc/ace.py b/openmc/ace.py deleted file mode 100644 index 3606b1e94..000000000 --- a/openmc/ace.py +++ /dev/null @@ -1,65 +0,0 @@ -from __future__ import division -from struct import pack - - -def ascii_to_binary(ascii_file, binary_file): - """Convert an ACE file in ASCII format (type 1) to binary format (type 2). - - Parameters - ---------- - ascii_file : str - Filename of ASCII ACE file - binary_file : str - Filename of binary ACE file to be written - - """ - - # Open ASCII file - ascii = open(ascii_file, 'r') - - # Set default record length - record_length = 4096 - - # Read data from ASCII file - lines = ascii.readlines() - ascii.close() - - # Open binary file - binary = open(binary_file, 'wb') - - idx = 0 - while idx < len(lines): - # Read/write header block - hz = lines[idx][:10].encode('UTF-8') - aw0 = float(lines[idx][10:22]) - tz = float(lines[idx][22:34]) - hd = lines[idx][35:45].encode('UTF-8') - hk = lines[idx + 1][:70].encode('UTF-8') - hm = lines[idx + 1][70:80].encode('UTF-8') - binary.write(pack('=10sdd10s70s10s', hz, aw0, tz, hd, hk, hm)) - - # Read/write IZ/AW pairs - data = ' '.join(lines[idx + 2:idx + 6]).split() - iz = list(map(int, data[::2])) - aw = list(map(float, data[1::2])) - izaw = [item for sublist in zip(iz, aw) for item in sublist] - binary.write(pack('=' + 16*'id', *izaw)) - - # Read/write NXS and JXS arrays. Null bytes are added at the end so - # that XSS will start at the second record - nxs = list(map(int, ' '.join(lines[idx + 6:idx + 8]).split())) - jxs = list(map(int, ' '.join(lines[idx + 8:idx + 12]).split())) - binary.write(pack('=16i32i{0}x'.format(record_length - 500), *(nxs + jxs))) - - # Read/write XSS array. Null bytes are added to form a complete record - # at the end of the file - n_lines = (nxs[0] + 3)//4 - xss = list(map(float, ' '.join(lines[idx + 12:idx + 12 + n_lines]).split())) - extra_bytes = record_length - ((len(xss)*8 - 1) % record_length + 1) - binary.write(pack('={0}d{1}x'.format(nxs[0], extra_bytes), *xss)) - - # Advance to next table in file - idx += 12 + n_lines - - # Close binary file - binary.close() diff --git a/openmc/data/__init__.py b/openmc/data/__init__.py index df22d8bbb..32de3b598 100644 --- a/openmc/data/__init__.py +++ b/openmc/data/__init__.py @@ -1 +1,13 @@ from .data import * +from .ace import * +from .angle_distribution import * +from .container import * +from .energy_distribution import * +from .product import * +from .angle_energy import * +from .uncorrelated import * +from .correlated import * +from .kalbach_mann import * +from .nbody import * +from .thermal import * +from .urr import * diff --git a/openmc/data/ace.py b/openmc/data/ace.py new file mode 100644 index 000000000..16c18dfb4 --- /dev/null +++ b/openmc/data/ace.py @@ -0,0 +1,2226 @@ +"""This module is for reading ACE-format cross sections. ACE stands for "A +Compact ENDF" format and originated from work on MCNP_. It is used in a number +of other Monte Carlo particle transport codes. + +ACE-format cross sections are typically generated from ENDF_ files through a +cross section processing program like NJOY_. The ENDF data consists of tabulated +thermal data, ENDF/B resonance parameters, distribution parameters in the +unresolved resonance region, and tabulated data in the fast region. After the +ENDF data has been reconstructed and Doppler-broadened, the ACER module +generates ACE-format cross sections. + +.. _MCNP: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/ +.. _NJOY: http://t2.lanl.gov/codes.shtml +.. _ENDF: http://www.nndc.bnl.gov/endf + +""" + +from __future__ import division, unicode_literals +import io +from os import SEEK_CUR +import struct +import sys +from warnings import warn +from collections import OrderedDict +from copy import deepcopy + +import numpy as np +from numpy.polynomial import Polynomial +import h5py + +from . import atomic_number, atomic_symbol, reaction_name +from .container import Tabulated1D, interpolation_scheme +from .angle_distribution import AngleDistribution +from .energy_distribution import * +from .product import Product +from .angle_energy import AngleEnergy +from .kalbach_mann import KalbachMann +from .uncorrelated import UncorrelatedAngleEnergy +from .correlated import CorrelatedAngleEnergy +from .nbody import NBodyPhaseSpace +from .thermal import CoherentElastic +from .urr import ProbabilityTables +from openmc.stats import Tabular, Discrete, Uniform, Mixture + +if sys.version_info[0] >= 3: + basestring = str + + +def ascii_to_binary(ascii_file, binary_file): + """Convert an ACE file in ASCII format (type 1) to binary format (type 2). + + Parameters + ---------- + ascii_file : str + Filename of ASCII ACE file + binary_file : str + Filename of binary ACE file to be written + + """ + + # Open ASCII file + ascii = open(ascii_file, 'r') + + # Set default record length + record_length = 4096 + + # Read data from ASCII file + lines = ascii.readlines() + ascii.close() + + # Open binary file + binary = open(binary_file, 'wb') + + idx = 0 + + while idx < len(lines): + # check if it's a > 2.0.0 version header + if lines[idx].split()[0][1] == '.': + if lines[idx + 1].split()[3] == '3': + idx = idx + 3 + else: + raise NotImplementedError('Only backwards compatible ACE' + 'headers currently supported') + # Read/write header block + hz = lines[idx][:10].encode('UTF-8') + aw0 = float(lines[idx][10:22]) + tz = float(lines[idx][22:34]) + hd = lines[idx][35:45].encode('UTF-8') + hk = lines[idx + 1][:70].encode('UTF-8') + hm = lines[idx + 1][70:80].encode('UTF-8') + binary.write(struct.pack(str('=10sdd10s70s10s'), hz, aw0, tz, hd, hk, hm)) + + # Read/write IZ/AW pairs + data = ' '.join(lines[idx + 2:idx + 6]).split() + iz = list(map(int, data[::2])) + aw = list(map(float, data[1::2])) + izaw = [item for sublist in zip(iz, aw) for item in sublist] + binary.write(struct.pack(str('=' + 16*'id'), *izaw)) + + # Read/write NXS and JXS arrays. Null bytes are added at the end so + # that XSS will start at the second record + nxs = list(map(int, ' '.join(lines[idx + 6:idx + 8]).split())) + jxs = list(map(int, ' '.join(lines[idx + 8:idx + 12]).split())) + binary.write(struct.pack(str('=16i32i{0}x'.format(record_length - 500)), + *(nxs + jxs))) + + # Read/write XSS array. Null bytes are added to form a complete record + # at the end of the file + n_lines = (nxs[0] + 3)//4 + xss = list(map(float, ' '.join(lines[ + idx + 12:idx + 12 + n_lines]).split())) + extra_bytes = record_length - ((len(xss)*8 - 1) % record_length + 1) + binary.write(struct.pack(str('={0}d{1}x'.format(nxs[0], extra_bytes)), + *xss)) + + # Advance to next table in file + idx += 12 + n_lines + + # Close binary file + binary.close() + + +def _get_tabulated_1d(array, idx=0): + """Create a Tabulated1D object from array. + + Parameters + ---------- + array : numpy.ndarray + Array is formed as a 1 dimensional array as follows: [number of regions, + final pair for each region, interpolation parameters, number of pairs, + x-values, y-values] + idx : int, optional + Offset to read from in array (default of zero) + + Returns + ------- + openmc.data.Tabulated1D + Tabulated data object + + """ + + # Get number of regions and pairs + n_regions = int(array[idx]) + n_pairs = int(array[idx + 1 + 2*n_regions]) + + # Get interpolation information + idx += 1 + if n_regions > 0: + nbt = np.asarray(array[idx:idx + n_regions], dtype=int) + interp = np.asarray(array[idx + n_regions:idx + 2*n_regions], dtype=int) + else: + # NR=0 regions implies linear-linear interpolation by default + nbt = np.array([n_pairs]) + interp = np.array([2]) + + # Get (x,y) pairs + idx += 2*n_regions + 1 + x = array[idx:idx + n_pairs] + y = array[idx + n_pairs:idx + 2*n_pairs] + + return Tabulated1D(x, y, nbt, interp) + + +def get_table(filename, name=None): + """Read a single table from an ACE file + + Parameters + ---------- + filename : str + Path of the ACE library to load table from + name : str, optional + Name of table to load, e.g. '92235.71c' + + Returns + ------- + openmc.data.ace.Table + ACE table with specified name. If no name is specified, the first table + in the file is returned. + + """ + + lib = Library(filename) + if name is None: + return list(lib.tables.values())[0] + else: + return lib.tables[name] + + +def get_all_tables(filename): + """Read all tables from an ACE file + + Parameters + ---------- + filename : str + Path of the ACE library to load table from + name : str, optional + Name of table to load, e.g. '92235.71c' + + Returns + ------- + list of openmc.data.ace.Table + ACE tables read from the file + + """ + + lib = Library(filename) + return list(lib.tables.values()) + + +class Library(object): + """A Library objects represents an ACE-formatted file which may contain + multiple tables with data. + + Parameters + ---------- + filename : str + Path of the ACE library file to load. + table_names : None, str, or iterable, optional + Tables from the file to read in. If None, reads in all of the + tables. If str, reads in only the single table of a matching name. + verbose : bool, optional + Determines whether output is printed to the stdout when reading a + Library + + Attributes + ---------- + tables : dict + Dictionary whose keys are the names of the ACE tables and whose values + are the instances of subclasses of :class:`Table` + (e.g. :class:`NeutronTable`) + + """ + + def __init__(self, filename, table_names=None, verbose=False): + if isinstance(table_names, basestring): + table_names = [table_names] + if table_names is not None: + table_names = set(table_names) + + self.tables = {} + + # Determine whether file is ASCII or binary + try: + fh = io.open(filename, 'rb') + # Grab 10 lines of the library + sb = b''.join([fh.readline() for i in range(10)]) + + # Try to decode it with ascii + sd = sb.decode('ascii') + + # No exception so proceed with ASCII - reopen in non-binary + fh.close() + fh = io.open(filename, 'r') + fh.seek(0) + self._read_ascii(fh, table_names, verbose) + except UnicodeDecodeError: + fh.close() + fh = open(filename, 'rb') + self._read_binary(fh, table_names, verbose) + + def _read_binary(self, fh, table_names, verbose=False, + recl_length=4096, entries=512): + """Read a binary (Type 2) ACE table. + + Parameters + ---------- + fh : file + Open ACE file + table_names : None, str, or iterable + Tables from the file to read in. If None, reads in all of the + tables. If str, reads in only the single table of a matching name. + verbose : str, optional + Whether to display what tables are being read. Defaults to False. + recl_length : int, optional + Fortran record length in binary file. Default value is 4096 bytes. + entries : int, optional + Number of entries per record. The default is 512 corresponding to a + record length of 4096 bytes with double precision data. + + """ + + while True: + start_position = fh.tell() + + # Check for end-of-file + if len(fh.read(1)) == 0: + return + fh.seek(start_position) + + # Read name, atomic mass ratio, temperature, date, comment, and + # material + name, atomic_weight_ratio, temperature, date, comment, mat = \ + struct.unpack(str('=10sdd10s70s10s'), fh.read(116)) + name = name.strip() + + # Read ZAID/awr combinations + izaw_pairs = struct.unpack(str('=' + 16*'id'), fh.read(192)) + + # Read NXS + nxs = list(struct.unpack(str('=16i'), fh.read(64))) + + # Determine length of XSS and number of records + length = nxs[0] + n_records = (length + entries - 1)//entries + + # name is bytes, make it a string + name = name.decode() + # verify that we are supposed to read this table in + if (table_names is not None) and (name not in table_names): + fh.seek(start_position + recl_length*(n_records + 1)) + continue + + # ensure we have a valid table type + if len(name) == 0 or name[-1] not in table_types: + warn("Unsupported table: " + name, RuntimeWarning) + fh.seek(start_position + recl_length*(n_records + 1)) + continue + + # get the table + table = table_types[name[-1]](name, atomic_weight_ratio, temperature) + + if verbose: + temperature_in_K = round(temperature * 1e6 / 8.617342e-5) + print("Loading nuclide {0} at {1} K".format(name, temperature_in_K)) + self.tables[name] = table + + # If table is S(a,b), add zaids + zaids = np.array(izaw_pairs[::2]) + table.zaids = zaids[np.nonzero(zaids)] + + # Read JXS + jxs = list(struct.unpack(str('=32i'), fh.read(128))) + + # Read XSS + fh.seek(start_position + recl_length) + xss = list(struct.unpack(str('={0}d'.format(length)), + fh.read(length*8))) + + # Insert empty object at beginning of NXS, JXS, and XSS arrays so + # that the indexing will be the same as Fortran. This makes it + # easier to follow the ACE format specification. + nxs.insert(0, 0) + table._nxs = np.array(nxs, dtype=int) + + jxs.insert(0, 0) + table._jxs = np.array(jxs, dtype=int) + + xss.insert(0, 0.0) + table._xss = np.array(xss) + + # Read all data blocks + table._read_all() + + # Advance to next record + fh.seek(start_position + recl_length*(n_records + 1)) + + def _read_ascii(self, fh, table_names, verbose=False): + """Read an ASCII (Type 1) ACE table. + + Parameters + ---------- + fh : file + Open ACE file + table_names : None, str, or iterable + Tables from the file to read in. If None, reads in all of the + tables. If str, reads in only the single table of a matching name. + verbose : str, optional + Whether to display what tables are being read. Defaults to False. + + """ + + tables_seen = set() + + lines = [fh.readline() for i in range(13)] + + while (0 != len(lines)) and (lines[0] != ''): + # Read name of table, atomic mass ratio, and temperature. If first + # line is empty, we are at end of file + + # check if it's a 2.0 style header + if lines[0].split()[0][1] == '.': + words = lines[0].split() + version = words[0] + name = words[1] + if len(words) == 3: + source = words[2] + words = lines[1].split() + atomic_weight_ratio = float(words[0]) + temperature = float(words[1]) + commentlines = int(words[3]) + for i in range(commentlines): + lines.pop(0) + lines.append(fh.readline()) + else: + words = lines[0].split() + name = words[0] + atomic_weight_ratio = float(words[1]) + temperature = float(words[2]) + + izaw_pairs = (' '.join(lines[2:6])).split() + + datastr = '0 ' + ' '.join(lines[6:8]) + nxs = np.fromstring(datastr, sep=' ', dtype=int) + + n_lines = (nxs[1] + 3)//4 + n_bytes = len(lines[-1]) * (n_lines - 2) + 1 + + # Ensure that we have more tables to read in + if (table_names is not None) and (table_names < tables_seen): + break + tables_seen.add(name) + + # verify that we are suppossed to read this table in + if (table_names is not None) and (name not in table_names): + fh.seek(n_bytes, SEEK_CUR) + fh.readline() + lines = [fh.readline() for i in range(13)] + continue + + # ensure we have a valid table type + if len(name) == 0 or name[-1] not in table_types: + warn("Unsupported table: " + name, RuntimeWarning) + fh.seek(n_bytes, SEEK_CUR) + fh.readline() + lines = [fh.readline() for i in range(13)] + continue + + # read and fix over-shoot + lines += fh.readlines(n_bytes) + if 12 + n_lines < len(lines): + goback = sum([len(line) for line in lines[12+n_lines:]]) + lines = lines[:12+n_lines] + fh.seek(-goback, SEEK_CUR) + + # get the table + table = table_types[name[-1]](name, atomic_weight_ratio, temperature) + + if verbose: + temperature_in_K = round(temperature * 1e6 / 8.617342e-5) + print("Loading nuclide {0} at {1} K".format(name, temperature_in_K)) + self.tables[name] = table + + # Read comment + table.comment = lines[1].strip() + + # If table is S(a,b), add zaids + if isinstance(table, SabTable): + zaids = np.fromiter(map(int, izaw_pairs[::2]), int) + table.zaids = zaids[np.nonzero(zaids)] + + # Add NXS, JXS, and XSS arrays to table Insert empty object at + # beginning of NXS, JXS, and XSS arrays so that the indexing will be + # the same as Fortran. This makes it easier to follow the ACE format + # specification. + table._nxs = nxs + + datastr = '0 ' + ' '.join(lines[8:12]) + table._jxs = np.fromstring(datastr, dtype=int, sep=' ') + + datastr = '0.0 ' + ''.join(lines[12:12+n_lines]) + table._xss = np.fromstring(datastr, sep=' ') + + # Read all data blocks + table._read_all() + lines = [fh.readline() for i in range(13)] + + +class Table(object): + """Abstract superclass of all other classes for cross section tables. + + Parameters + ---------- + name : str + ZAID identifier of the table, e.g. '92235.70c'. + atomic_weight_ratio : float + Atomic mass ratio of the target nuclide. + temperature : float + Temperature of the target nuclide in eV. + + Attributes + ---------- + name : str + ZAID identifier of the table, e.g. '92235.70c'. + atomic_weight_ratio : float + Atomic mass ratio of the target nuclide. + temperature : float + Temperature of the target nuclide in eV. + + """ + + def __init__(self, name, atomic_weight_ratio, temperature): + self.name = name + self.atomic_weight_ratio = atomic_weight_ratio + self.temperature = temperature + + def _read_all(self): + raise NotImplementedError + + def _get_continuous_tabular(self, idx, ldis): + """Get continuous tabular energy distribution (ACE law 4) starting at specified + index in the XSS array. + + Parameters + ---------- + idx : int + Index in XSS array of the start of the energy distribution data + (LDIS + LOCC - 1) + ldis : int + Index in XSS array of the start of the energy distribution block + (e.g. JXS[11]) + + Returns + ------- + openmc.data.energy_distribution.ContinuousTabular + Continuous tabular energy distribution + + """ + + # Read number of interpolation regions and incoming energies + n_regions = int(self._xss[idx]) + n_energy_in = int(self._xss[idx + 1 + 2*n_regions]) + + # Get interpolation information + idx += 1 + if n_regions > 0: + breakpoints = np.asarray(self._xss[idx:idx + n_regions], dtype=int) + interpolation = np.asarray(self._xss[idx + n_regions: + idx + 2*n_regions], dtype=int) + else: + breakpoints = np.array([n_energy_in]) + interpolation = np.array([2]) + + # Incoming energies at which distributions exist + idx += 2 * n_regions + 1 + energy = self._xss[idx:idx + n_energy_in] + + # Location of distributions + idx += n_energy_in + loc_dist = np.asarray(self._xss[idx:idx + n_energy_in], dtype=int) + + # Initialize variables + energy_out = [] + + # Read each outgoing energy distribution + for i in range(n_energy_in): + idx = ldis + loc_dist[i] - 1 + + # intt = interpolation scheme (1=hist, 2=lin-lin) + INTTp = int(self._xss[idx]) + intt = INTTp % 10 + n_discrete_lines = (INTTp - intt)//10 + if intt not in (1, 2): + warn("Interpolation scheme for continuous tabular distribution " + "is not histogram or linear-linear.") + intt = 2 + + n_energy_out = int(self._xss[idx + 1]) + data = self._xss[idx + 2:idx + 2 + 3*n_energy_out] + data.shape = (3, n_energy_out) + + # Create continuous distribution + eout_continuous = Tabular(data[0][n_discrete_lines:], + data[1][n_discrete_lines:], + interpolation_scheme[intt]) + eout_continuous.c = data[2][n_discrete_lines:] + + # If discrete lines are present, create a mixture distribution + if n_discrete_lines > 0: + eout_discrete = Discrete(data[0][:n_discrete_lines], + data[1][:n_discrete_lines]) + eout_discrete.c = data[2][:n_discrete_lines] + if n_discrete_lines == n_energy_out: + eout_i = eout_discrete + else: + p_discrete = min(sum(eout_discrete.p), 1.0) + eout_i = Mixture([p_discrete, 1. - p_discrete], + [eout_discrete, eout_continuous]) + else: + eout_i = eout_continuous + + energy_out.append(eout_i) + + return ContinuousTabular(breakpoints, interpolation, energy, + energy_out) + + def _get_general_evaporation(self, idx): + # Read nuclear temperature as Tabulated1D + theta = _get_tabulated_1d(array, idx) + + # X-function + nr = int(array[idx]) + ne = int(array[idx + 1 + 2*nr]) + idx += 2 + 2*nr + 2*ne + net = int(array[idx]) + x = array[idx + 1:idx + 1 + net] + + raise NotImplementedError("Where'd you get this ACE file from?") + + def _get_maxwell_energy(self, idx): + # Read nuclear temperature as Tabulated1D + theta = _get_tabulated_1d(self._xss, idx) + + # Restriction energy + nr = int(self._xss[idx]) + ne = int(self._xss[idx + 1 + 2*nr]) + u = self._xss[idx + 2 + 2*nr + 2*ne] + + return MaxwellEnergy(theta, u) + + def _get_evaporation(self, idx): + # Read nuclear temperature as Tabulated1D + theta = _get_tabulated_1d(self._xss, idx) + + # Restriction energy + nr = int(self._xss[idx]) + ne = int(self._xss[idx + 1 + 2*nr]) + u = self._xss[idx + 2 + 2*nr + 2*ne] + + return Evaporation(theta, u) + + def _get_watt_energy(self, idx): + # Energy-dependent a parameter + a = _get_tabulated_1d(self._xss, idx) + + # Advance index + nr = int(self._xss[idx]) + ne = int(self._xss[idx + 1 + 2*nr]) + idx += 2 + 2*nr + 2*ne + + # Energy-dependent b parameter + b = _get_tabulated_1d(self._xss, idx) + + # Advance index + nr = int(self._xss[idx]) + ne = int(self._xss[idx + 1 + 2*nr]) + idx += 2 + 2*nr + 2*ne + + # Restriction energy + u = self._xss[idx] + + return WattEnergy(a, b, u) + + def _get_kalbach_mann(self, idx, ldis): + # Read number of interpolation regions and incoming energies + n_regions = int(self._xss[idx]) + n_energy_in = int(self._xss[idx + 1 + 2*n_regions]) + + # Get interpolation information + idx += 1 + if n_regions > 0: + breakpoints = np.asarray(self._xss[idx:idx + n_regions], dtype=int) + interpolation = np.asarray(self._xss[idx + n_regions: + idx + 2*n_regions], dtype=int) + else: + breakpoints = np.array([n_energy_in]) + interpolation = np.array([2]) + + # Incoming energies at which distributions exist + idx += 2 * n_regions + 1 + energy = self._xss[idx:idx + n_energy_in] + + # Location of distributions + idx += n_energy_in + loc_dist = np.asarray(self._xss[idx:idx + n_energy_in], dtype=int) + + # Initialize variables + energy_out = [] + km_r = [] + km_a = [] + + # Read each outgoing energy distribution + for i in range(n_energy_in): + idx = ldis + loc_dist[i] - 1 + + # intt = interpolation scheme (1=hist, 2=lin-lin) + INTTp = int(self._xss[idx]) + intt = INTTp % 10 + n_discrete_lines = (INTTp - intt)//10 + if intt not in (1, 2): + warn("Interpolation scheme for continuous tabular distribution " + "is not histogram or linear-linear.") + intt = 2 + + n_energy_out = int(self._xss[idx + 1]) + data = self._xss[idx + 2:idx + 2 + 5*n_energy_out] + data.shape = (5, n_energy_out) + + # Create continuous distribution + eout_continuous = Tabular(data[0][n_discrete_lines:], + data[1][n_discrete_lines:], + interpolation_scheme[intt]) + eout_continuous.c = data[2][n_discrete_lines:] + + # If discrete lines are present, create a mixture distribution + if n_discrete_lines > 0: + eout_discrete = Discrete(data[0][:n_discrete_lines], + data[1][:n_discrete_lines]) + eout_discrete.c = data[2][:n_discrete_lines] + if n_discrete_lines == n_energy_out: + eout_i = eout_discrete + else: + p_discrete = min(sum(eout_discrete.p), 1.0) + eout_i = Mixture([p_discrete, 1. - p_discrete], + [eout_discrete, eout_continuous]) + else: + eout_i = eout_continuous + + energy_out.append(eout_i) + km_r.append(Tabulated1D(data[0], data[3])) + km_a.append(Tabulated1D(data[0], data[4])) + + return KalbachMann(breakpoints, interpolation, energy, energy_out, + km_r, km_a) + + def _get_correlated(self, idx, ldis): + # Read number of interpolation regions and incoming energies + n_regions = int(self._xss[idx]) + n_energy_in = int(self._xss[idx + 1 + 2*n_regions]) + + # Get interpolation information + idx += 1 + if n_regions > 0: + breakpoints = np.asarray(self._xss[idx:idx + n_regions], dtype=int) + interpolation = np.asarray(self._xss[idx + n_regions: + idx + 2*n_regions], dtype=int) + else: + breakpoints = np.array([n_energy_in]) + interpolation = np.array([2]) + + # Incoming energies at which distributions exist + idx += 2 * n_regions + 1 + energy = self._xss[idx:idx + n_energy_in] + + # Location of distributions + idx += n_energy_in + loc_dist = np.asarray(self._xss[idx:idx + n_energy_in], dtype=int) + + # Initialize list of distributions + energy_out = [] + mu = [] + + # Read each outgoing energy distribution + for i in range(n_energy_in): + idx = ldis + loc_dist[i] - 1 + + # intt = interpolation scheme (1=hist, 2=lin-lin) + INTTp = int(self._xss[idx]) + intt = INTTp % 10 + n_discrete_lines = (INTTp - intt)//10 + if intt not in (1, 2): + warn("Interpolation scheme for continuous tabular distribution " + "is not histogram or linear-linear.") + intt = 2 + + # Secondary energy distribution + n_energy_out = int(self._xss[idx + 1]) + data = self._xss[idx + 2:idx + 2 + 4*n_energy_out] + data.shape = (4, n_energy_out) + + # Create continuous distribution + eout_continuous = Tabular(data[0][n_discrete_lines:], + data[1][n_discrete_lines:], + interpolation_scheme[intt], + ignore_negative=True) + eout_continuous.c = data[2][n_discrete_lines:] + + # If discrete lines are present, create a mixture distribution + if n_discrete_lines > 0: + eout_discrete = Discrete(data[0][:n_discrete_lines], + data[1][:n_discrete_lines]) + eout_discrete.c = data[2][:n_discrete_lines] + if n_discrete_lines == n_energy_out: + eout_i = eout_discrete + else: + p_discrete = min(sum(eout_discrete.p), 1.0) + eout_i = Mixture([p_discrete, 1. - p_discrete], + [eout_discrete, eout_continuous]) + else: + eout_i = eout_continuous + + energy_out.append(eout_i) + + lc = np.asarray(data[3], dtype=int) + + # Secondary angular distributions + mu_i = [] + for j in range(n_energy_out): + if lc[j] > 0: + idx = ldis + abs(lc[j]) - 1 + + intt = int(self._xss[idx]) + n_cosine = int(self._xss[idx + 1]) + data = self._xss[idx + 2:idx + 2 + 3*n_cosine] + data.shape = (3, n_cosine) + + mu_ij = Tabular(data[0], data[1], interpolation_scheme[intt]) + mu_ij.c = data[2] + else: + # Isotropic distribution + mu_ij = Uniform(-1., 1.) + + mu_i.append(mu_ij) + + # Add cosine distributions for this incoming energy to list + mu.append(mu_i) + + return CorrelatedAngleEnergy(breakpoints, interpolation, energy, + energy_out, mu) + + def _get_energy_distribution(self, location_dist, location_start, rx=None): + """Returns an EnergyDistribution object from data read in starting at + location_start. + + Parameters + ---------- + location_dist : int + Index in the XSS array corresponding to the start of a block, + e.g. JXS(11) for the the DLW block. + location_start : int + Index in the XSS array corresponding to the start of an energy + distribution array + rx : Reaction + Reaction this energy distribution will be associated with + + Returns + ------- + distribution : openmc.data.AngleEnergy + Secondary angle-energy distribution + + """ + + # Set starting index for energy distribution + idx = location_dist + location_start - 1 + + law = int(self._xss[idx + 1]) + location_data = int(self._xss[idx + 2]) + + # Position index for reading law data + idx = location_dist + location_data - 1 + + # Parse energy distribution data + if law == 2: + primary_flag = int(self._xss[idx]) + energy = self._xss[idx + 1] + distribution = UncorrelatedAngleEnergy() + distribution.energy = DiscretePhoton(primary_flag, energy, + self.atomic_weight_ratio) + elif law in (3, 33): + threshold, mass_ratio = self._xss[idx:idx + 2] + distribution = UncorrelatedAngleEnergy() + distribution.energy = LevelInelastic(threshold, mass_ratio) + elif law == 4: + distribution = UncorrelatedAngleEnergy() + distribution.energy = self._get_continuous_tabular(idx, location_dist) + elif law == 5: + distribution = UncorrelatedAngleEnergy() + distribution.energy = self._get_general_evaporation(idx) + elif law == 7: + distribution = UncorrelatedAngleEnergy() + distribution.energy = self._get_maxwell_energy(idx) + elif law == 9: + distribution = UncorrelatedAngleEnergy() + distribution.energy = self._get_evaporation(idx) + elif law == 11: + distribution = UncorrelatedAngleEnergy() + distribution.energy = self._get_watt_energy(idx) + elif law == 44: + distribution = self._get_kalbach_mann(idx, location_dist) + elif law == 61: + distribution = self._get_correlated(idx, location_dist) + elif law == 66: + n_particles = int(self._xss[idx]) + total_mass = self._xss[idx + 1] + distribution = NBodyPhaseSpace(total_mass, n_particles, + self.atomic_weight_ratio, rx.Q_value) + else: + raise IOError("Unsupported ACE secondary energy " + "distribution law {0}".format(law)) + + return distribution + + +class NeutronTable(Table): + """A NeutronTable object contains continuous-energy neutron interaction data + read from an ACE-formatted table. These objects are not normally + instantiated by the user but rather created when reading data using a + Library object and stored within the :attr:`Library.tables` attribute. + + Parameters + ---------- + name : str + ZAID identifier of the table, e.g. '92235.70c'. + atomic_weight_ratio : float + Atomic mass ratio of the target nuclide. + temperature : float + Temperature of the target nuclide in eV. + + Attributes + ---------- + absorption_xs : numpy.ndarray + The microscopic absorption cross section for each value on the energy + grid. + atomic_weight_ratio : float + Atomic weight ratio of the target nuclide. + energy : numpy.ndarray + The energy values (MeV) at which reaction cross-sections are tabulated. + heating_number : numpy.ndarray + The total heating number for each value on the energy grid in MeV-b. + name : str + ZAID identifier of the table, e.g. 92235.70c. + reactions : collections.OrderedDict + Contains the cross sections, secondary angle and energy distributions, + and other associated data for each reaction. The keys are the MT values + and the values are Reaction objects. + temperature : float + Temperature of the target nuclide in eV. + total_xs : numpy.ndarray + The microscopic total cross section for each value on the energy grid in b. + urr : None or openmc.data.ProbabilityTables + Unresolved resonance region probability tables + + """ + + def __init__(self, name, atomic_weight_ratio, temperature): + super(NeutronTable, self).__init__(name, atomic_weight_ratio, temperature) + self.absorption_xs = None + self.energy = None + self.heating_number = None + self.total_xs = None + self.reactions = OrderedDict() + self.urr = None + + def __repr__(self): + if hasattr(self, 'name'): + return "".format(self.name) + else: + return "" + + def __iter__(self): + return iter(self.reactions.values()) + + def _read_all(self): + self._read_cross_sections() + self._read_nu() + self._read_secondaries() + self._read_photon_production_data() + self._read_unr() + + def _read_cross_sections(self): + """Read reaction cross sections and other data. + + Reads and parses the ESZ, MTR, LQR, TRY, LSIG, and SIG blocks. These + blocks contain the energy grid, all reaction cross sections, the total + cross section, average heating numbers, and a list of reactions with + their Q-values and multiplicites. + """ + + # Determine number of energies on nuclide grid and number of reactions + # excluding elastic scattering + n_energies = self._nxs[3] + n_reactions = self._nxs[4] + + # Read energy grid and total, absorption, elastic scattering, and + # heating cross sections -- note that this appear separate from the rest + # of the reaction cross sections + arr = self._xss[self._jxs[1]:self._jxs[1] + 5*n_energies] + arr.shape = (5, n_energies) + self.energy, self.total_xs, self.absorption_xs, \ + elastic_xs, self.heating_number = arr + + # Create elastic scattering reaction + elastic_scatter = Reaction(2, self) + elastic_scatter.products.append(Product('neutron')) + elastic_scatter.xs = Tabulated1D(self.energy, elastic_xs) + self.reactions[2] = elastic_scatter + + # Create all other reactions with MT values + mts = np.asarray(self._xss[self._jxs[3]:self._jxs[3] + n_reactions], dtype=int) + qvalues = np.asarray(self._xss[self._jxs[4]:self._jxs[4] + + n_reactions], dtype=float) + tys = np.asarray(self._xss[self._jxs[5]:self._jxs[5] + n_reactions], dtype=int) + + # Create all reactions other than elastic scatter + reactions = [(mt, Reaction(mt, self)) for mt in mts] + self.reactions.update(reactions) + + # Loop over all reactions other than elastic scattering + for i, rx in enumerate(list(self.reactions.values())[1:]): + # Copy Q values determine if scattering should be treated in the + # center-of-mass or lab system + rx.Q_value = qvalues[i] + rx.center_of_mass = (tys[i] < 0) + + # For neutron-producing reactions, get yield + if rx.MT < 100: + if tys[i] != 19: + if abs(tys[i]) > 100: + # Energy-dependent neutron yield + idx = self._jxs[11] + abs(tys[i]) - 101 + yield_ = _get_tabulated_1d(self._xss, idx) + else: + yield_ = abs(tys[i]) + + neutron = Product('neutron') + neutron.yield_ = yield_ + rx.products.append(neutron) + + # Get locator for cross-section data + loc = int(self._xss[self._jxs[6] + i]) + + # Determine starting index on energy grid + rx.threshold_idx = int(self._xss[self._jxs[7] + loc - 1]) - 1 + + # Determine number of energies in reaction + n_energies = int(self._xss[self._jxs[7] + loc]) + energy = self.energy[rx.threshold_idx:rx.threshold_idx + n_energies] + + # Read reaction cross section + xs = self._xss[self._jxs[7] + loc + 1:self._jxs[7] + loc + 1 + n_energies] + rx.xs = Tabulated1D(energy, xs) + + def _read_nu(self): + """Read the NU block -- this contains information on the prompt and delayed + neutron precursor yields, decay constants, etc + + """ + # No NU block + if self._jxs[2] == 0: + return + + products = [] + derived_products = [] + + # Either prompt nu or total nu is given + if self._xss[self._jxs[2]] > 0: + whichnu = 'prompt' if self._jxs[24] > 0 else 'total' + + neutron = Product('neutron') + neutron.emission_mode = whichnu + + idx = self._jxs[2] + LNU = int(self._xss[idx]) + if LNU == 1: + # Polynomial function form of nu + NC = int(self._xss[idx+1]) + coefficients = self._xss[idx+2 : idx+2+NC] + neutron.yield_ = Polynomial(coefficients) + elif LNU == 2: + # Tabular data form of nu + neutron.yield_ = _get_tabulated_1d(self._xss, idx + 1) + + products.append(neutron) + + # Both prompt nu and total nu + elif self._xss[self._jxs[2]] < 0: + # Read prompt neutron yield + prompt_neutron = Product('neutron') + prompt_neutron.emission_mode = 'prompt' + + idx = self._jxs[2] + 1 + LNU = int(self._xss[idx]) + if LNU == 1: + # Polynomial function form of nu + NC = int(self._xss[idx+1]) + coefficients = self._xss[idx+2 : idx+2+NC] + prompt_neutron.yield_ = Polynomial(coefficients) + elif LNU == 2: + # Tabular data form of nu + prompt_neutron.yield_ = _get_tabulated_1d(self._xss, idx + 1) + + # Read total neutron yield + total_neutron = Product('neutron') + total_neutron.emission_mode = 'total' + + idx = self._jxs[2] + int(abs(self._xss[self._jxs[2]])) + 1 + LNU = int(self._xss[idx]) + + if LNU == 1: + # Polynomial function form of nu + NC = int(self._xss[idx+1]) + coefficients = self._xss[idx+2 : idx+2+NC] + total_neutron.yield_ = Polynomial(coefficients) + elif LNU == 2: + # Tabular data form of nu + total_neutron.yield_ = _get_tabulated_1d(self._xss, idx + 1) + + products.append(prompt_neutron) + derived_products.append(total_neutron) + + # Check for delayed nu data + if self._jxs[24] > 0: + yield_delayed = _get_tabulated_1d(self._xss, self._jxs[24] + 1) + + # Delayed neutron precursor distribution + idx = self._jxs[25] + n_group = self._nxs[8] + total_group_probability = 0. + for i, group in enumerate(range(n_group)): + delayed_neutron = Product('neutron') + delayed_neutron.emission_mode = 'delayed' + delayed_neutron.decay_rate = self._xss[idx] + + group_probability = _get_tabulated_1d(self._xss, idx + 1) + if np.all(group_probability.y == group_probability.y[0]): + delayed_neutron.yield_ = deepcopy(yield_delayed) + delayed_neutron.yield_.y *= group_probability.y[0] + total_group_probability += group_probability.y[0] + else: + raise NotImplementedError( + 'Delayed neutron with energy-dependent ' + 'group probability') + + # Advance position + nr = int(self._xss[idx + 1]) + ne = int(self._xss[idx + 2 + 2*nr]) + idx += 3 + 2*nr + 2*ne + + # Energy distribution for delayed fission neutrons + location_start = int(self._xss[self._jxs[26] + group]) + delayed_neutron.distribution.append( + self._get_energy_distribution(self._jxs[27], location_start)) + + products.append(delayed_neutron) + + # Renormalize delayed neutron yields to reflect fact that in ACE + # file, the sum of the group probabilities is not exactly one + for product in products[1:]: + product.yield_.y /= total_group_probability + + # Copy fission neutrons to reactions + for MT, rx in self.reactions.items(): + if MT in (18, 19, 20, 21, 38): + rx.products = deepcopy(products) + if derived_products: + rx.derived_products = deepcopy(derived_products) + + def _get_angle_distribution(self, location_dist, location_start): + # Set starting index for angle distribution + idx = location_dist + location_start - 1 + + # Number of energies at which angular distributions are tabulated + n_energies = int(self._xss[idx]) + idx += 1 + + # Incoming energy grid + energy = self._xss[idx:idx + n_energies] + idx += n_energies + + # Read locations for angular distributions + lc = np.asarray(self._xss[idx:idx + n_energies], dtype=int) + idx += n_energies + + mu = [] + for i in range(n_energies): + if lc[i] > 0: + # Equiprobable 32 bin distribution + idx = location_dist + abs(lc[i]) - 1 + cos = self._xss[idx:idx + 33] + pdf = np.zeros(33) + pdf[:32] = 1.0/(32.0*np.diff(cos)) + cdf = np.linspace(0.0, 1.0, 33) + + mu_i = Tabular(cos, pdf, 'histogram', ignore_negative=True) + mu_i.c = cdf + elif lc[i] < 0: + # Tabular angular distribution + idx = location_dist + abs(lc[i]) - 1 + intt = int(self._xss[idx]) + n_points = int(self._xss[idx + 1]) + data = self._xss[idx + 2:idx + 2 + 3*n_points] + data.shape = (3, n_points) + + mu_i = Tabular(data[0], data[1], interpolation_scheme[intt]) + mu_i.c = data[2] + else: + # Isotropic angular distribution + mu_i = Uniform(-1., 1.) + + mu.append(mu_i) + + return AngleDistribution(energy, mu) + + def _read_secondaries(self): + """Read angle/energy distributions for each reaction MT + """ + + # Number of reactions with secondary neutrons (including elastic + # scattering) + n_reactions = self._nxs[5] + 1 + + for i, rx in enumerate(list(self.reactions.values())[:n_reactions]): + if rx.MT == 18: + for p in rx.products: + if p.emission_mode == 'prompt': + neutron = p + break + else: + neutron = rx.products[0] + + if i > 0: + # Determine locator for ith energy distribution + lnw = int(self._xss[self._jxs[10] + i - 1]) + + while lnw > 0: + # Applicability of this distribution + neutron.applicability.append(_get_tabulated_1d( + self._xss, self._jxs[11] + lnw + 2)) + + # Read energy distribution data + neutron.distribution.append(self._get_energy_distribution( + self._jxs[11], lnw, rx)) + + lnw = int(self._xss[self._jxs[11] + lnw - 1]) + else: + # No energy distribution for elastic scattering + neutron.distribution.append(UncorrelatedAngleEnergy()) + + # Check if angular distribution data exist + loc = int(self._xss[self._jxs[8] + i]) + if loc == -1: + # Angular distribution data are given as part of product + # angle-energy distribution + continue + elif loc == 0: + # No angular distribution data are given for this + # reaction, isotropic scattering is asssumed + angle_dist = None + else: + angle_dist = self._get_angle_distribution(self._jxs[9], loc) + + # Apply angular distribution to each uncorrelated angle-energy + # distribution + if angle_dist is not None: + for d in neutron.distribution: + d.angle = angle_dist + + def _read_photon_production_data(self): + """Read cross sections for each photon-production reaction""" + + n_photon_reactions = self._nxs[6] + photon_mts = np.asarray(self._xss[self._jxs[13]:self._jxs[13] + + n_photon_reactions], dtype=int) + + for i, rx in enumerate(photon_mts): + # Determine corresponding reaction + mt = photon_mts[i] // 1000 + reactions = [] + if mt not in self.reactions: + # If the photon is assigned to MT=18 but the file splits fission + # into MT=19,20,21,38, assign the photon product to each of the + # individual reactions + if mt == 18: + for mt_fiss in (19, 20, 21, 38): + if mt_fiss in self.reactions: + reactions.append(self.reactions[mt_fiss]) + if not reactions: + reactions.append(Reaction(mt, self)) + else: + reactions.append(self.reactions[mt]) + + # Create photon product and assign to reactions + photon = Product('photon') + for rx in reactions: + rx.products.append(photon) + + # ================================================================== + # Read photon yield / production cross section + + loca = int(self._xss[self._jxs[14] + i]) + idx = self._jxs[15] + loca - 1 + mftype = int(self._xss[idx]) + idx += 1 + + if mftype in (12, 16): + # Yield data taken from ENDF File 12 or 6 + mtmult = int(self._xss[idx]) + assert mtmult == mt + + # Read photon yield as function of energy + photon.yield_ = _get_tabulated_1d(self._xss, idx + 1) + + elif mftype == 13: + # Cross section data from ENDF File 13 + + # Energy grid index at which data starts + threshold_idx = int(self._xss[idx]) - 1 + + # Get photon production cross section + n_energy = int(self._xss[idx + 1]) + photon._xs = self._xss[idx + 2:idx + 2 + n_energy] + + # Determine yield based on ratio of cross sections + energy = self.energy[threshold_idx:threshold_idx + n_energy] + photon.yield_ = Tabulated1D(energy, photon._xs) + + else: + raise ValueError("MFTYPE must be 12, 13, 16. Got {0}".format( + mftype)) + + # ================================================================== + # Read photon energy distribution + + location_start = int(self._xss[self._jxs[18] + i]) + + # Read energy distribution data + distribution = self._get_energy_distribution( + self._jxs[19], location_start) + assert isinstance(distribution, UncorrelatedAngleEnergy) + + # ================================================================== + # Read photon angular distribution + loc = int(self._xss[self._jxs[16] + i]) + + if loc == 0: + # No angular distribution data are given for this reaction, + # isotropic scattering is asssumed in LAB + energy = np.array([photon.yield_.x[0], photon.yield_.x[-1]]) + mu_isotropic = Uniform(-1., 1.) + distribution.angle = AngleDistribution( + energy, [mu_isotropic, mu_isotropic]) + else: + distribution.angle = self._get_angle_distribution(self._jxs[17], loc) + + # Add to list of distributions + photon.distribution.append(distribution) + + def _read_unr(self): + """Read the unresolved resonance range probability tables if present. + """ + + # Check if URR probability tables are present + idx = self._jxs[23] + if idx == 0: + return + + N = int(self._xss[idx]) # Number of incident energies + M = int(self._xss[idx+1]) # Length of probability table + interpolation = int(self._xss[idx+2]) + inelastic_flag = int(self._xss[idx+3]) + absorption_flag = int(self._xss[idx+4]) + multiply_smooth = (int(self._xss[idx+5]) == 1) + idx += 6 + + # Get energies at which tables exist + energy = self._xss[idx : idx+N] + idx += N + + # Get probability tables + table = self._xss[idx:idx+N*6*M] + table.shape = (N, 6, M) + + # Create object + self.urr = ProbabilityTables(energy, table, interpolation, inelastic_flag, + absorption_flag, multiply_smooth) + + def export_to_hdf5(self, path, element=None, mass_number=None, metastable=0): + """Export table to an HDF5 file. + + Parameters + ---------- + path : str + Path to write HDF5 file to + element : str or None + Elemental symbol, e.g. Zr. If not specified, the atomic + number/symbol are inferred from the name of the table. + mass_number : int or None + Mass number of the nuclide. For natural elements, a value of zero + should be given. If not specified, the mass number is inferred from + the name of the table. + metastable : int + Metastable level of the nuclide. Defaults to 0. + + """ + + f = h5py.File(path, 'a') + + # If element and/or mass number haven't been specified, make an educated + # guess + zaid, xs = self.name.split('.') + if element is None: + Z = int(zaid) // 1000 + element = atomic_symbol[Z] + else: + Z = atomic_number[element] + if mass_number is None: + mass_number = int(zaid) % 1000 + + # Write basic data + if metastable > 0: + name = '{}{}_m{}.{}'.format(element, mass_number, metastable, xs) + else: + name = '{}{}.{}'.format(element, mass_number, xs) + g = f.create_group(name) + g.attrs['Z'] = Z + g.attrs['A'] = mass_number + g.attrs['metastable'] = metastable + g.attrs['atomic_weight_ratio'] = self.atomic_weight_ratio + g.attrs['temperature'] = self.temperature + g.attrs['n_reaction'] = len(self.reactions) + + # Write energy grid + g.create_dataset('energy', data=self.energy) + + # Write reaction data + for i, rx in enumerate(self.reactions.values()): + rx_group = g.create_group('reaction_{}'.format(i)) + rx.to_hdf5(rx_group) + + # Write total nu data if available + if hasattr(rx, 'derived_products') and 'total_nu' not in g: + tgroup = g.create_group('total_nu') + rx.derived_products[0].to_hdf5(tgroup) + + # Write unresolved resonance probability tables + if self.urr is not None: + urr_group = g.create_group('urr') + self.urr.to_hdf5(urr_group) + + f.close() + + @classmethod + def from_hdf5(self, group): + """Generate continuous-energy neutron interaction data from HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group containing interaction data + + Returns + ------- + openmc.data.ace.NeutronTable + Continuous-energy neutron interaction data + + """ + name = group.name[1:] + atomic_weight_ratio = group.attrs['atomic_weight_ratio'] + temperature = group.attrs['temperature'] + table = NeutronTable(name, atomic_weight_ratio, temperature) + + # Read energy grid + table.energy = group['energy'].value + + # Read reaction data + n_reaction = group.attrs['n_reaction'] + + # Write reaction data + for i in range(n_reaction): + rx_group = group['reaction_{}'.format(i)] + rx = Reaction.from_hdf5(rx_group, table) + table.reactions[rx.MT] = rx + + # Read total nu data if available + if 'total_nu' in rx_group: + tgroup = rx_group['total_nu'] + rx.derived_products = [Product.from_hdf5(tgroup)] + + # Read unresolved resonance probability tables + if 'urr' in group: + urr_group = group['urr'] + table.urr = ProbabilityTables.from_hdf5(urr_group) + + return table + + +class SabTable(Table): + """A SabTable object contains thermal scattering data as represented by + an S(alpha, beta) table. + + Parameters + ---------- + name : str + ZAID identifier of the table, e.g. lwtr.10t. + atomic_weight_ratio : float + Atomic mass ratio of the target nuclide. + temperature : float + Temperature of the target nuclide in eV. + + Attributes + ---------- + atomic_weight_ratio : float + Atomic mass ratio of the target nuclide. + elastic_xs : openmc.data.Tabulated1D or openmc.data.CoherentElastic + Elastic scattering cross section derived in the coherent or incoherent + approximation + inelastic_xs : openmc.data.Tabulated1D + Inelastic scattering cross section derived in the incoherent + approximation + name : str + ZAID identifier of the table, e.g. 92235.70c. + temperature : float + Temperature of the target nuclide in eV. + + """ + + def __init__(self, name, atomic_weight_ratio, temperature): + super(SabTable, self).__init__(name, atomic_weight_ratio, temperature) + self.elastic_xs = None + self.elastic_mu_out = None + + self.inelastic_xs = None + self.inelastic_e_out = None + self.inelastic_mu_out = None + self.secondary_mode = None + + def __repr__(self): + if hasattr(self, 'name'): + return "".format(self.name) + else: + return "" + + def _read_all(self): + self._read_itie() + self._read_itce() + self._read_itxe() + self._read_itca() + + def _read_itie(self): + """Read energy-dependent inelastic scattering cross sections. + """ + idx = self._jxs[1] + n_energies = int(self._xss[idx]) + energy = self._xss[idx+1 : idx+1+n_energies] + xs = self._xss[idx+1+n_energies : idx+1+2*n_energies] + self.inelastic_xs = Tabulated1D(energy, xs) + + def _read_itce(self): + """Read energy-dependent elastic scattering cross sections. + """ + # Determine if ITCE block exists + idx = self._jxs[4] + if idx == 0: + return + + # Read values + n_energies = int(self._xss[idx]) + energy = self._xss[idx+1 : idx+1+n_energies] + P = self._xss[idx+1+n_energies : idx+1+2*n_energies] + + if self._nxs[5] == 4: + self.elastic_xs = CoherentElastic(energy, P) + else: + self.elastic_xs = Tabulated1D(energy, P) + + def _read_itxe(self): + """Read coupled energy/angle distributions for inelastic scattering. + """ + # Determine number of energies and angles + NE_in = len(self.inelastic_xs) + NE_out = self._nxs[4] + + if self._nxs[7] == 0: + self.secondary_mode = 'equal' + elif self._nxs[7] == 1: + self.secondary_mode = 'skewed' + elif self._nxs[7] == 2: + self.secondary_mode = 'continuous' + + if self.secondary_mode in ('equal', 'skewed'): + NMU = self._nxs[3] + idx = self._jxs[3] + self.inelastic_e_out = self._xss[idx:idx+NE_in*NE_out*(NMU+2):NMU+2] + self.inelastic_e_out.shape = (NE_in, NE_out) + + self.inelastic_mu_out = self._xss[idx:idx+NE_in*NE_out*(NMU+2)] + self.inelastic_mu_out.shape = (NE_in, NE_out, NMU+2) + self.inelastic_mu_out = self.inelastic_mu_out[:, :, 1:] + else: + NMU = self._nxs[3] - 1 + idx = self._jxs[3] + locc = self._xss[idx:idx + NE_in].astype(int) + NE_out = self._xss[idx + NE_in:idx + 2*NE_in].astype(int) + energy_out = [] + mu_out = [] + for i in range(NE_in): + idx = locc[i] + + # Outgoing energy distribution for incoming energy i + e = self._xss[idx + 1:idx + 1 + NE_out[i]*(NMU + 3):NMU + 3] + p = self._xss[idx + 2:idx + 2 + NE_out[i]*(NMU + 3):NMU + 3] + c = self._xss[idx + 3:idx + 3 + NE_out[i]*(NMU + 3):NMU + 3] + eout_i = Tabular(e, p, 'linear-linear', ignore_negative=True) + eout_i.c = c + + # Outgoing angle distribution for each (incoming, outgoing) energy pair + mu_i = [] + for j in range(NE_out[i]): + mu = self._xss[idx + 4:idx + 4 + NMU] + p_mu = 1./NMU*np.ones(NMU) + mu_ij = Discrete(mu, p_mu) + mu_ij.c = np.cumsum(p_mu) + mu_i.append(mu_ij) + idx += 3 + NMU + + energy_out.append(eout_i) + mu_out.append(mu_i) + + # Create correlated angle-energy distribution + breakpoints = [NE_in] + interpolation = [2] + energy = self.inelastic_xs.x + self.inelastic_dist = CorrelatedAngleEnergy( + breakpoints, interpolation, energy, energy_out, mu_out) + + def _read_itca(self): + """Read angular distributions for elastic scattering. + """ + NMU = self._nxs[6] + if self._jxs[4] == 0 or NMU == -1: + return + idx = self._jxs[6] + + NE = len(self.elastic_xs) + self.elastic_mu_out = self._xss[idx:idx+NE*NMU] + self.elastic_mu_out.shape = (NE, NMU) + + def export_to_hdf5(self, path, name): + """Export table to an HDF5 file. + + Parameters + ---------- + path : str + Path to write HDF5 file to + name : str + Name of compound (used as first group in HDF5 file) + + """ + + f = h5py.File(path, 'a') + + # Write basic data + g = f.create_group('{}.{}'.format(name, self.name.split('.')[1])) + g.attrs['atomic_weight_ratio'] = self.atomic_weight_ratio + g.attrs['temperature'] = self.temperature + g.attrs['zaids'] = self.zaids + + # Write thermal elastic scattering + if self.elastic_xs is not None: + elastic_group = g.create_group('elastic') + self.elastic_xs.to_hdf5(elastic_group, 'xs') + if self.elastic_mu_out is not None: + elastic_group.create_dataset('mu_out', data=self.elastic_mu_out) + + # Write thermal inelastic scattering + if self.inelastic_xs is not None: + inelastic_group = g.create_group('inelastic') + self.inelastic_xs.to_hdf5(inelastic_group, 'xs') + inelastic_group.attrs['secondary_mode'] = np.string_(self.secondary_mode) + if self.secondary_mode in ('equal', 'skewed'): + inelastic_group.create_dataset('energy_out', data=self.inelastic_e_out) + inelastic_group.create_dataset('mu_out', data=self.inelastic_mu_out) + elif self.secondary_mode == 'continuous': + self.inelastic_dist.to_hdf5(inelastic_group) + + @classmethod + def from_hdf5(self, group): + """Generate thermal scattering data from HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.SabTable + Neutron thermal scattering data + + """ + name = group.name[1:] + atomic_weight_ratio = group.attrs['atomic_weight_ratio'] + temperature = group.attrs['temperature'] + table = SabTable(name, atomic_weight_ratio, temperature) + table.zaids = group.attrs['zaids'] + + # Read thermal elastic scattering + if 'elastic' in group: + elastic_group = group['elastic'] + + # Cross section + elastic_xs_type = elastic_group['xs'].attrs['type'].decode() + if elastic_xs_type == 'tab1': + table.elastic_xs = Tabulated1D.from_hdf5(elastic_group['xs']) + elif elastic_xs_type == 'bragg': + table.elastic_xs = CoherentElastic.from_hdf5(elastic_group['xs']) + + # Angular distribution + if 'mu_out' in elastic_group: + table.elastic_mu_out = elastic_group['mu_out'].value + + # Read thermal inelastic scattering + if 'inelastic' in group: + inelastic_group = group['inelastic'] + table.secondary_mode = inelastic_group.attrs['secondary_mode'].decode() + table.inelastic_xs = Tabulated1D.from_hdf5(inelastic_group['xs']) + if table.secondary_mode in ('equal', 'skewed'): + table.inelastic_e_out = inelastic_group['energy_out'] + table.inelastic_mu_out = inelastic_group['mu_out'] + elif table.secondary_mode == 'continuous': + table.inelastic_dist = AngleEnergy.from_hdf5(inelastic_group) + + return table + + +class Reaction(object): + """Reaction(MT, table=None) + + A Reaction object represents a single reaction channel for a nuclide with + an associated cross section and, if present, a secondary angle and energy + distribution. These objects are stored within the ``reactions`` attribute on + subclasses of Table, e.g. NeutronTable. + + Parameters + ---------- + MT : int + The ENDF MT number for this reaction. On occasion, MCNP uses MT numbers + that don't correspond exactly to the ENDF specification. + table : openmc.data.ace.Table + The ACE table which contains this reaction. This is useful if data on + the parent nuclide is needed (for instance, the energy grid at which + cross sections are tabulated) + + Attributes + ---------- + center_of_mass : bool + Indicates whether scattering kinematics should be performed in the + center-of-mass or laboratory reference frame. + grid above the threshold value in barns. + MT : int + The ENDF MT number for this reaction. + Q_value : float + The Q-value of this reaction in MeV. + table : openmc.data.ace.Table + The ACE table which contains this reaction. + threshold : float + Threshold of the reaction in MeV + threshold_idx : int + The index on the energy grid corresponding to the threshold of this + reaction. + xs : openmc.data.Tabulated1D + Microscopic cross section for this reaction as a function of incident + energy + products : Iterable of openmc.data.Product + Reaction products + + """ + + def __init__(self, MT, table=None): + self.center_of_mass = True + self.table = table + self.MT = MT + self.Q_value = 0. + self.threshold_idx = 0 + self._xs = None + self.products = [] + + def __repr__(self): + if self.MT in reaction_name: + return "".format(self.MT, reaction_name[self.MT]) + else: + return "".format(self.MT) + + @property + def center_of_mass(self): + return self._center_of_mass + + @property + def products(self): + return self._products + + @property + def threshold(self): + return self.xs.x[0] + + @property + def xs(self): + return self._xs + + @center_of_mass.setter + def center_of_mass(self, center_of_mass): + cv.check_type('center of mass', center_of_mass, (bool, np.bool_)) + self._center_of_mass = center_of_mass + + @products.setter + def products(self, products): + cv.check_type('reaction products', products, Iterable, Product) + self._products = products + + @xs.setter + def xs(self, xs): + cv.check_type('reaction cross section', xs, Tabulated1D) + for y in xs.y: + cv.check_greater_than('reaction cross section', y, 0.0, True) + self._xs = xs + + def to_hdf5(self, group): + """Write reaction to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + + """ + + group.attrs['MT'] = self.MT + if self.MT in reaction_name: + group.attrs['label'] = np.string_(reaction_name[self.MT]) + else: + group.attrs['label'] = np.string_(self.MT) + group.attrs['Q_value'] = self.Q_value + group.attrs['threshold_idx'] = self.threshold_idx + 1 + group.attrs['center_of_mass'] = 1 if self.center_of_mass else 0 + group.attrs['n_product'] = len(self.products) + if self.xs is not None: + group.create_dataset('xs', data=self.xs.y) + for i, p in enumerate(self.products): + pgroup = group.create_group('product_{}'.format(i)) + p.to_hdf5(pgroup) + + @classmethod + def from_hdf5(cls, group, table): + """Generate reaction from an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + + Returns + ------- + openmc.data.ace.Reaction + Reaction data + + """ + MT = group.attrs['MT'] + rxn = cls(MT) + rxn.table = table + rxn.Q_value = group.attrs['Q_value'] + rxn.threshold_idx = group.attrs['threshold_idx'] - 1 + rxn.center_of_mass = bool(group.attrs['center_of_mass']) + + # Read cross section + if 'xs' in group: + xs = group['xs'].value + rxn.xs = Tabulated1D(table.energy, xs) + + # Read reaction products + n_product = group.attrs['n_product'] + products = [] + for i in range(n_product): + pgroup = group['product_{}'.format(i)] + products.append(Product.from_hdf5(pgroup)) + rxn.products = products + + return rxn + + +class DosimetryTable(Table): + def __init__(self, name, atomic_weight_ratio, temperature): + super(DosimetryTable, self).__init__( + name, atomic_weight_ratio, temperature) + + def __repr__(self): + if hasattr(self, 'name'): + return "".format(self.name) + else: + return "" + + +class NeutronDiscreteTable(Table): + + def __init__(self, name, atomic_weight_ratio, temperature): + super(NeutronDiscreteTable, self).__init__( + name, atomic_weight_ratio, temperature) + + def __repr__(self): + if hasattr(self, 'name'): + return "".format(self.name) + else: + return "" + + +class NeutronMGTable(Table): + + def __init__(self, name, atomic_weight_ratio, temperature): + super(NeutronMGTable, self).__init__( + name, atomic_weight_ratio, temperature) + + def __repr__(self): + if hasattr(self, 'name'): + return "".format(self.name) + else: + return "" + + +class PhotoatomicTable(Table): + + def __init__(self, name, atomic_weight_ratio, temperature): + super(PhotoatomicTable, self).__init__( + name, atomic_weight_ratio, temperature) + + def __repr__(self): + if hasattr(self, 'name'): + return "".format(self.name) + else: + return "" + + def _read_all(self): + self._read_eszg() + self._read_jinc() + self._read_jcoh() + self._read_heating() + self._read_compton_data() + + def _read_eszg(self): + # Determine number of energies on common energy grid + n_energies = self._nxs[3] + + # Read cross sections + idx = self._jxs[1] + data = np.asarray(self._xss[idx:idx + 5*n_energies]) + data.shape = (5, n_energies) + self.energy = data[0] + self.incoherent = data[1] + self.coherent = data[2] + self.photoelectric = data[3] + self.pairproduction = data[4] + + def _read_jinc(self): + # Read incoherent scattering function + idx = self._jxs[2] + self.incoherent_scattering = self._xss[idx:idx + 21] + + def _read_jcoh(self): + # Read coherent form factors and integrated coherent form factors + idx = self._jxs[3] + self.int_coherent_form_factors = self._xss[idx:idx + 55] + self.coherent_form_factors = self._xss[idx + 55:idx + 2*55] + + def _read_jflo(self): + raise NotImplementedError + + def _read_heating(self): + idx = self._jxs[5] + self.avg_heating = self._xss[idx:idx + self._nxs[3]] + + def _read_compton_data(self): + # Determine number of Compton profiles + n_shells = self._nxs[5] + + if n_shells > 0: + # Number of electrons per shell + idx = self._jxs[6] + self.electrons_per_shell = np.asarray( + self._xss[idx:idx + n_shells], dtype=int) + + # Binding energy per shell + idx = self._jxs[7] + self.binding_energy_per_shell = self._xss[idx:idx + n_shells] + + # Probability of interaction per shell + idx = self._jxs[8] + self.probability_per_shell = self._xss[idx:idx + n_shells] + + # Initialize arrays for Compton profile data + self.compton_profile_interp = np.zeros(n_shells) + self.compton_profile_momentum = [] + self.compton_profile_pdf = [] + self.compton_profile_cdf = [] + + for i in range(n_shells): + # Get locator for SWD block + loca = int(self._xss[self._jxs[9] + i]) + idx = self._jxs[10] + loca - 1 + + # Get interpolation parameter and number of momentum entries + self.compton_profile_interp[i] = int(self._xss[idx]) + n_momentum = int(self._xss[idx + 1]) + idx += 2 + + # Get momentum entries, PDF, and CDF + data = self._xss[idx:idx + 3*n_momentum] + data.shape = (3, n_momentum) + self.compton_profile_momentum.append(data[0]) + self.compton_profile_pdf.append(data[1]) + self.compton_profile_cdf.append(data[2]) + + +class PhotoatomicMGTable(Table): + + def __init__(self, name, atomic_weight_ratio, temperature): + super(PhotoatomicMGTable, self).__init__( + name, atomic_weight_ratio, temperature) + + def __repr__(self): + if hasattr(self, 'name'): + return "".format(self.name) + else: + return "" + + +class ElectronTable(Table): + + def __init__(self, name, atomic_weight_ratio, temperature): + super(ElectronTable, self).__init__( + name, atomic_weight_ratio, temperature) + + def __repr__(self): + if hasattr(self, 'name'): + return "".format(self.name) + else: + return "" + + +class PhotonuclearTable(Table): + + def __init__(self, name, atomic_weight_ratio, temperature): + super(PhotonuclearTable, self).__init__( + name, atomic_weight_ratio, temperature) + self.reactions = OrderedDict() + + def __repr__(self): + if hasattr(self, 'name'): + return "".format(self.name) + else: + return "" + + def _read_all(self): + self._read_basic() + self._read_cross_sections() + self._read_secondaries() + self._read_angular_distributions() + self._read_energy_distributions() + + def _read_basic(self): + n_energies = self._nxs[3] + + # Read energy mesh + idx = self._jxs[1] + self.energy = self._xss[idx:idx + n_energies] + + # Read total cross section + idx = self._jxs[2] + self.total_xs = self._xss[idx:idx + n_energies] + + # Read non-elastic and elastic cross section + if self._jxs[4] > 0: + idx = self._jxs[3] + self.non_elastic_xs = self._xss[idx:idx + n_energies] + idx = self._jxs[4] + self.elastic_xs = self._xss[idx:idx + n_energies] + else: + self.non_elastic_xs = self.total_xs.copy() + self.elastic_xs = np.zeros(n_energies) + + # Read heating numbers + idx = self._jxs[5] + if idx > 0: + self.heating_number = self._xss[idx:idx + n_energies] + else: + self.heating_number = np.zeros(n_energies) + + def _read_cross_sections(self): + # Determine number of reactions + n_reactions = self._nxs[4] + + # Read list of MT numbers and Q values + mts = np.asarray(self._xss[self._jxs[6]:self._jxs[6] + + n_reactions], dtype=int) + qvalues = np.asarray(self._xss[self._jxs[7]:self._jxs[7] + + n_reactions]) + + # Create reactions in dictionary + reactions = [(mt, Reaction(mt, self)) for mt in mts] + self.reactions.update(reactions) + + for i, rx in enumerate(self.reactions.values()): + # Copy Q values + rx.Q_value = qvalues[i] + + # Determine starting index on energy grid and number of energies + idx = self._jxs[9] + int(self._xss[self._jxs[8] + i]) - 1 + rx.threshold_idx = int(self._xss[idx]) + n_energies = int(self._xss[idx + 1]) + energy = self.energy[rx.threshold_idx:rx.threshold_idx + n_energies] + idx += 2 + + # Read reaction cross setion + xs = self._xss[idx:idx + n_energies] + rx.xs = Tabulated1D(energy, xs, [], []) + + def _read_secondaries(self): + names = {1: 'neutron', 2: 'photon', 3: 'electron', + 9: 'proton', 31: 'deuteron', 32: 'triton', + 33: 'helium3', 34: 'alpha'} + + n_particles = self._nxs[5] + n_entries = self._nxs[7] + + idx = self._jxs[10] + ixs = np.asarray(self._xss[idx:idx + n_particles*n_entries], dtype=int) + ixs.shape = (n_particles, n_entries) + self.ixs = ixs.transpose() + + self.particles = [] + + for j in range(n_particles): + # Create dictionary for particle + particle = {} + self.particles.append(particle) + + # Get secondary particle type/name + particle['ipt'] = self.ixs[0, j] + particle['name'] = names[particle['ipt']] + + # Number of reactions that produce secondary particle + n_producing = self.ixs[1, j] + + # Particle-production cross section + idx = self.ixs[2, j] + particle['ie_production'] = int(self._xss[idx]) + ne = int(self._xss[idx + 1]) + idx += 2 + particle['production'] = self._xss[idx:idx + ne] + + # Average heating numbers + idx = self.ixs[3, j] + particle['ie_heating'] = int(self._xss[idx]) + ne = int(self._xss[idx + 1]) + idx += 2 + particle['heating_number'] = self._xss[idx:idx + ne] + + # MTs of particle production reactions + idx = self.ixs[4, j] + particle['mt_producing'] = np.asarray( + self._xss[idx:idx + n_producing], dtype=int) + + # Coordinate system of reaction producing secondary particle + idx = self.ixs[5, j] + particle['center_of_mass'] = [i < 0 for i in + self._xss[idx:idx + n_producing]] + + # Reaction yields + particle['yield'] = {} + for k in range(n_producing): + # Create dictionary for yield data + yieldData = {} + + # Read reaction yield data for a single MT + idx = self.ixs[7, j] + int(self._xss[self.ixs[6, j] + k]) - 1 + + yieldData['mftype'] = int(self._xss[idx]) + idx += 1 + + if yieldData['mftype'] in (6, 12, 16): + # Yield data from ENDF File 6 or 12 + mtmult = int(self._xss[idx]) + assert mtmult == particle['mt_producing'][k] + + # Read yield as function of energy + yieldData['multiplicity'] = _get_tabulated_1d( + self._xss, idx + 1) + + elif yieldData['mftype'] == 13: + # Production cross section for corresponding MT + yieldData['ie'] = int(self._xss[idx]) + ne = int(self._xss[idx + 1]) + idx += 2 + yieldData['cross_section'] = self._xss[idx:idx + ne] + + # Add reaction yield data to dictionary + mt = particle['mt_producing'][k] + particle['yield'][mt] = yieldData + + def _read_angular_distributions(self): + for j, particle in enumerate(self.particles): + # Create dictionary for angular distributions + angular_dists = {} + particle['angular_distribution'] = angular_dists + + for k, mt in enumerate(particle['mt_producing']): + landp = int(self._xss[self.ixs[8, j] + k]) + + # check if angular distribution data exists + if landp == -1: + # Angular distribution data are specified through the + # DLWP block + continue + elif landp == 0: + # No angular distribution data are given for this + # reaction, isotropic scattering is assumed + ie = self.reactions[mt].threshold_idx + ne = len(self.reactions[mt].sigma) + angular_dists[mt] = AngularDistribution.isotropic( + np.array([self.energy[ie], self.energy[ie + ne - 1]])) + continue + + idx = self.ixs[9, j] + landp - 1 + + angular_dists[mt] = AngularDistribution() + angular_dists[mt].read(self._xss, idx, self.ixs[9, j]) + + def _read_energy_distributions(self): + for j, particle in enumerate(self.particles): + # Create dictionary for energy distributions + energy_dists = {} + particle['energy_distribution'] = energy_dists + + for k, mt in enumerate(particle['mt_producing']): + # Determine locator for kth energy distribution + ldlwp = int(self._xss[self.ixs[10, j] + k]) + + # Read energy distribution data + energy_dists[mt] = self._get_energy_distribution( + self.ixs[11, j], ldlwp) +table_types = { + "c": NeutronTable, + "t": SabTable, + "y": DosimetryTable, + "d": NeutronDiscreteTable, + "p": PhotoatomicTable, + "m": NeutronMGTable, + "g": PhotoatomicMGTable, + "e": ElectronTable, + "u": PhotonuclearTable} diff --git a/openmc/data/angle_distribution.py b/openmc/data/angle_distribution.py new file mode 100644 index 000000000..5f19450bc --- /dev/null +++ b/openmc/data/angle_distribution.py @@ -0,0 +1,134 @@ +from collections import Iterable +from numbers import Real + +import numpy as np + +import openmc.checkvalue as cv +from openmc.stats import Univariate, Tabular +from .container import interpolation_scheme + + +class AngleDistribution(object): + """Angle distribution as a function of incoming energy + + Parameters + ---------- + energy : Iterable of float + Incoming energies at which distributions exist + mu : Iterable of openmc.stats.Univariate + Distribution of scattering cosines corresponding to each incoming energy + + Attributes + ---------- + energy : Iterable of float + Incoming energies at which distributions exist + mu : Iterable of openmc.stats.Univariate + Distribution of scattering cosines corresponding to each incoming energy + + """ + + def __init__(self, energy, mu): + super(AngleDistribution, self).__init__() + self.energy = energy + self.mu = mu + + @property + def energy(self): + return self._energy + + @property + def mu(self): + return self._mu + + @energy.setter + def energy(self, energy): + cv.check_type('angle distribution incoming energy', energy, + Iterable, Real) + self._energy = energy + + @mu.setter + def mu(self, mu): + cv.check_type('angle distribution scattering cosines', mu, + Iterable, Univariate) + self._mu = mu + + def to_hdf5(self, group): + """Write angle distribution to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + + """ + + dset = group.create_dataset('energy', data=self.energy) + + # Make sure all data is tabular + mu_tabular = [mu_i if isinstance(mu_i, Tabular) else + mu_i.to_tabular() for mu_i in self.mu] + + # Determine total number of (mu,p) pairs and create array + n_pairs = sum([len(mu_i.x) for mu_i in mu_tabular]) + pairs = np.empty((3, n_pairs)) + + # Create array for offsets + offsets = np.empty(len(mu_tabular), dtype=int) + interpolation = np.empty(len(mu_tabular), dtype=int) + j = 0 + + # Populate offsets and pairs array + for i, mu_i in enumerate(mu_tabular): + n = len(mu_i.x) + offsets[i] = j + interpolation[i] = 1 if mu_i.interpolation == 'histogram' else 2 + pairs[0, j:j+n] = mu_i.x + pairs[1, j:j+n] = mu_i.p + pairs[2, j:j+n] = mu_i.c + j += n + + # Create dataset for distributions + dset = group.create_dataset('mu', data=pairs) + + # Write interpolation as attribute + dset.attrs['offsets'] = offsets + dset.attrs['interpolation'] = interpolation + + @classmethod + def from_hdf5(cls, group): + """Generate angular distribution from HDF5 data + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.AngleDistribution + Angular distribution + + """ + energy = group['energy'].value + data = group['mu'] + offsets = data.attrs['offsets'] + interpolation = data.attrs['interpolation'] + + mu = [] + n_energy = len(energy) + for i in range(n_energy): + # Determine length of outgoing energy distribution and number of + # discrete lines + j = offsets[i] + if i < n_energy - 1: + n = offsets[i+1] - j + else: + n = data.shape[1] - j + + interp = interpolation_scheme[interpolation[i]] + mu_i = Tabular(data[0, j:j+n], data[1, j:j+n], interp) + mu_i.c = data[2, j:j+n] + + mu.append(mu_i) + + return cls(energy, mu) diff --git a/openmc/data/angle_energy.py b/openmc/data/angle_energy.py new file mode 100644 index 000000000..fc17d7bec --- /dev/null +++ b/openmc/data/angle_energy.py @@ -0,0 +1,40 @@ +from abc import ABCMeta, abstractmethod + +import numpy as np + +import openmc.data + + +class AngleEnergy(object): + """Distribution in angle and energy of a secondary particle.""" + + __metaclass = ABCMeta + + @abstractmethod + def to_hdf5(self, group): + pass + + @staticmethod + def from_hdf5(group): + """Generate angle-energy distribution from HDF5 data + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.AngleEnergy + Angle-energy distribution + + """ + dist_type = group.attrs['type'].decode() + if dist_type == 'uncorrelated': + return openmc.data.UncorrelatedAngleEnergy.from_hdf5(group) + elif dist_type == 'correlated': + return openmc.data.CorrelatedAngleEnergy.from_hdf5(group) + elif dist_type == 'kalbach-mann': + return openmc.data.KalbachMann.from_hdf5(group) + elif dist_type == 'nbody': + return openmc.data.NBodyPhaseSpace.from_hdf5(group) diff --git a/openmc/data/container.py b/openmc/data/container.py new file mode 100644 index 000000000..4bd293c36 --- /dev/null +++ b/openmc/data/container.py @@ -0,0 +1,269 @@ +from collections import Iterable +from numbers import Real, Integral + +import numpy as np + +import openmc.checkvalue as cv + +interpolation_scheme = {1: 'histogram', 2: 'linear-linear', 3: 'linear-log', + 4: 'log-linear', 5: 'log-log'} + + +class Tabulated1D(object): + """A one-dimensional tabulated function. + + This class mirrors the TAB1 type from the ENDF-6 format. A tabulated + function is specified by tabulated (x,y) pairs along with interpolation + rules that determine the values between tabulated pairs. + + Once an object has been created, it can be used as though it were an actual + function, e.g.: + + >>> f = Tabulated1D([0, 10], [4, 5]) + >>> [f(xi) for xi in numpy.linspace(0, 10, 5)] + [4.0, 4.25, 4.5, 4.75, 5.0] + + Parameters + ---------- + x : Iterable of float + Independent variable + y : Iterable of float + Dependent variable + breakpoints : Iterable of int + Breakpoints for interpolation regions + interpolation : Iterable of int + Interpolation scheme identification number, e.g., 3 means y is linear in + ln(x). + + Attributes + ---------- + x : Iterable of float + Independent variable + y : Iterable of float + Dependent variable + breakpoints : Iterable of int + Breakpoints for interpolation regions + interpolation : Iterable of int + Interpolation scheme identification number, e.g., 3 means y is linear in + ln(x). + n_regions : int + Number of interpolation regions + n_pairs : int + Number of tabulated (x,y) pairs + + """ + + def __init__(self, x, y, breakpoints=None, interpolation=None): + if breakpoints is None or interpolation is None: + # Single linear-linear interpolation region by default + self.breakpoints = np.array([len(x)]) + self.interpolation = np.array([2]) + else: + self.breakpoints = np.asarray(breakpoints, dtype=int) + self.interpolation = np.asarray(interpolation, dtype=int) + + self.x = np.asarray(x) + self.y = np.asarray(y) + + def __call__(self, x): + # Check if input is array or scalar + if isinstance(x, Iterable): + iterable = True + x = np.array(x) + else: + iterable = False + x = np.array([x], dtype=float) + + # Create output array + y = np.zeros_like(x) + + # Get indices for interpolation + idx = np.searchsorted(self.x, x, side='right') - 1 + + # Find lowest valid index + i_low = np.searchsorted(idx, 0) + + for k in range(len(self.breakpoints)): + # Determine which x values are within this interpolation range + i_high = np.searchsorted(idx, self.breakpoints[k] - 1) + + # Get x values and bounding (x,y) pairs + xk = x[i_low:i_high] + xi = self.x[idx[i_low:i_high]] + xi1 = self.x[idx[i_low:i_high] + 1] + yi = self.y[idx[i_low:i_high]] + yi1 = self.y[idx[i_low:i_high] + 1] + + if self.interpolation[k] == 1: + # Histogram + y[i_low:i_high] = yi + + elif self.interpolation[k] == 2: + # Linear-linear + y[i_low:i_high] = yi + (xk - xi)/(xi1 - xi)*(yi1 - yi) + + elif self.interpolation[k] == 3: + # Linear-log + y[i_low:i_high] = yi + np.log(xk/xi)/np.log(xi1/xi)*(yi1 - yi) + + elif self.interpolation[k] == 4: + # Log-linear + y[i_low:i_high] = yi*np.exp((xk - xi)/(xi1 - xi)*np.log(yi1/yi)) + + elif self.interpolation[k] == 5: + # Log-log + y[i_low:i_high] = yi*np.exp(np.log(xk/xi)/np.log(xi1/xi)*np.log(yi1/yi)) + + i_low = i_high + + # In some cases, the first/last point of x may be less than the first + # value of self.x due only to precision, so we check if they're close + # and set them equal if so. Otherwise, the interpolated value might be + # out of range (and thus zero) + if np.isclose(x[0], self.x[0], 1e-8): + y[0] = self.y[0] + if np.isclose(x[-1], self.x[-1], 1e-8): + y[-1] = self.y[-1] + + return y if iterable else y[0] + + def __len__(self): + return len(self.x) + + @property + def x(self): + return self._x + + @property + def y(self): + return self._y + + @property + def breakpoints(self): + return self._breakpoints + + @property + def interpolation(self): + return self._interpolation + + @property + def n_pairs(self): + return len(self.x) + + @property + def n_regions(self): + return len(self.breakpoints) + + @x.setter + def x(self, x): + cv.check_type('x values', x, Iterable, Real) + self._x = x + + @y.setter + def y(self, y): + cv.check_type('y values', y, Iterable, Real) + self._y = y + + @breakpoints.setter + def breakpoints(self, breakpoints): + cv.check_type('breakpoints', breakpoints, Iterable, Integral) + self._breakpoints = breakpoints + + @interpolation.setter + def interpolation(self, interpolation): + cv.check_type('interpolation', interpolation, Iterable, Integral) + self._interpolation = interpolation + + def integral(self): + """Integral of the tabulated function over its tabulated range. + + Returns + ------- + numpy.ndarray + Array of same length as the tabulated data that represents partial + integrals from the bottom of the range to each tabulated point. + + """ + + # Create output array + partial_sum = np.zeros(len(self.x) - 1) + + i_low = 0 + for k in range(len(self.breakpoints)): + # Determine which x values are within this interpolation range + i_high = self.breakpoints[k] - 1 + + # Get x values and bounding (x,y) pairs + x0 = self.x[i_low:i_high] + x1 = self.x[i_low + 1:i_high + 1] + y0 = self.y[i_low:i_high] + y1 = self.y[i_low + 1:i_high + 1] + + if self.interpolation[k] == 1: + # Histogram + partial_sum[i_low:i_high] = y0*(x1 - x0) + + elif self.interpolation[k] == 2: + # Linear-linear + m = (y1 - y0)/(x1 - x0) + partial_sum[i_low:i_high] = (y0 - m*x0)*(x1 - x0) + \ + m*(x1**2 - x0**2)/2 + + elif self.interpolation[k] == 3: + # Linear-log + logx = np.log(x1/x0) + m = (y1 - y0)/logx + partial_sum[i_low:i_high] = y0 + m*(x1*(logx - 1) + x0) + + elif self.interpolation[k] == 4: + # Log-linear + m = np.log(y1/y0)/(x1 - x0) + partial_sum[i_low:i_high] = y0/m*(np.exp(m*(x1 - x0)) - 1) + + elif self.interpolation[k] == 5: + # Log-log + m = np.log(y1/y0)/np.log(x1/x0) + partial_sum[i_low:i_high] = y0/((m + 1)*x0**m)*( + x1**(m + 1) - x0**(m + 1)) + + i_low = i_high + + return np.concatenate(([0.], np.cumsum(partial_sum))) + + def to_hdf5(self, group, name='xy'): + """Write tabulated function to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + name : str + Name of the dataset to create + + """ + dataset = group.create_dataset(name, data=np.vstack( + [self.x, self.y])) + dataset.attrs['type'] = np.string_('tab1') + dataset.attrs['breakpoints'] = self.breakpoints + dataset.attrs['interpolation'] = self.interpolation + + @classmethod + def from_hdf5(cls, dataset): + """Generate tabulated function from an HDF5 dataset + + Parameters + ---------- + dataset : h5py.Dataset + Dataset to read from + + Returns + ------- + openmc.data.Tabulated1D + Function read from dataset + + """ + x = dataset.value[0,:] + y = dataset.value[1,:] + breakpoints = dataset.attrs['breakpoints'] + interpolation = dataset.attrs['interpolation'] + return cls(x, y, breakpoints, interpolation) diff --git a/openmc/data/correlated.py b/openmc/data/correlated.py new file mode 100644 index 000000000..58a90da9d --- /dev/null +++ b/openmc/data/correlated.py @@ -0,0 +1,291 @@ +from collections import Iterable +from numbers import Real, Integral + +import numpy as np + +import openmc.checkvalue as cv +from openmc.stats import Tabular, Univariate, Discrete, Mixture +from .container import interpolation_scheme +from .angle_energy import AngleEnergy + + +class CorrelatedAngleEnergy(AngleEnergy): + """Correlated angle-energy distribution + + Parameters + ---------- + breakpoints : Iterable of int + Breakpoints defining interpolation regions + interpolation : Iterable of int + Interpolation codes + energy : Iterable of float + Incoming energies at which distributions exist + energy_out : Iterable of openmc.stats.Univariate + Distribution of outgoing energies corresponding to each incoming energy + mu : Iterable of Iterable of openmc.stats.Univariate + Distribution of scattering cosine for each incoming/outgoing energy + + Attributes + ---------- + breakpoints : Iterable of int + Breakpoints defining interpolation regions + interpolation : Iterable of int + Interpolation codes + energy : Iterable of float + Incoming energies at which distributions exist + energy_out : Iterable of openmc.stats.Univariate + Distribution of outgoing energies corresponding to each incoming energy + mu : Iterable of Iterable of openmc.stats.Univariate + Distribution of scattering cosine for each incoming/outgoing energy + + """ + + def __init__(self, breakpoints, interpolation, energy, energy_out, mu): + super(CorrelatedAngleEnergy, self).__init__() + self.breakpoints = breakpoints + self.interpolation = interpolation + self.energy = energy + self.energy_out = energy_out + self.mu = mu + + @property + def breakpoints(self): + return self._breakpoints + + @property + def interpolation(self): + return self._interpolation + @property + def energy(self): + return self._energy + + @property + def energy_out(self): + return self._energy_out + + @property + def mu(self): + return self._mu + + @breakpoints.setter + def breakpoints(self, breakpoints): + cv.check_type('correlated angle-energy breakpoints', breakpoints, + Iterable, Integral) + self._breakpoints = breakpoints + + @interpolation.setter + def interpolation(self, interpolation): + cv.check_type('correlated angle-energy interpolation', interpolation, + Iterable, Integral) + self._interpolation = interpolation + + @energy.setter + def energy(self, energy): + cv.check_type('correlated angle-energy incoming energy', energy, + Iterable, Real) + self._energy = energy + + @energy_out.setter + def energy_out(self, energy_out): + cv.check_type('correlated angle-energy outgoing energy', energy_out, + Iterable, Univariate) + self._energy_out = energy_out + + @mu.setter + def mu(self, mu): + cv.check_iterable_type('correlated angle-energy outgoing cosine', + mu, Univariate, 2, 2) + self._mu = mu + + def to_hdf5(self, group): + """Write distribution to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + + """ + group.attrs['type'] = np.string_('correlated') + + dset = group.create_dataset('energy', data=self.energy) + dset.attrs['interpolation'] = np.vstack((self.breakpoints, + self.interpolation)) + + # Determine total number of (E,p) pairs and create array + n_tuple = sum(len(d.x) for d in self.energy_out) + eout = np.empty((5, n_tuple)) + + # Make sure all mu data is tabular + mu_tabular = [] + for i, mu_i in enumerate(self.mu): + mu_tabular.append([mu_ij if isinstance(mu_ij, (Tabular, Discrete)) else + mu_ij.to_tabular() for mu_ij in mu_i]) + + # Determine total number of (mu,p) points and create array + n_tuple = sum(sum(len(mu_ij.x) for mu_ij in mu_i) + for mu_i in mu_tabular) + mu = np.empty((3, n_tuple)) + + # Create array for offsets + offsets = np.empty(len(self.energy_out), dtype=int) + interpolation = np.empty(len(self.energy_out), dtype=int) + n_discrete_lines = np.empty(len(self.energy_out), dtype=int) + offset_e = 0 + offset_mu = 0 + + # Populate offsets and eout array + for i, d in enumerate(self.energy_out): + n = len(d) + offsets[i] = offset_e + + if isinstance(d, Mixture): + discrete, continuous = d.distribution + n_discrete_lines[i] = m = len(discrete) + interpolation[i] = 1 if continuous.interpolation == 'histogram' else 2 + eout[0, offset_e:offset_e+m] = discrete.x + eout[1, offset_e:offset_e+m] = discrete.p + eout[2, offset_e:offset_e+m] = discrete.c + eout[0, offset_e+m:offset_e+n] = continuous.x + eout[1, offset_e+m:offset_e+n] = continuous.p + eout[2, offset_e+m:offset_e+n] = continuous.c + else: + if isinstance(d, Tabular): + n_discrete_lines[i] = 0 + interpolation[i] = 1 if d.interpolation == 'histogram' else 2 + elif isinstance(d, Discrete): + n_discrete_lines[i] = n + interpolation[i] = 1 + eout[0, offset_e:offset_e+n] = d.x + eout[1, offset_e:offset_e+n] = d.p + eout[2, offset_e:offset_e+n] = d.c + + for j, mu_ij in enumerate(mu_tabular[i]): + if isinstance(mu_ij, Discrete): + eout[3, offset_e+j] = 0 + else: + eout[3, offset_e+j] = 1 if mu_ij.interpolation == 'histogram' else 2 + eout[4, offset_e+j] = offset_mu + + n_mu = len(mu_ij) + mu[0, offset_mu:offset_mu+n_mu] = mu_ij.x + mu[1, offset_mu:offset_mu+n_mu] = mu_ij.p + mu[2, offset_mu:offset_mu+n_mu] = mu_ij.c + + offset_mu += n_mu + + offset_e += n + + # Create dataset for outgoing energy distributions + dset = group.create_dataset('energy_out', data=eout) + + # Write interpolation on outgoing energy as attribute + dset.attrs['offsets'] = offsets + dset.attrs['interpolation'] = interpolation + dset.attrs['n_discrete_lines'] = n_discrete_lines + + # Create dataset for outgoing angle distributions + group.create_dataset('mu', data=mu) + + @classmethod + def from_hdf5(cls, group): + """Generate correlated angle-energy distribution from HDF5 data + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.CorrelatedAngleEnergy + Correlated angle-energy distribution + + """ + interp_data = group['energy'].attrs['interpolation'] + energy_breakpoints = interp_data[0,:] + energy_interpolation = interp_data[1,:] + energy = group['energy'].value + + offsets = group['energy_out'].attrs['offsets'] + interpolation = group['energy_out'].attrs['interpolation'] + n_discrete_lines = group['energy_out'].attrs['n_discrete_lines'] + dset_eout = group['energy_out'].value + energy_out = [] + + dset_mu = group['mu'].value + mu = [] + + n_energy = len(energy) + for i in range(n_energy): + # Determine length of outgoing energy distribution and number of + # discrete lines + offset_e = offsets[i] + if i < n_energy - 1: + n = offsets[i+1] - offset_e + else: + n = dset_eout.shape[1] - offset_e + m = n_discrete_lines[i] + + # Create discrete distribution if lines are present + if m > 0: + x = dset_eout[0, offset_e:offset_e+m] + p = dset_eout[1, offset_e:offset_e+m] + eout_discrete = Discrete(x, p) + eout_discrete.c = dset_eout[2, offset_e:offset_e+m] + p_discrete = eout_discrete.c[-1] + + # Create continuous distribution + if m < n: + interp = interpolation_scheme[interpolation[i]] + + x = dset_eout[0, offset_e+m:offset_e+n] + p = dset_eout[1, offset_e+m:offset_e+n] + eout_continuous = Tabular(x, p, interp, ignore_negative=True) + eout_continuous.c = dset_eout[2, offset_e+m:offset_e+n] + + # If both continuous and discrete are present, create a mixture + # distribution + if m == 0: + eout_i = eout_continuous + elif m == n: + eout_i = eout_discrete + else: + eout_i = Mixture([p_discrete, 1. - p_discrete], + [eout_discrete, eout_continuous]) + + # Read angular distributions + mu_i = [] + for j in range(n): + # Determine interpolation scheme + interp_code = int(dset_eout[3, offsets[i] + j]) + + # Determine offset and length + offset_mu = int(dset_eout[4, offsets[i] + j]) + if offsets[i] + j < dset_eout.shape[1] - 1: + n_mu = int(dset_eout[4, offsets[i] + j + 1]) - offset_mu + else: + n_mu = dset_mu.shape[1] - offset_mu + + # Get data + x = dset_mu[0, offset_mu:offset_mu+n_mu] + p = dset_mu[1, offset_mu:offset_mu+n_mu] + c = dset_mu[2, offset_mu:offset_mu+n_mu] + + if interp_code == 0: + mu_ij = Discrete(x, p) + else: + mu_ij = Tabular(x, p, interpolation_scheme[interp_code], + ignore_negative=True) + mu_ij.c = c + mu_i.append(mu_ij) + + offset_mu += n_mu + + energy_out.append(eout_i) + mu.append(mu_i) + + j += n + + return cls(energy_breakpoints, energy_interpolation, + energy, energy_out, mu) diff --git a/openmc/data/data.py b/openmc/data/data.py index c6dd81ba6..e5aa55285 100644 --- a/openmc/data/data.py +++ b/openmc/data/data.py @@ -99,3 +99,60 @@ natural_abundance = { 'Bi-209': 1.0, 'Th-232': 1.0, 'Pa-231': 1.0, 'U-234': 5.4e-05, 'U-235': 0.007204, 'U-238': 0.992742 } + +atomic_symbol = {1: 'H', 2: 'He', 3: 'Li', 4: 'Be', 5: 'B', 6: 'C', 7: 'N', + 8: 'O', 9: 'F', 10: 'Ne', 11: 'Na', 12: 'Mg', 13: 'Al', + 14: 'Si', 15: 'P', 16: 'S', 17: 'Cl', 18: 'Ar', 19: 'K', + 20: 'Ca', 21: 'Sc', 22: 'Ti', 23: 'V', 24: 'Cr', 25: 'Mn', + 26: 'Fe', 27: 'Co', 28: 'Ni', 29: 'Cu', 30: 'Zn', 31: 'Ga', + 32: 'Ge', 33: 'As', 34: 'Se', 35: 'Br', 36: 'Kr', 37: 'Rb', + 38: 'Sr', 39: 'Y', 40: 'Zr', 41: 'Nb', 42: 'Mo', 43: 'Tc', + 44: 'Ru', 45: 'Rh', 46: 'Pd', 47: 'Ag', 48: 'Cd', 49: 'In', + 50: 'Sn', 51: 'Sb', 52: 'Te', 53: 'I', 54: 'Xe', 55: 'Cs', + 56: 'Ba', 57: 'La', 58: 'Ce', 59: 'Pr', 60: 'Nd', 61: 'Pm', + 62: 'Sm', 63: 'Eu', 64: 'Gd', 65: 'Tb', 66: 'Dy', 67: 'Ho', + 68: 'Er', 69: 'Tm', 70: 'Yb', 71: 'Lu', 72: 'Hf', 73: 'Ta', + 74: 'W', 75: 'Re', 76: 'Os', 77: 'Ir', 78: 'Pt', 79: 'Au', + 80: 'Hg', 81: 'Tl', 82: 'Pb', 83: 'Bi', 84: 'Po', 85: 'At', + 86: 'Rn', 87: 'Fr', 88: 'Ra', 89: 'Ac', 90: 'Th', 91: 'Pa', + 92: 'U', 93: 'Np', 94: 'Pu', 95: 'Am', 96: 'Cm', 97: 'Bk', + 98: 'Cf', 99: 'Es', 100: 'Fm', 101: 'Md', 102: 'No', + 103: 'Lr', 104: 'Rf', 105: 'Db', 106: 'Sg', 107: 'Bh', + 108: 'Hs', 109: 'Mt', 110: 'Ds', 111: 'Rg', 112: 'Cn', + 114: 'Fl', 116: 'Lv'} +atomic_number = {value: key for key, value in atomic_symbol.items()} + +reaction_name = {1: '(n,total)', 2: '(n,elastic)', 4: '(n,level)', 5: '(n,misc)', 11: '(n,2nd)', + 16: '(n,2n)', 17: '(n,3n)', 18: '(n,fission)', 19: '(n,f)', + 20: '(n,nf)', 21: '(n,2nf)', 22: '(n,na)', 23: '(n,n3a)', + 24: '(n,2na)', 25: '(n,3na)', 28: '(n,np)', 29: '(n,n2a)', + 30: '(n,2n2a)', 32: '(n,nd)', 33: '(n,nt)', 34: '(n,nHe-3)', + 35: '(n,nd2a)', 36: '(n,nt2a)', 37: '(n,4n)', 38: '(n,3nf)', + 41: '(n,2np)', 42: '(n,3np)', 44: '(n,n2p)', 45: '(n,npa)', + 91: '(n,nc)', 101: '(n,disappear)', 102: '(n,gamma)', + 103: '(n,p)', 104: '(n,d)', 105: '(n,t)', 106: '(n,3He)', + 107: '(n,a)', 108: '(n,2a)', 109: '(n,3a)', 111: '(n,2p)', + 112: '(n,pa)', 113: '(n,t2a)', 114: '(n,d2a)', 115: '(n,pd)', + 116: '(n,pt)', 117: '(n,da)', 152: '(n,5n)', 153: '(n,6n)', + 154: '(n,2nt)', 155: '(n,ta)', 156: '(n,4np)', 157: '(n,3nd)', + 158: '(n,nda)', 159: '(n,2npa)', 160: '(n,7n)', 161: '(n,8n)', + 162: '(n,5np)', 163: '(n,6np)', 164: '(n,7np)', 165: '(n,4na)', + 166: '(n,5na)', 167: '(n,6na)', 168: '(n,7na)', 169: '(n,4nd)', + 170: '(n,5nd)', 171: '(n,6nd)', 172: '(n,3nt)', 173: '(n,4nt)', + 174: '(n,5nt)', 175: '(n,6nt)', 176: '(n,2n3He)', + 177: '(n,3n3He)', 178: '(n,4n3He)', 179: '(n,3n2p)', + 180: '(n,3n3a)', 181: '(n,3npa)', 182: '(n,dt)', + 183: '(n,npd)', 184: '(n,npt)', 185: '(n,ndt)', + 186: '(n,np3He)', 187: '(n,nd3He)', 188: '(n,nt3He)', + 189: '(n,nta)', 190: '(n,2n2p)', 191: '(n,p3He)', + 192: '(n,d3He)', 193: '(n,3Hea)', 194: '(n,4n2p)', + 195: '(n,4n2a)', 196: '(n,4npa)', 197: '(n,3p)', + 198: '(n,n3p)', 199: '(n,3n2pa)', 200: '(n,5n2p)', 444: '(n,damage)', + 649: '(n,pc)', 699: '(n,dc)', 749: '(n,tc)', 799: '(n,3Hec)', + 849: '(n,ac)'} +reaction_name.update({i: '(n,n{})'.format(i-50) for i in range(50,91)}) +reaction_name.update({i: '(n,p{})'.format(i-600) for i in range(600,649)}) +reaction_name.update({i: '(n,d{})'.format(i-650) for i in range(650,699)}) +reaction_name.update({i: '(n,t{})'.format(i-700) for i in range(700,749)}) +reaction_name.update({i: '(n,3He{})'.format(i-750) for i in range(750,799)}) +reaction_name.update({i: '(n,a{})'.format(i-800) for i in range(800,849)}) diff --git a/openmc/data/energy_distribution.py b/openmc/data/energy_distribution.py new file mode 100644 index 000000000..afbc39014 --- /dev/null +++ b/openmc/data/energy_distribution.py @@ -0,0 +1,850 @@ +from abc import ABCMeta, abstractmethod +from collections import Iterable +from numbers import Integral, Real + +import h5py +import numpy as np + +from openmc.data.container import Tabulated1D, interpolation_scheme +from openmc.stats.univariate import Univariate, Tabular, Discrete, Mixture +import openmc.checkvalue as cv + + +class EnergyDistribution(object): + """Abstract superclass for all energy distributions.""" + + __metaclass__ = ABCMeta + + def __init__(self): + pass + + @abstractmethod + def to_hdf5(self, group): + pass + + @staticmethod + def from_hdf5(group): + """Generate energy distribution from HDF5 data + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.EnergyDistribution + Energy distribution + + """ + energy_type = group.attrs['type'].decode() + if energy_type == 'maxwell': + return MaxwellEnergy.from_hdf5(group) + elif energy_type == 'evaporation': + return Evaporation.from_hdf5(group) + elif energy_type == 'watt': + return WattEnergy.from_hdf5(group) + elif energy_type == 'madland-nix': + return MadlandNix.from_hdf5(group) + elif energy_type == 'discrete_photon': + return DiscretePhoton.from_hdf5(group) + elif energy_type == 'level': + return LevelInelastic.from_hdf5(group) + elif energy_type == 'continuous': + return ContinuousTabular.from_hdf5(group) + + +class ArbitraryTabulated(EnergyDistribution): + r"""Arbitrary tabulated function given in ENDF MF=5, LF=1 represented as + + .. math:: + f(E \rightarrow E') = g(E \rightarrow E') + + Parameters + ---------- + energy : numpy.ndarray + Array of incident neutron energies + pdf : list of openmc.data.Tabulated1D + Tabulated outgoing energy distribution probability density functions + + Attributes + ---------- + energy : numpy.ndarray + Array of incident neutron energies + pdf : list of openmc.data.Tabulated1D + Tabulated outgoing energy distribution probability density functions + + """ + + def __init__(self, energy, pdf): + self.energy = energy + self.pdf = pdf + + def to_hdf5(self, group): + NotImplementedError + + +class GeneralEvaporation(EnergyDistribution): + r"""General evaporation spectrum given in ENDF MF=5, LF=5 represented as + + .. math:: + f(E \rightarrow E') = g(E'/\theta(E)) + + Parameters + ---------- + theta : openmc.data.Tabulated1D + Tabulated function of incident neutron energy :math:`E` + g : openmc.data.Tabulated1D + Tabulated function of :math:`x = E'/\theta(E)` + u : float + Constant introduced to define the proper upper limit for the final + particle energy such that :math:`0 \le E' \le E - U` + + Attributes + ---------- + theta : openmc.data.Tabulated1D + Tabulated function of incident neutron energy :math:`E` + g : openmc.data.Tabulated1D + Tabulated function of :math:`x = E'/\theta(E)` + u : float + Constant introduced to define the proper upper limit for the final + particle energy such that :math:`0 \le E' \le E - U` + + """ + + def __init__(self, theta, g, u): + self.theta = theta + self.g = g + self.u = u + + def to_hdf5(self, group): + raise NotImplementedError + + +class MaxwellEnergy(EnergyDistribution): + r"""Simple Maxwellian fission spectrum represented as + + .. math:: + f(E \rightarrow E') = \frac{\sqrt{E'}}{I} e^{-E'/\theta(E)} + + Parameters + ---------- + theta : openmc.data.Tabulated1D + Tabulated function of incident neutron energy + u : float + Constant introduced to define the proper upper limit for the final + particle energy such that :math:`0 \le E' \le E - U` + + Attributes + ---------- + theta : openmc.data.Tabulated1D + Tabulated function of incident neutron energy + u : float + Constant introduced to define the proper upper limit for the final + particle energy such that :math:`0 \le E' \le E - U` + + """ + + def __init__(self, theta, u): + self.theta = theta + self.u = u + + @property + def theta(self): + return self._theta + + @property + def u(self): + return self._u + + @theta.setter + def theta(self, theta): + cv.check_type('Maxwell theta', theta, Tabulated1D) + self._theta = theta + + @u.setter + def u(self, u): + cv.check_type('Maxwell restriction energy', u, Real) + self._u = u + + def to_hdf5(self, group): + """Write distribution to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + + """ + + group.attrs['type'] = np.string_('maxwell') + group.attrs['u'] = self.u + self.theta.to_hdf5(group, 'theta') + + @classmethod + def from_hdf5(cls, group): + """Generate Maxwell distribution from HDF5 data + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.MaxwellEnergy + Maxwell distribution + + """ + theta = Tabulated1D.from_hdf5(group['theta']) + u = group.attrs['u'] + return cls(theta, u) + + +class Evaporation(EnergyDistribution): + r"""Evaporation spectrum represented as + + .. math:: + f(E \rightarrow E') = \frac{E'}{I} e^{-E'/\theta(E)} + + Parameters + ---------- + theta : openmc.data.Tabulated1D + Tabulated function of incident neutron energy + u : float + Constant introduced to define the proper upper limit for the final + particle energy such that :math:`0 \le E' \le E - U` + + Attributes + ---------- + theta : openmc.data.Tabulated1D + Tabulated function of incident neutron energy + u : float + Constant introduced to define the proper upper limit for the final + particle energy such that :math:`0 \le E' \le E - U` + + """ + + def __init__(self, theta, u): + self.theta = theta + self.u = u + + @property + def theta(self): + return self._theta + + @property + def u(self): + return self._u + + @theta.setter + def theta(self, theta): + cv.check_type('Evaporation theta', theta, Tabulated1D) + self._theta = theta + + @u.setter + def u(self, u): + cv.check_type('Evaporation restriction energy', u, Real) + self._u = u + + def to_hdf5(self, group): + """Write distribution to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + + """ + + group.attrs['type'] = np.string_('evaporation') + group.attrs['u'] = self.u + self.theta.to_hdf5(group, 'theta') + + @classmethod + def from_hdf5(cls, group): + """Generate evaporation spectrum from HDF5 data + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.Evaporation + Evaporation spectrum distribution + + """ + theta = Tabulated1D.from_hdf5(group['theta']) + u = group.attrs['u'] + return cls(theta, u) + + +class WattEnergy(EnergyDistribution): + r"""Energy-dependent Watt spectrum represented as + + .. math:: + f(E \rightarrow E') = \frac{e^{-E'/a}}{I} \sinh \left ( \sqrt{bE'} + \right ) + + Parameters + ---------- + a, b : openmc.data.Tabulated1D + Energy-dependent parameters tabulated as function of incident neutron + energy + u : float + Constant introduced to define the proper upper limit for the final + particle energy such that :math:`0 \le E' \le E - U` + + Attributes + ---------- + a, b : openmc.data.Tabulated1D + Energy-dependent parameters tabulated as function of incident neutron + energy + u : float + Constant introduced to define the proper upper limit for the final + particle energy such that :math:`0 \le E' \le E - U` + + """ + + def __init__(self, a, b, u): + self.a = a + self.b = b + self.u = u + + @property + def a(self): + return self._a + + @property + def b(self): + return self._b + + @property + def u(self): + return self._u + + @a.setter + def a(self, a): + cv.check_type('Watt a', a, Tabulated1D) + self._a = a + + @b.setter + def b(self, b): + cv.check_type('Watt b', b, Tabulated1D) + self._b = b + + @u.setter + def u(self, u): + cv.check_type('Watt restriction energy', u, Real) + self._u = u + + def to_hdf5(self, group): + """Write distribution to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + + """ + + group.attrs['type'] = np.string_('watt') + group.attrs['u'] = self.u + self.a.to_hdf5(group, 'a') + self.b.to_hdf5(group, 'b') + + @classmethod + def from_hdf5(cls, group): + """Generate Watt fission spectrum from HDF5 data + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.WattEnergy + Watt fission spectrum + + """ + a = Tabulated1D.from_hdf5(group['a']) + b = Tabulated1D.from_hdf5(group['b']) + u = group.attrs['u'] + return cls(a, b, u) + +class MadlandNix(EnergyDistribution): + r"""Energy-dependent fission neutron spectrum (Madland and Nix) given in + ENDF MF=5, LF=12 represented as + + .. math:: + f(E \rightarrow E') = \frac{1}{2} [ g(E', E_F(L)) + g(E', E_F(H))] + + where + + .. math:: + g(E',E_F) = \frac{1}{3\sqrt{E_F T_M}} \left [ u_2^{3/2} E_1 (u_2) - + u_1^{3/2} E_1 (u_1) + \gamma \left ( \frac{3}{2}, u_2 \right ) - \gamma + \left ( \frac{3}{2}, u_1 \right ) \right ] \\ u_1 = \left ( \sqrt{E'} - + \sqrt{E_F} \right )^2 / T_M \\ u_2 = \left ( \sqrt{E'} + \sqrt{E_F} + \right )^2 / T_M. + + Parameters + ---------- + efl, efh : float + Constants which represent the average kinetic energy per nucleon of the + fission fragment (efl = light, efh = heavy) + tm : openmc.data.Tabulated1D + Parameter tabulated as a function of incident neutron energy + + Attributes + ---------- + efl, efh : float + Constants which represent the average kinetic energy per nucleon of the + fission fragment (efl = light, efh = heavy) + tm : openmc.data.Tabulated1D + Parameter tabulated as a function of incident neutron energy + + """ + + def __init__(self, efl, efh, tm): + self.efl = efl + self.efh = efh + self.tm = tm + + @property + def efl(self): + return self._efl + + @property + def efh(self): + return self._efh + + @property + def tm(self): + return self._tm + + @efl.setter + def efl(self, efl): + name = 'Madland-Nix light fragment energy' + cv.check_type(name, efl, Real) + cv.check_greater_than(name, efl, 0.) + self._efl = efl + + @efh.setter + def efh(self, efh): + name = 'Madland-Nix heavy fragment energy' + cv.check_type(name, efh, Real) + cv.check_greater_than(name, efh, 0.) + self._efh = efh + + @tm.setter + def tm(self, tm): + cv.check_type('Madland-Nix maximum temperature', tm, Tabulated1D) + self._tm = tm + + def to_hdf5(self, group): + """Write distribution to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + + """ + + group.attrs['type'] = np.string_('madland-nix') + group.attrs['efl'] = self.efl + group.attrs['efh'] = self.efh + self.tm.to_hdf5(group) + + @classmethod + def from_hdf5(cls, group): + """Generate Madland-Nix fission spectrum from HDF5 data + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.MadlandNix + Madland-Nix fission spectrum + + """ + efl = group.attrs['efl'] + efh = group.attrs['efh'] + tm = Tabulated1D.from_hdf5(group['tm']) + return cls(efl, efh, tm) + + +class DiscretePhoton(EnergyDistribution): + """Discrete photon energy distribution + + Parameters + ---------- + primary_flag : int + Indicator of whether the photon is a primary or non-primary photon. + energy : float + Photon energy (if lp==0 or lp==1) or binding energy (if lp==2). + atomic_weight_ratio : float + Atomic weight ratio of the target nuclide responsible for the emitted + particle + + Attributes + ---------- + primary_flag : int + Indicator of whether the photon is a primary or non-primary photon. + energy : float + Photon energy (if lp==0 or lp==1) or binding energy (if lp==2). + atomic_weight_ratio : float + Atomic weight ratio of the target nuclide responsible for the emitted + particle + + """ + + def __init__(self, primary_flag, energy, atomic_weight_ratio): + super(DiscretePhoton, self).__init__() + self.primary_flag = primary_flag + self.energy = energy + self.atomic_weight_ratio = atomic_weight_ratio + + @property + def primary_flag(self): + return self._primary_flag + + @property + def energy(self): + return self._energy + + @property + def atomic_weight_ratio(self): + return self._atomic_weight_ratio + + @primary_flag.setter + def primary_flag(self, primary_flag): + cv.check_type('discrete photon primary_flag', primary_flag, Integral) + self._primary_flag = primary_flag + + @energy.setter + def energy(self, energy): + cv.check_type('discrete photon energy', energy, Real) + self._energy = energy + + @atomic_weight_ratio.setter + def atomic_weight_ratio(self, atomic_weight_ratio): + cv.check_type('atomic weight ratio', atomic_weight_ratio, Real) + self._atomic_weight_ratio = atomic_weight_ratio + + def to_hdf5(self, group): + """Write distribution to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + + """ + + group.attrs['type'] = np.string_('discrete_photon') + group.attrs['primary_flag'] = self.primary_flag + group.attrs['energy'] = self.energy + group.attrs['atomic_weight_ratio'] = self.atomic_weight_ratio + + @classmethod + def from_hdf5(cls, group): + """Generate discrete photon energy distribution from HDF5 data + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.DiscretePhoton + Discrete photon energy distribution + + """ + primary_flag = group.attrs['primary_flag'] + energy = group.attrs['energy'] + awr = group.attrs['atomic_weight_ratio'] + return cls(primary_flag, energy, awr) + + +class LevelInelastic(EnergyDistribution): + r"""Level inelastic scattering + + Parameters + ---------- + threshold : float + Energy threshold in the laboratory system, :math:`(A + 1)/A * |Q|` + mass_ratio : float + :math:`(A/(A + 1))^2` + + Attributes + ---------- + threshold : float + Energy threshold in the laboratory system, :math:`(A + 1)/A * |Q|` + mass_ratio : float + :math:`(A/(A + 1))^2` + + """ + + def __init__(self, threshold, mass_ratio): + super(LevelInelastic, self).__init__() + self.threshold = threshold + self.mass_ratio = mass_ratio + + @property + def threshold(self): + return self._threshold + + @property + def mass_ratio(self): + return self._mass_ratio + + @threshold.setter + def threshold(self, threshold): + cv.check_type('level inelastic threhsold', threshold, Real) + self._threshold = threshold + + @mass_ratio.setter + def mass_ratio(self, mass_ratio): + cv.check_type('level inelastic mass ratio', mass_ratio, Real) + self._mass_ratio = mass_ratio + + def to_hdf5(self, group): + """Write distribution to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + + """ + + group.attrs['type'] = np.string_('level') + group.attrs['threshold'] = self.threshold + group.attrs['mass_ratio'] = self.mass_ratio + + @classmethod + def from_hdf5(cls, group): + """Generate level inelastic distribution from HDF5 data + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.LevelInelastic + Level inelastic scattering distribution + + """ + threshold = group.attrs['threshold'] + mass_ratio = group.attrs['mass_ratio'] + return cls(threshold, mass_ratio) + + +class ContinuousTabular(EnergyDistribution): + """Continuous tabular distribution + + Parameters + ---------- + breakpoints : Iterable of int + Breakpoints defining interpolation regions + interpolation : Iterable of int + Interpolation codes + energy : Iterable of float + Incoming energies at which distributions exist + energy_out : Iterable of openmc.stats.Univariate + Distribution of outgoing energies corresponding to each incoming energy + + Attributes + ---------- + breakpoints : Iterable of int + Breakpoints defining interpolation regions + interpolation : Iterable of int + Interpolation codes + energy : Iterable of float + Incoming energies at which distributions exist + energy_out : Iterable of openmc.stats.Univariate + Distribution of outgoing energies corresponding to each incoming energy + + """ + + def __init__(self, breakpoints, interpolation, energy, energy_out): + super(ContinuousTabular, self).__init__() + self.breakpoints = breakpoints + self.interpolation = interpolation + self.energy = energy + self.energy_out = energy_out + + @property + def breakpoints(self): + return self._breakpoints + + @property + def interpolation(self): + return self._interpolation + + @property + def energy(self): + return self._energy + + @property + def energy_out(self): + return self._energy_out + + @breakpoints.setter + def breakpoints(self, breakpoints): + cv.check_type('continuous tabular breakpoints', breakpoints, + Iterable, Integral) + self._breakpoints = breakpoints + + @interpolation.setter + def interpolation(self, interpolation): + cv.check_type('continuous tabular interpolation', interpolation, + Iterable, Integral) + self._interpolation = interpolation + + @energy.setter + def energy(self, energy): + cv.check_type('continuous tabular incoming energy', energy, + Iterable, Real) + self._energy = energy + + @energy_out.setter + def energy_out(self, energy_out): + cv.check_type('continuous tabular outgoing energy', energy_out, + Iterable, Univariate) + self._energy_out = energy_out + + def to_hdf5(self, group): + """Write distribution to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + + """ + + group.attrs['type'] = np.string_('continuous') + + dset = group.create_dataset('energy', data=self.energy) + dset.attrs['interpolation'] = np.vstack((self.breakpoints, + self.interpolation)) + + # Determine total number of (E,p) pairs and create array + n_pairs = sum(len(d) for d in self.energy_out) + pairs = np.empty((3, n_pairs)) + + # Create array for offsets + offsets = np.empty(len(self.energy_out), dtype=int) + interpolation = np.empty(len(self.energy_out), dtype=int) + n_discrete_lines = np.empty(len(self.energy_out), dtype=int) + j = 0 + + # Populate offsets and pairs array + for i, eout in enumerate(self.energy_out): + n = len(eout) + offsets[i] = j + + if isinstance(eout, Mixture): + discrete, continuous = eout.distribution + n_discrete_lines[i] = m = len(discrete) + interpolation[i] = 1 if continuous.interpolation == 'histogram' else 2 + pairs[0, j:j+m] = discrete.x + pairs[1, j:j+m] = discrete.p + pairs[2, j:j+m] = discrete.c + pairs[0, j+m:j+n] = continuous.x + pairs[1, j+m:j+n] = continuous.p + pairs[2, j+m:j+n] = continuous.c + else: + if isinstance(eout, Tabular): + n_discrete_lines[i] = 0 + interpolation[i] = 1 if eout.interpolation == 'histogram' else 2 + elif isinstance(eout, Discrete): + n_discrete_lines[i] = n + interpolation[i] = 1 + pairs[0, j:j+n] = eout.x + pairs[1, j:j+n] = eout.p + pairs[2, j:j+n] = eout.c + j += n + + # Create dataset for distributions + dset = group.create_dataset('distribution', data=pairs) + + # Write interpolation as attribute + dset.attrs['offsets'] = offsets + dset.attrs['interpolation'] = interpolation + dset.attrs['n_discrete_lines'] = n_discrete_lines + + @classmethod + def from_hdf5(cls, group): + """Generate continuous tabular distribution from HDF5 data + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.ContinuousTabular + Continuous tabular energy distribution + + """ + interp_data = group['energy'].attrs['interpolation'] + energy_breakpoints = interp_data[0,:] + energy_interpolation = interp_data[1,:] + energy = group['energy'].value + + data = group['distribution'] + offsets = data.attrs['offsets'] + interpolation = data.attrs['interpolation'] + n_discrete_lines = data.attrs['n_discrete_lines'] + + energy_out = [] + n_energy = len(energy) + for i in range(n_energy): + # Determine length of outgoing energy distribution and number of + # discrete lines + j = offsets[i] + if i < n_energy - 1: + n = offsets[i+1] - j + else: + n = data.shape[1] - j + m = n_discrete_lines[i] + + # Create discrete distribution if lines are present + if m > 0: + eout_discrete = Discrete(data[0, j:j+m], data[1, j:j+m]) + eout_discrete.c = data[2, j:j+m] + p_discrete = eout_discrete.c[-1] + + # Create continuous distribution + if m < n: + interp = interpolation_scheme[interpolation[i]] + eout_continuous = Tabular(data[0, j+m:j+n], data[1, j+m:j+n], interp) + eout_continuous.c = data[2, j+m:j+n] + + # If both continuous and discrete are present, create a mixture + # distribution + if m == 0: + eout_i = eout_continuous + elif m == n: + eout_i = eout_discrete + else: + eout_i = Mixture([p_discrete, 1. - p_discrete], + [eout_discrete, eout_continuous]) + energy_out.append(eout_i) + + return cls(energy_breakpoints, energy_interpolation, + energy, energy_out) diff --git a/openmc/data/kalbach_mann.py b/openmc/data/kalbach_mann.py new file mode 100644 index 000000000..a8fc8d4c6 --- /dev/null +++ b/openmc/data/kalbach_mann.py @@ -0,0 +1,253 @@ +from collections import Iterable +from numbers import Real, Integral + +import numpy as np + +import openmc.checkvalue as cv +from openmc.stats import Tabular, Univariate, Discrete, Mixture +from .container import Tabulated1D, interpolation_scheme +from .angle_energy import AngleEnergy + + +class KalbachMann(AngleEnergy): + """Kalbach-Mann distribution + + Parameters + ---------- + breakpoints : Iterable of int + Breakpoints defining interpolation regions + interpolation : Iterable of int + Interpolation codes + energy : Iterable of float + Incoming energies at which distributions exist + energy_out : Iterable of openmc.stats.Univariate + Distribution of outgoing energies corresponding to each incoming energy + precompound : Iterable of openmc.data.Tabulated1D + Precompound factor 'r' as a function of outgoing energy for each + incoming energy + slope : Iterable of openmc.data.Tabulated1D + Kalbach-Chadwick angular distribution slope value 'a' as a function of + outgoing energy for each incoming energy + + Attributes + ---------- + breakpoints : Iterable of int + Breakpoints defining interpolation regions + interpolation : Iterable of int + Interpolation codes + energy : Iterable of float + Incoming energies at which distributions exist + energy_out : Iterable of openmc.stats.Univariate + Distribution of outgoing energies corresponding to each incoming energy + precompound : Iterable of openmc.data.Tabulated1D + Precompound factor 'r' as a function of outgoing energy for each + incoming energy + slope : Iterable of openmc.data.Tabulated1D + Kalbach-Chadwick angular distribution slope value 'a' as a function of + outgoing energy for each incoming energy + + """ + + def __init__(self, breakpoints, interpolation, energy, energy_out, + precompound, slope): + super(KalbachMann, self).__init__() + self.breakpoints = breakpoints + self.interpolation = interpolation + self.energy = energy + self.energy_out = energy_out + self.precompound = precompound + self.slope = slope + + @property + def breakpoints(self): + return self._breakpoints + + @property + def interpolation(self): + return self._interpolation + + @property + def energy(self): + return self._energy + + @property + def energy_out(self): + return self._energy_out + + @property + def precompound(self): + return self._precompound + + @property + def slope(self): + return self._slope + + @breakpoints.setter + def breakpoints(self, breakpoints): + cv.check_type('Kalbach-Mann breakpoints', breakpoints, + Iterable, Integral) + self._breakpoints = breakpoints + + @interpolation.setter + def interpolation(self, interpolation): + cv.check_type('Kalbach-Mann interpolation', interpolation, + Iterable, Integral) + self._interpolation = interpolation + + @energy.setter + def energy(self, energy): + cv.check_type('Kalbach-Mann incoming energy', energy, + Iterable, Real) + self._energy = energy + + @energy_out.setter + def energy_out(self, energy_out): + cv.check_type('Kalbach-Mann distributions', energy_out, + Iterable, Univariate) + self._energy_out = energy_out + + @precompound.setter + def precompound(self, precompound): + cv.check_type('Kalbach-Mann precompound factor', precompound, + Iterable, Tabulated1D) + self._precompound = precompound + + @slope.setter + def slope(self, slope): + cv.check_type('Kalbach-Mann slope', slope, Iterable, Tabulated1D) + self._slope = slope + + def to_hdf5(self, group): + """Write distribution to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + + """ + group.attrs['type'] = np.string_('kalbach-mann') + + dset = group.create_dataset('energy', data=self.energy) + dset.attrs['interpolation'] = np.vstack((self.breakpoints, + self.interpolation)) + + # Determine total number of (E,p,r,a) tuples and create array + n_tuple = sum(len(d) for d in self.energy_out) + distribution = np.empty((5, n_tuple)) + + # Create array for offsets + offsets = np.empty(len(self.energy_out), dtype=int) + interpolation = np.empty(len(self.energy_out), dtype=int) + n_discrete_lines = np.empty(len(self.energy_out), dtype=int) + j = 0 + + # Populate offsets and distribution array + for i, (eout, km_r, km_a) in enumerate(zip( + self.energy_out, self.precompound, self.slope)): + n = len(eout) + offsets[i] = j + + if isinstance(eout, Mixture): + discrete, continuous = eout.distribution + n_discrete_lines[i] = m = len(discrete) + interpolation[i] = 1 if continuous.interpolation == 'histogram' else 2 + distribution[0, j:j+m] = discrete.x + distribution[1, j:j+m] = discrete.p + distribution[2, j:j+m] = discrete.c + distribution[0, j+m:j+n] = continuous.x + distribution[1, j+m:j+n] = continuous.p + distribution[2, j+m:j+n] = continuous.c + else: + if isinstance(eout, Tabular): + n_discrete_lines[i] = 0 + interpolation[i] = 1 if eout.interpolation == 'histogram' else 2 + elif isinstance(eout, Discrete): + n_discrete_lines[i] = n + interpolation[i] = 1 + distribution[0, j:j+n] = eout.x + distribution[1, j:j+n] = eout.p + distribution[2, j:j+n] = eout.c + + distribution[3, j:j+n] = km_r.y + distribution[4, j:j+n] = km_a.y + j += n + + # Create dataset for distributions + dset = group.create_dataset('distribution', data=distribution) + + # Write interpolation as attribute + dset.attrs['offsets'] = offsets + dset.attrs['interpolation'] = interpolation + dset.attrs['n_discrete_lines'] = n_discrete_lines + + @classmethod + def from_hdf5(cls, group): + """Generate Kalbach-Mann distribution from HDF5 data + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.KalbachMann + Kalbach-Mann energy distribution + + """ + interp_data = group['energy'].attrs['interpolation'] + energy_breakpoints = interp_data[0,:] + energy_interpolation = interp_data[1,:] + energy = group['energy'].value + + data = group['distribution'] + offsets = data.attrs['offsets'] + interpolation = data.attrs['interpolation'] + n_discrete_lines = data.attrs['n_discrete_lines'] + + energy_out = [] + precompound = [] + slope = [] + n_energy = len(energy) + for i in range(n_energy): + # Determine length of outgoing energy distribution and number of + # discrete lines + j = offsets[i] + if i < n_energy - 1: + n = offsets[i+1] - j + else: + n = data.shape[1] - j + m = n_discrete_lines[i] + + # Create discrete distribution if lines are present + if m > 0: + eout_discrete = Discrete(data[0, j:j+m], data[1, j:j+m]) + eout_discrete.c = data[2, j:j+m] + p_discrete = eout_discrete.c[-1] + + # Create continuous distribution + if m < n: + interp = interpolation_scheme[interpolation[i]] + eout_continuous = Tabular(data[0, j+m:j+n], data[1, j+m:j+n], interp) + eout_continuous.c = data[2, j+m:j+n] + + # If both continuous and discrete are present, create a mixture + # distribution + if m == 0: + eout_i = eout_continuous + elif m == n: + eout_i = eout_discrete + else: + eout_i = Mixture([p_discrete, 1. - p_discrete], + [eout_discrete, eout_continuous]) + + km_r = Tabulated1D(data[0, j:j+n], data[3, j:j+n]) + km_a = Tabulated1D(data[0, j:j+n], data[4, j:j+n]) + + energy_out.append(eout_i) + precompound.append(km_r) + slope.append(km_a) + + return cls(energy_breakpoints, energy_interpolation, + energy, energy_out, precompound, slope) diff --git a/openmc/data/nbody.py b/openmc/data/nbody.py new file mode 100644 index 000000000..4fa3d05b2 --- /dev/null +++ b/openmc/data/nbody.py @@ -0,0 +1,118 @@ +from numbers import Real, Integral + +import numpy as np + +import openmc.checkvalue as cv +from .angle_energy import AngleEnergy + +class NBodyPhaseSpace(AngleEnergy): + """N-body phase space distribution + + Parameters + ---------- + total_mass : float + Total mass of product particles + n_particles : int + Number of product particles + atomic_weight_ratio : float + Atomic weight ratio of target nuclide + q_value : float + Q value for reaction in MeV + + Attributes + ---------- + total_mass : float + Total mass of product particles + n_particles : int + Number of product particles + atomic_weight_ratio : float + Atomic weight ratio of target nuclide + q_value : float + Q value for reaction in MeV + + """ + + def __init__(self, total_mass, n_particles, atomic_weight_ratio, q_value): + self.total_mass = total_mass + self.n_particles = n_particles + self.atomic_weight_ratio = atomic_weight_ratio + self.q_value = q_value + + @property + def total_mass(self): + return self._total_mass + + @property + def n_particles(self): + return self._n_particles + + @property + def atomic_weight_ratio(self): + return self._atomic_weight_ratio + + @property + def q_value(self): + return self._q_value + + @total_mass.setter + 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 + + @n_particles.setter + 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 + + @atomic_weight_ratio.setter + 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 + + @q_value.setter + def q_value(self, q_value): + name = 'N-body phase space Q value' + cv.check_type(name, q_value, Real) + self._q_value = q_value + + def to_hdf5(self, group): + """Write distribution to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + + """ + group.attrs['type'] = np.string_('nbody') + group.attrs['total_mass'] = self.total_mass + group.attrs['n_particles'] = self.n_particles + group.attrs['atomic_weight_ratio'] = self.atomic_weight_ratio + group.attrs['q_value'] = self.q_value + + @classmethod + def from_hdf5(cls, group): + """Generate N-body phase space distribution from HDF5 data + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.NBodyPhaseSpace + N-body phase space distribution + + """ + total_mass = group.attrs['total_mass'] + n_particles = group.attrs['n_particles'] + awr = group.attrs['atomic_weight_ratio'] + q_value = group.attrs['q_value'] + return cls(total_mass, n_particles, awr, q_value) diff --git a/openmc/data/product.py b/openmc/data/product.py new file mode 100644 index 000000000..6a331dc24 --- /dev/null +++ b/openmc/data/product.py @@ -0,0 +1,205 @@ +from collections import Iterable +from numbers import Real +import sys + +import numpy as np +from numpy.polynomial.polynomial import Polynomial + +import openmc.checkvalue as cv +from .container import Tabulated1D +from .angle_energy import AngleEnergy + +if sys.version_info[0] >= 3: + basestring = str + + +class Product(object): + """Secondary particle emitted in a nuclear reaction + + Parameters + ---------- + particle : str, optional + What particle the reaction product is. Defaults to 'neutron'. + + Attributes + ---------- + applicability : Iterable of openmc.data.Tabulated1D + Probability of sampling a given distribution for this product. + decay_rate : float + Decay rate in inverse seconds + distribution : Iterable of openmc.data.AngleEnergy + Distributions of energy and angle of product. + emission_mode : {'prompt', 'delayed', 'total'} + Indicate whether the particle is emitted immediately or whether it + results from the decay of reaction product (e.g., neutron emitted from a + delayed neutron precursor). A special value of 'total' is used when the + yield represents particles from prompt and delayed sources. + particle : str + What particle the reaction product is. + yield_ : float or openmc.data.Tabulated1D or numpy.polynomial.Polynomial + Yield of secondary particle in the reaction. + + """ + + def __init__(self, particle='neutron'): + self.particle = particle + self.decay_rate = 0.0 + self.emission_mode = 'prompt' + self.distribution = [] + self.applicability = [] + self.yield_ = 1 + + def __repr__(self): + if isinstance(self.yield_, Real): + return "".format( + self.particle, self.emission_mode, self.yield_) + elif isinstance(self.yield_, Tabulated1D): + if np.all(self.yield_.y == self.yield_.y[0]): + return "".format( + self.particle, self.emission_mode, self.yield_.y[0]) + else: + return "".format( + self.particle, self.emission_mode) + else: + return "".format( + self.particle, self.emission_mode) + + @property + def applicability(self): + return self._applicability + + @property + def decay_rate(self): + return self._decay_rate + + @property + def distribution(self): + return self._distribution + + @property + def emission_mode(self): + return self._emission_mode + + @property + def particle(self): + return self._particle + + @property + def yield_(self): + return self._yield + + @applicability.setter + def applicability(self, applicability): + cv.check_type('product distribution applicability', applicability, + Iterable, Tabulated1D) + self._applicability = applicability + + @decay_rate.setter + def decay_rate(self, decay_rate): + cv.check_type('product decay rate', decay_rate, Real) + cv.check_greater_than('product decay rate', decay_rate, 0.0, True) + self._decay_rate = decay_rate + + @distribution.setter + def distribution(self, distribution): + cv.check_type('product angle-energy distribution', distribution, + Iterable, AngleEnergy) + self._distribution = distribution + + @emission_mode.setter + def emission_mode(self, emission_mode): + cv.check_value('product emission mode', emission_mode, + ('prompt', 'delayed', 'total')) + self._emission_mode = emission_mode + + @particle.setter + def particle(self, particle): + cv.check_type('product particle type', particle, basestring) + self._particle = particle + + @yield_.setter + def yield_(self, yield_): + cv.check_type('product yield', yield_, + (Real, Tabulated1D, Polynomial)) + self._yield = yield_ + + def to_hdf5(self, group): + """Write product to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + + """ + group.attrs['particle'] = np.string_(self.particle) + group.attrs['emission_mode'] = np.string_(self.emission_mode) + if self.decay_rate > 0.0: + group.attrs['decay_rate'] = self.decay_rate + + # Write yield + if isinstance(self.yield_, Tabulated1D): + self.yield_.to_hdf5(group, 'yield') + dset = group['yield'] + dset.attrs['type'] = np.string_('tabulated') + elif isinstance(self.yield_, Polynomial): + dset = group.create_dataset('yield', data=self.yield_.coef) + dset.attrs['type'] = np.string_('polynomial') + else: + dset = group.create_dataset('yield', data=float(self.yield_)) + dset.attrs['type'] = np.string_('constant') + + # Write applicability/distribution + group.attrs['n_distribution'] = len(self.distribution) + for i, d in enumerate(self.distribution): + dgroup = group.create_group('distribution_{}'.format(i)) + if self.applicability: + self.applicability[i].to_hdf5(dgroup, 'applicability') + d.to_hdf5(dgroup) + + @classmethod + def from_hdf5(cls, group): + """Generate reaction product from HDF5 data + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.Product + Reaction product + + """ + particle = group.attrs['particle'].decode() + p = cls(particle) + + p.emission_mode = group.attrs['emission_mode'].decode() + if 'decay_rate' in group.attrs: + p.decay_rate = group.attrs['decay_rate'] + + # Read yield + yield_type = group['yield'].attrs['type'].decode() + if yield_type == 'constant': + p.yield_ = group['yield'].value + elif yield_type == 'polynomial': + p.yield_ = Polynomial(group['yield'].value) + elif yield_type == 'tabulated': + p.yield_ = Tabulated1D.from_hdf5(group['yield']) + + # Read applicability/distribution + n_distribution = group.attrs['n_distribution'] + distribution = [] + applicability = [] + for i in range(n_distribution): + dgroup = group['distribution_{}'.format(i)] + if 'applicability' in dgroup: + applicability.append(Tabulated1D.from_hdf5( + dgroup['applicability'])) + distribution.append(AngleEnergy.from_hdf5(dgroup)) + + p.distribution = distribution + p.applicability = applicability + + return p diff --git a/openmc/data/thermal.py b/openmc/data/thermal.py new file mode 100644 index 000000000..663b6472c --- /dev/null +++ b/openmc/data/thermal.py @@ -0,0 +1,92 @@ +from collections import Iterable +from numbers import Real + +import numpy as np + +import openmc.checkvalue as cv + + +class CoherentElastic(object): + """Coherent elastic scattering data from a crystalline material + + Parameters + ---------- + bragg_edges : Iterable of float + Bragg edge energies in MeV + factors : Iterable of float + Partial sum of structure factors, :math:`\sum\limits_{i=1}^{E_i= 3: basestring = str +_INTERPOLATION_SCHEMES = ['histogram', 'linear-linear', 'linear-log', + 'log-linear', 'log-log'] + class Univariate(object): """Probability distribution of a single random variable. @@ -27,6 +32,10 @@ class Univariate(object): def to_xml(self, element_name): return '' + @abstractmethod + def __len__(self): + return 0 + class Discrete(Univariate): """Distribution characterized by a probability mass function. @@ -56,6 +65,9 @@ class Discrete(Univariate): self.x = x self.p = p + def __len__(self): + return len(self.x) + @property def x(self): return self._x @@ -114,6 +126,9 @@ class Uniform(Univariate): self.a = a self.b = b + def __len__(self): + return 2 + @property def a(self): return self._a @@ -132,6 +147,12 @@ class Uniform(Univariate): cv.check_type('Uniform b', b, Real) self._b = b + def to_tabular(self): + prob = 1./(self.b - self.a) + t = Tabular([self.a, self.b], [prob, prob], 'histogram') + t.c = [0., 1.] + return t + def to_xml(self, element_name): element = ET.Element(element_name) element.set("type", "uniform") @@ -162,6 +183,9 @@ class Maxwell(Univariate): super(Maxwell, self).__init__() self.theta = theta + def __len__(self): + return 1 + @property def theta(self): return self._theta @@ -207,6 +231,9 @@ class Watt(Univariate): self.a = a self.b = b + def __len__(self): + return 2 + @property def a(self): return self._a @@ -238,8 +265,8 @@ class Tabular(Univariate): """Piecewise continuous probability distribution. This class is used to represent a probability distribution whose density - function is tabulated at specific values and is either histogram or linearly - interpolated between points. + function is tabulated at specific values with a specified interpolation + scheme. Parameters ---------- @@ -247,9 +274,11 @@ class Tabular(Univariate): Tabulated values of the random variable p : Iterable of float Tabulated probabilities - interpolation : {'histogram', 'linear-linear'}, optional + interpolation : {'histogram', 'linear-linear', 'linear-log', 'log-linear', 'log-log'}, optional Indicate whether the density function is constant between tabulated - points or linearly-interpolated. + points or linearly-interpolated. Defaults to 'linear-linear'. + ignore_negative : bool + Ignore negative probabilities Attributes ---------- @@ -257,18 +286,23 @@ class Tabular(Univariate): Tabulated values of the random variable p : Iterable of float Tabulated probabilities - interpolation : {'histogram', 'linear-linear'}, optional + interpolation : {'histogram', 'linear-linear', 'linear-log', 'log-linear', 'log-log'}, optional Indicate whether the density function is constant between tabulated points or linearly-interpolated. """ - def __init__(self, x, p, interpolation='linear-linear'): + def __init__(self, x, p, interpolation='linear-linear', + ignore_negative=False): super(Tabular, self).__init__() + self._ignore_negative = ignore_negative self.x = x self.p = p self.interpolation = interpolation + def __len__(self): + return len(self.x) + @property def x(self): return self._x @@ -289,14 +323,14 @@ class Tabular(Univariate): @p.setter def p(self, p): cv.check_type('tabulated probabilities', p, Iterable, Real) - for pk in p: - cv.check_greater_than('tabulated probability', pk, 0.0, True) + if not self._ignore_negative: + for pk in p: + cv.check_greater_than('tabulated probability', pk, 0.0, True) self._p = p @interpolation.setter def interpolation(self, interpolation): - cv.check_value('interpolation', interpolation, - ['linear-linear', 'histogram']) + cv.check_value('interpolation', interpolation, _INTERPOLATION_SCHEMES) self._interpolation = interpolation def to_xml(self, element_name): @@ -308,3 +342,103 @@ class Tabular(Univariate): params.text = ' '.join(map(str, self.x)) + ' ' + ' '.join(map(str, self.p)) return element + + +class Legendre(Univariate): + r"""Probability density given by a Legendre polynomial expansion + :math:`\sum\limits_{\ell=0}^N \frac{2\ell + 1}{2} a_\ell P_\ell(\mu)`. + + Parameters + ---------- + coefficients : Iterable of Real + Expansion coefficients :math:`a_\ell`. Note that the :math:`(2\ell + + 1)/2` factor should not be included. + + Attributes + ---------- + coefficients : Iterable of Real + Expansion coefficients :math:`a_\ell`. Note that the :math:`(2\ell + + 1)/2` factor should not be included. + + """ + + def __init__(self, coefficients): + self.coefficients = coefficients + + def __call__(self, x): + return self._legendre_polynomial(x) + + def __len__(self): + return len(self._legendre_polynomial.coef) + + @property + def coefficients(self): + poly = self._legendre_polynomial + l = np.arange(poly.degree() + 1) + return 2./(2.*l + 1.) * poly.coef + + @coefficients.setter + def coefficients(self, coefficients): + cv.check_type('Legendre expansion coefficients', coefficients, + Iterable, Real) + for l in range(len(coefficients)): + coefficients[l] *= (2.*l + 1.)/2. + self._legendre_polynomial = np.polynomial.legendre.Legendre( + coefficients) + + def to_xml(self, element_name): + raise NotImplementedError + + +class Mixture(Univariate): + """Probability distribution characterized by a mixture of random variables. + + Parameters + ---------- + probability : Iterable of Real + Probability of selecting a particular distribution + distribution : Iterable of Univariate + List of distributions with corresponding probabilities + + Attributes + ---------- + probability : Iterable of Real + Probability of selecting a particular distribution + distribution : Iterable of Univariate + List of distributions with corresponding probabilities + + """ + + def __init__(self, probability, distribution): + super(Mixture, self).__init__() + self.probability = probability + self.distribution = distribution + + def __len__(self): + return sum(len(d) for d in self.distribution) + + @property + def probability(self): + return self._probability + + @property + def distribution(self): + return self._distribution + + @probability.setter + 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 + + @distribution.setter + def distribution(self, distribution): + cv.check_type('mixture distribution components', distribution, + Iterable, Univariate) + self._distribution = distribution + + def to_xml(self, element_name): + raise NotImplementedError diff --git a/src/ace.F90 b/src/ace.F90 deleted file mode 100644 index 170c885f0..000000000 --- a/src/ace.F90 +++ /dev/null @@ -1,1738 +0,0 @@ -module ace - - use angleenergy_header, only: AngleEnergy - use constants - use distribution_univariate, only: Uniform, Equiprobable, Tabular - use endf, only: is_fission, is_disappearance - use endf_header, only: Constant1D, Tabulated1D, Polynomial - use energy_distribution, only: TabularEquiprobable, LevelInelastic, & - ContinuousTabular, MaxwellEnergy, Evaporation, WattEnergy - use error, only: fatal_error, warning - use global - use list_header, only: ListInt - use material_header, only: Material - use multipole, only: multipole_read - use nuclide_header - use output, only: write_message - use product_header, only: ReactionProduct - use sab_header - use set_header, only: SetChar - use secondary_correlated, only: CorrelatedAngleEnergy - use secondary_kalbach, only: KalbachMann - use secondary_nbody, only: NBodyPhaseSpace - use secondary_uncorrelated, only: UncorrelatedAngleEnergy - use string, only: to_str, to_lower - - implicit none - - integer :: JXS(32) ! Pointers into ACE XSS tables - integer :: NXS(16) ! Descriptors for ACE XSS tables - real(8), allocatable :: XSS(:) ! Cross section data - integer :: XSS_index ! Current index in XSS data - - private :: JXS - private :: NXS - private :: XSS - -contains - -!=============================================================================== -! READ_ACE_XS reads all the cross sections for the problem and stores them in -! nuclides and sab_tables arrays -!=============================================================================== - - subroutine read_ace_xs() - - integer :: i ! index in materials array - integer :: j ! index over nuclides in material - integer :: k ! index over S(a,b) tables in material - integer :: n ! index over resonant scatterers - integer :: i_listing ! index in xs_listings array - integer :: i_nuclide ! index in nuclides - integer :: i_sab ! index in sab_tables - integer :: m ! position for sorting - integer :: temp_nuclide ! temporary value for sorting - integer :: temp_table ! temporary value for sorting - character(12) :: name ! name of isotope, e.g. 92235.03c - character(12) :: alias ! alias of nuclide, e.g. U-235.03c - logical :: mp_found ! if windowed multipole libraries were found - type(Material), pointer :: mat - type(Nuclide), pointer :: nuc - type(SAlphaBeta), pointer :: sab - type(SetChar) :: already_read - - ! allocate arrays for ACE table storage and cross section cache - allocate(nuclides(n_nuclides_total)) - allocate(sab_tables(n_sab_tables)) -!$omp parallel - allocate(micro_xs(n_nuclides_total)) -!$omp end parallel - - ! ========================================================================== - ! READ ALL ACE CROSS SECTION TABLES - - ! Loop over all files - MATERIAL_LOOP: do i = 1, n_materials - mat => materials(i) - - NUCLIDE_LOOP: do j = 1, mat % n_nuclides - name = mat % names(j) - - if (.not. already_read % contains(name)) then - i_listing = xs_listing_dict % get_key(to_lower(name)) - i_nuclide = nuclide_dict % get_key(to_lower(name)) - name = xs_listings(i_listing) % name - alias = xs_listings(i_listing) % alias - - ! Keep track of what listing is associated with this nuclide - nuc => nuclides(i_nuclide) - nuc % listing = i_listing - - ! Read the ACE table into the appropriate entry on the nuclides - ! array - call read_ace_table(i_nuclide, i_listing) - - ! 0K resonant scatterer information, if treating resonance scattering - if (treat_res_scat) then - do n = 1, n_res_scatterers_total - if (name == nuclides_0K(n) % name) then - nuclides(i_nuclide) % resonant = .true. - nuclides(i_nuclide) % name_0K = nuclides_0K(n) % name_0K - nuclides(i_nuclide) % name_0K = trim(nuclides(i_nuclide) % & - & name_0K) - nuclides(i_nuclide) % scheme = nuclides_0K(n) % scheme - nuclides(i_nuclide) % scheme = trim(nuclides(i_nuclide) % & - & scheme) - nuclides(i_nuclide) % E_min = nuclides_0K(n) % E_min - nuclides(i_nuclide) % E_max = nuclides_0K(n) % E_max - if (.not. already_read % contains(nuclides(i_nuclide) % & - & name_0K)) then - i_listing = xs_listing_dict % get_key(nuclides(i_nuclide) % & - & name_0K) - call read_ace_table(i_nuclide, i_listing) - end if - exit - end if - end do - end if - - ! Read multipole file into the appropriate entry on the nuclides array - if (multipole_active) call read_multipole_data(i_nuclide) - - ! Add name and alias to dictionary - call already_read % add(name) - call already_read % add(alias) - end if - end do NUCLIDE_LOOP - - SAB_LOOP: do k = 1, mat % n_sab - ! Get name of S(a,b) table - name = mat % sab_names(k) - - if (.not. already_read % contains(name)) then - i_listing = xs_listing_dict % get_key(to_lower(name)) - i_sab = sab_dict % get_key(to_lower(name)) - - ! Read the ACE table into the appropriate entry on the sab_tables - ! array - call read_ace_table(i_sab, i_listing) - - ! Add name to dictionary - call already_read % add(name) - end if - end do SAB_LOOP - end do MATERIAL_LOOP - - ! ========================================================================== - ! ASSIGN S(A,B) TABLES TO SPECIFIC NUCLIDES WITHIN MATERIALS - - MATERIAL_LOOP2: do i = 1, n_materials - ! Get pointer to material - mat => materials(i) - - ASSIGN_SAB: do k = 1, mat % n_sab - ! In order to know which nuclide the S(a,b) table applies to, we need to - ! search through the list of nuclides for one which has a matching zaid - sab => sab_tables(mat % i_sab_tables(k)) - - ! Loop through nuclides and find match - FIND_NUCLIDE: do j = 1, mat % n_nuclides - if (any(sab % zaid == nuclides(mat % nuclide(j)) % zaid)) then - mat % i_sab_nuclides(k) = j - exit FIND_NUCLIDE - end if - end do FIND_NUCLIDE - - ! Check to make sure S(a,b) table matched a nuclide - if (mat % i_sab_nuclides(k) == NONE) then - call fatal_error("S(a,b) table " // trim(mat % sab_names(k)) & - &// " did not match any nuclide on material " & - &// trim(to_str(mat % id))) - end if - end do ASSIGN_SAB - - ! If there are multiple S(a,b) tables, we need to make sure that the - ! entries in i_sab_nuclides are sorted or else they won't be applied - ! correctly in the cross_section module. The algorithm here is a simple - ! insertion sort -- don't need anything fancy! - - if (mat % n_sab > 1) then - SORT_SAB: do k = 2, mat % n_sab - ! Save value to move - m = k - temp_nuclide = mat % i_sab_nuclides(k) - temp_table = mat % i_sab_tables(k) - - MOVE_OVER: do - ! Check if insertion value is greater than (m-1)th value - if (temp_nuclide >= mat % i_sab_nuclides(m-1)) exit - - ! Move values over until hitting one that's not larger - mat % i_sab_nuclides(m) = mat % i_sab_nuclides(m-1) - mat % i_sab_tables(m) = mat % i_sab_tables(m-1) - m = m - 1 - - ! Exit if we've reached the beginning of the list - if (m == 1) exit - end do MOVE_OVER - - ! Put the original value into its new position - mat % i_sab_nuclides(m) = temp_nuclide - mat % i_sab_tables(m) = temp_table - end do SORT_SAB - end if - - ! Deallocate temporary arrays for names of nuclides and S(a,b) tables - if (allocated(mat % names)) deallocate(mat % names) - - end do MATERIAL_LOOP2 - - ! Avoid some valgrind leak errors - call already_read % clear() - - ! Loop around material - MATERIAL_LOOP3: do i = 1, n_materials - - ! Get material - mat => materials(i) - - ! Loop around nuclides in material - NUCLIDE_LOOP2: do j = 1, mat % n_nuclides - - ! Check for fission in nuclide - if (nuclides(mat % nuclide(j)) % fissionable) then - mat % fissionable = .true. - exit NUCLIDE_LOOP2 - end if - - end do NUCLIDE_LOOP2 - - end do MATERIAL_LOOP3 - - ! Show which nuclide results in lowest energy for neutron transport - do i = 1, n_nuclides_total - if (nuclides(i) % energy(nuclides(i) % n_grid) == energy_max_neutron) then - call write_message("Maximum neutron transport energy: " // & - trim(to_str(energy_max_neutron)) // " MeV for " // & - trim(adjustl(nuclides(i) % name)), 6) - exit - end if - end do - - ! If the user wants multipole, make sure we found a multipole library. - if (multipole_active) then - mp_found = .false. - do i = 1, n_nuclides_total - if (nuclides(i) % mp_present) then - mp_found = .true. - exit - end if - end do - if (.not. mp_found) call warning("Windowed multipole functionality is & - &turned on, but no multipole libraries were found. Set the & - & element in settings.xml or the & - &OPENMC_MULTIPOLE_LIBRARY environment variable.") - end if - - end subroutine read_ace_xs - -!=============================================================================== -! READ_ACE_TABLE reads a single cross section table in either ASCII or binary -! format. This routine reads the header data for each table and then calls -! appropriate subroutines to parse the actual data. -!=============================================================================== - - subroutine read_ace_table(i_table, i_listing) - integer, intent(in) :: i_table ! index in nuclides/sab_tables - integer, intent(in) :: i_listing ! index in xs_listings - - integer :: i ! loop index for XSS records - integer :: j, j1, j2 ! indices in XSS - integer :: record_length ! Fortran record length - integer :: location ! location of ACE table - integer :: entries ! number of entries on each record - integer :: length ! length of ACE table - integer :: unit_ace ! file unit - integer :: zaids(16) ! list of ZAIDs (only used for S(a,b)) - integer :: filetype ! filetype (ASCII or BINARY) - real(8) :: kT ! temperature of table - real(8) :: awrs(16) ! list of atomic weight ratios (not used) - real(8) :: awr ! atomic weight ratio for table - logical :: file_exists ! does ACE library exist? - logical :: data_0K ! are we reading 0K data? - character(7) :: readable ! is ACE library readable? - character(10) :: name ! name of ACE table - character(10) :: date_ ! date ACE library was processed - character(10) :: mat ! material identifier - character(70) :: comment ! comment for ACE table - character(MAX_FILE_LEN) :: filename ! path to ACE cross section library - type(Nuclide), pointer :: nuc - type(SAlphaBeta), pointer :: sab - type(XsListing), pointer :: listing - - ! determine path, record length, and location of table - listing => xs_listings(i_listing) - filename = listing % path - record_length = listing % recl - location = listing % location - entries = listing % entries - filetype = listing % filetype - - ! Check if ACE library exists and is readable - inquire(FILE=filename, EXIST=file_exists, READ=readable) - if (.not. file_exists) then - call fatal_error("ACE library '" // trim(filename) // "' does not exist!") - elseif (readable(1:3) == 'NO') then - call fatal_error("ACE library '" // trim(filename) // "' is not readable!& - & Change file permissions with chmod command.") - end if - - ! display message - call write_message("Loading ACE cross section table: " // listing % name, 6) - - if (filetype == ASCII) then - ! ======================================================================= - ! READ ACE TABLE IN ASCII FORMAT - - ! Find location of table - open(NEWUNIT=unit_ace, FILE=filename, STATUS='old', ACTION='read') - rewind(UNIT=unit_ace) - do i = 1, location - 1 - read(UNIT=unit_ace, FMT=*) - end do - - ! Read first line of header - read(UNIT=unit_ace, FMT='(A10,2G12.0,1X,A10)') name, awr, kT, date_ - - ! Check that correct xs was found -- if cross_sections.xml is broken, the - ! location of the table may be wrong - if(adjustl(name) /= adjustl(listing % name)) then - call fatal_error("XS listing entry " // trim(listing % name) // " did & - ¬ match ACE data, " // trim(name) // " found instead.") - end if - - ! Read more header and NXS and JXS - read(UNIT=unit_ace, FMT=100) comment, mat, & - (zaids(i), awrs(i), i=1,16), NXS, JXS -100 format(A70,A10/4(I7,F11.0)/4(I7,F11.0)/4(I7,F11.0)/4(I7,F11.0)/& - ,8I9/8I9/8I9/8I9/8I9/8I9) - - ! determine table length - length = NXS(1) - allocate(XSS(length)) - - ! Read XSS array - read(UNIT=unit_ace, FMT='(4G20.0)') XSS - - ! Close ACE file - close(UNIT=unit_ace) - - elseif (filetype == BINARY) then - ! ======================================================================= - ! READ ACE TABLE IN BINARY FORMAT - - ! Open ACE file - open(NEWUNIT=unit_ace, FILE=filename, STATUS='old', ACTION='read', & - ACCESS='direct', RECL=record_length) - - ! Read all header information - read(UNIT=unit_ace, REC=location) name, awr, kT, date_, & - comment, mat, (zaids(i), awrs(i), i=1,16), NXS, JXS - - ! determine table length - length = NXS(1) - allocate(XSS(length)) - - ! Read remaining records with XSS - do i = 1, (length + entries - 1)/entries - j1 = 1 + (i-1)*entries - j2 = min(length, j1 + entries - 1) - read(UNIT=UNIT_ACE, REC=location + i) (XSS(j), j=j1,j2) - end do - - ! Close ACE file - close(UNIT=unit_ace) - end if - - ! ========================================================================== - ! PARSE DATA BASED ON NXS, JXS, AND XSS ARRAYS - - select case(listing % type) - case (ACE_NEUTRON) - - ! only read in a resonant scatterers info once - nuc => nuclides(i_table) - data_0K = .false. - if (trim(adjustl(name)) == nuc % name_0K) then - data_0K = .true. - else - nuc % name = name - nuc % awr = awr - nuc % kT = kT - nuc % zaid = listing % zaid - end if - - ! read all blocks - call read_esz(nuc, data_0K) - - ! don't read unnecessary 0K data for resonant scatterers - if (data_0K) then - continue - else - call read_reactions(nuc) - call read_nu_data(nuc) - call read_energy_dist(nuc) - call read_angular_dist(nuc) - call read_unr_res(nuc) - end if - - ! for fissionable nuclides, precalculate microscopic nu-fission cross - ! sections so that we don't need to call the nu_total function during - ! cross section lookups (except if we're dealing w/ 0K data for resonant - ! scatterers) - - if (nuc % fissionable .and. .not. data_0K) then - call generate_nu_fission(nuc) - end if - - case (ACE_THERMAL) - sab => sab_tables(i_table) - sab % name = name - sab % awr = awr - sab % kT = kT - ! Find sab % n_zaid - do i = 1, 16 - if (zaids(i) == 0) then - sab % n_zaid = i - 1 - exit - end if - end do - allocate(sab % zaid(sab % n_zaid)) - sab % zaid = zaids(1: sab % n_zaid) - - call read_thermal_data(sab) - end select - - deallocate(XSS) - - end subroutine read_ace_table - -!=============================================================================== -! READ_MULTIPOLE_DATA checks for the existence of a multipole library in the -! directory and loads it using multipole_read -!=============================================================================== - - subroutine read_multipole_data(i_table) - - integer, intent(in) :: i_table ! index in nuclides/sab_tables - - logical :: file_exists ! Does multipole library exist? - character(7) :: readable ! Is multipole library readable? - character(6) :: zaid_string ! String of the ZAID - character(MAX_FILE_LEN+9) :: filename ! Path to multipole xs library - - ! For the time being, and I know this is a bit hacky, we just assume - ! that the file will be zaid.h5. - associate (nuc => nuclides(i_table)) - - write(zaid_string, '(I6.6)') nuc % zaid - filename = trim(path_multipole) // zaid_string // ".h5" - - ! Check if Multipole library exists and is readable - inquire(FILE=filename, EXIST=file_exists, READ=readable) - if (.not. file_exists) then - nuc % mp_present = .false. - return - elseif (readable(1:3) == 'NO') then - call fatal_error("Multipole library '" // trim(filename) // "' is not & - &readable! Change file permissions with chmod command.") - end if - - ! Display message - call write_message("Loading Multipole XS table: " // filename, 6) - - allocate(nuc % multipole) - - ! Call the read routine - call multipole_read(filename, nuc % multipole, i_table) - nuc % mp_present = .true. - - ! Recreate nu-fission tables - if (nuc % fissionable) then - call generate_nu_fission(nuc) - end if - - end associate - - end subroutine read_multipole_data - -!=============================================================================== -! READ_ESZ - reads through the ESZ block. This block contains the energy grid, -! total xs, absorption xs, elastic scattering xs, and heating numbers. -!=============================================================================== - - subroutine read_esz(nuc, data_0K) - type(Nuclide), intent(inout) :: nuc - logical, intent(in) :: data_0K ! are we reading 0K data? - - integer :: NE ! number of energy points for total and elastic cross sections - integer :: i ! index in 0K elastic xs array for this nuclide - - real(8) :: xs_cdf_sum = ZERO ! xs cdf value - - ! determine number of energy points - NE = NXS(3) - - ! allocate storage for energy grid and cross section arrays - - ! read in 0K data if we've already read in non-0K data - if (data_0K) then - nuc % n_grid_0K = NE - allocate(nuc % energy_0K(NE)) - allocate(nuc % elastic_0K(NE)) - allocate(nuc % xs_cdf(NE)) - nuc % elastic_0K = ZERO - nuc % xs_cdf = ZERO - XSS_index = 1 - nuc % energy_0K = get_real(NE) - - ! Skip total and absorption - XSS_index = XSS_index + 2*NE - - ! Continue reading elastic scattering and heating - nuc % elastic_0K = get_real(NE) - - do i = 1, nuc % n_grid_0K - 1 - - ! Negative cross sections result in a CDF that is not monotonically - ! increasing. Set all negative xs values to ZERO. - if (nuc % elastic_0K(i) < ZERO) nuc % elastic_0K(i) = ZERO - - ! build xs cdf - xs_cdf_sum = xs_cdf_sum & - + (sqrt(nuc % energy_0K(i)) * nuc % elastic_0K(i) & - + sqrt(nuc % energy_0K(i+1)) * nuc % elastic_0K(i+1)) / TWO & - * (nuc % energy_0K(i+1) - nuc % energy_0K(i)) - nuc % xs_cdf(i) = xs_cdf_sum - end do - - else ! read in non-0K data - nuc % n_grid = NE - allocate(nuc % energy(NE)) - allocate(nuc % total(NE)) - allocate(nuc % elastic(NE)) - allocate(nuc % fission(NE)) - allocate(nuc % nu_fission(NE)) - allocate(nuc % absorption(NE)) - - ! initialize cross sections - nuc % total = ZERO - nuc % elastic = ZERO - nuc % fission = ZERO - nuc % nu_fission = ZERO - nuc % absorption = ZERO - - ! Read data from XSS -- only the energy grid, elastic scattering and heating - ! cross section values are actually read from here. The total and absorption - ! cross sections are reconstructed from the partial reaction data. - - XSS_index = 1 - nuc % energy = get_real(NE) - - ! Skip total and absorption - XSS_index = XSS_index + 2*NE - - ! Continue reading elastic scattering and heating - nuc % elastic = get_real(NE) - - ! Determine if minimum/maximum energy for this nuclide is greater/less - ! than the previous - energy_min_neutron = max(energy_min_neutron, nuc%energy(1)) - energy_max_neutron = min(energy_max_neutron, nuc%energy(NE)) - end if - - end subroutine read_esz - -!=============================================================================== -! READ_NU_DATA reads data given on the number of neutrons emitted from fission -! as a function of the incoming energy of a neutron. This data may be broken -! down into prompt and delayed neutrons emitted as well. -!=============================================================================== - - subroutine read_nu_data(nuc) - type(Nuclide), intent(inout) :: nuc - - integer :: i, j ! loop index - integer :: idx ! index in XSS - integer :: KNU ! location for nu data - integer :: LNU ! type of nu data (polynomial or tabular) - integer :: NR ! number of interpolation regions - integer :: NE ! number of energies - integer :: NPCR ! number of delayed neutron precursor groups - integer :: LOCC ! location of energy distributions for given MT - integer :: LAW - integer :: IDAT - real(8) :: total_group_probability - type(Tabulated1D) :: yield_delayed - type(Tabulated1D) :: group_probability - - if (JXS(2) == 0) then - ! Nuclide is not fissionable - return - end if - - ! Determine number of delayed neutron precursors - if (JXS(24) > 0) then - NPCR = NXS(8) - else - NPCR = 0 - end if - nuc % n_precursor = NPCR - - ! Check to make sure nuclide does not have more than the maximum number - ! of delayed groups - if (NPCR > MAX_DELAYED_GROUPS) then - call fatal_error("Encountered nuclide with " // trim(to_str(NPCR)) & - // " delayed groups while the maximum number of delayed groups is " & - // trim(to_str(MAX_DELAYED_GROUPS))) - end if - - associate (rx => nuc % reactions(nuc % index_fission(1))) - ! Allocate space for prompt/delayed neutron products - allocate(rx % products(1 + NPCR)) - rx % products(:) % particle = NEUTRON - - if (XSS(JXS(2)) > 0) then - ! ======================================================================= - ! PROMPT OR TOTAL NU DATA - - ! If delayed data is present, then prompt data must be present. Otherwise - ! the product represents 'total' neutron emission - if (JXS(24) > 0) then - rx % products(1) % emission_mode = EMISSION_PROMPT - else - rx % products(1) % emission_mode = EMISSION_TOTAL - end if - - KNU = JXS(2) - LNU = nint(XSS(KNU)) - if (LNU == 1) then - ! Polynomial data - allocate(Polynomial :: rx % products(1) % yield) - - ! determine order of polynomial and read coefficients - select type (yield => rx % products(1) % yield) - type is (Polynomial) - call yield % from_ace(XSS, KNU + 1) - end select - - elseif (LNU == 2) then - ! Tabulated data - allocate(Tabulated1D :: rx % products(1) % yield) - - select type(yield => rx % products(1) % yield) - type is (Tabulated1D) - call yield % from_ace(XSS, KNU + 1) - end select - - end if - - elseif (XSS(JXS(2)) < 0) then - ! ======================================================================= - ! PROMPT AND TOTAL NU DATA - - rx % products(1) % emission_mode = EMISSION_PROMPT - - KNU = JXS(2) + 1 - LNU = nint(XSS(KNU)) - if (LNU == 1) then - ! Polynomial data - allocate(Polynomial :: rx % products(1) % yield) - - ! determine order of polynomial and read coefficients - select type (yield => rx % products(1) % yield) - type is (Polynomial) - call yield % from_ace(XSS, KNU + 1) - end select - - elseif (LNU == 2) then - ! Tabulated data - allocate(Tabulated1D :: rx % products(1) % yield) - - select type(yield => rx % products(1) % yield) - type is (Tabulated1D) - call yield % from_ace(XSS, KNU + 1) - end select - end if - - KNU = JXS(2) + nint(abs(XSS(JXS(2)))) + 1 - LNU = nint(XSS(KNU)) - if (LNU == 1) then - ! Polynomial data - allocate(Polynomial :: nuc % total_nu) - - ! determine order of polynomial and read coefficients - select type (yield => nuc % total_nu) - type is (Polynomial) - call yield % from_ace(XSS, KNU + 1) - end select - - elseif (LNU == 2) then - ! Tabulated data - allocate(Tabulated1D :: nuc % total_nu) - - select type(yield => nuc % total_nu) - type is (Tabulated1D) - call yield % from_ace(XSS, KNU + 1) - end select - end if - end if - - if (JXS(24) > 0) then - ! ======================================================================= - ! DELAYED NU DATA - - ! Read total yield of delayed neutrons - call yield_delayed % from_ace(XSS, JXS(24) + 1) - - idx = JXS(25) - total_group_probability = ZERO - do i = 1, NPCR - ! Set emission mode and decay rate - rx % products(1 + i) % emission_mode = EMISSION_DELAYED - rx % products(1 + i) % decay_rate = XSS(idx) - - ! Read probability for this precursor group - call group_probability % from_ace(XSS, idx + 1) - - ! Set yield based on product of group probability and delayed yield - if (all(group_probability % y == group_probability % y(1))) then - allocate(Tabulated1D :: rx % products(1 + i) % yield) - select type (yield => rx % products(1 + i) % yield) - type is (Tabulated1D) - yield = yield_delayed - yield % y(:) = yield % y(:) * group_probability % y(1) - total_group_probability = total_group_probability + group_probability % y(1) - end select - else - call fatal_error("Delayed neutron with energy-dependent group & - &probability not implemented") - end if - - ! Advance position - NR = nint(XSS(idx + 1)) - NE = nint(XSS(idx + 2 + 2*NR)) - idx = idx + 3 + 2*(NR + NE) - - ! ======================================================================= - ! DELAYED NEUTRON ENERGY DISTRIBUTION - - ! Read energy distribution - LOCC = nint(XSS(JXS(26) + i - 1)) - - ! Determine law and location of data - LAW = nint(XSS(JXS(27) + LOCC)) - IDAT = nint(XSS(JXS(27) + LOCC + 1)) - - ! read energy distribution data - associate(p => rx % products(1 + i)) - allocate(p % applicability(1)) - allocate(p % distribution(1)) - call get_energy_dist(p % distribution(1) % obj, LAW, JXS(27), IDAT, & - ZERO, ZERO) - - select type (aedist => p % distribution(1) % obj) - type is (UncorrelatedAngleEnergy) - aedist % fission = .true. - end select - end associate - end do - - ! Renormalize delayed neutron yields to reflect fact that in ACE file, the - ! sum of the group probabilities is not exactly one - do i = 1, NPCR - select type (yield => rx % products(1 + i) % yield) - type is (Tabulated1D) - yield % y(:) = yield % y(:) / total_group_probability - end select - end do - end if - - ! Assign products to other fission reactions - do i = 2, nuc % n_fission - j = nuc % index_fission(i) - allocate(nuc % reactions(j) % products(1 + NPCR)) - nuc % reactions(j) % products(:) = rx % products(:) - end do - end associate - - end subroutine read_nu_data - -!=============================================================================== -! READ_REACTIONS - Get the list of reaction MTs for this cross-section -! table. The MT values are somewhat arbitrary. Also read in Q-values, neutron -! multiplicities, and cross-sections. -!=============================================================================== - - subroutine read_reactions(nuc) - type(Nuclide), intent(inout) :: nuc - - integer :: i ! loop indices - integer :: i_fission ! index in nuc % index_fission - integer :: LMT ! index of MT list in XSS - integer :: NMT ! Number of reactions - integer :: JXS4 ! index of Q values in XSS - integer :: JXS5 ! index of neutron multiplicities in XSS - integer :: JXS7 ! index of reactions cross-sections in XSS - integer :: LXS ! location of cross-section locators - integer :: LOCA ! location of cross-section for given MT - integer :: IE ! reaction's starting index on energy grid - integer :: NE ! number of energies - real(8) :: y - type(ListInt) :: MTs - - LMT = JXS(3) - JXS4 = JXS(4) - JXS5 = JXS(5) - LXS = JXS(6) - JXS7 = JXS(7) - NMT = NXS(4) - - ! allocate array of reactions. Add one since we need to include an elastic - ! scattering channel - nuc % n_reaction = NMT + 1 - allocate(nuc % reactions(NMT+1)) - - ! Store elastic scattering cross-section on reaction one -- note that the - ! sigma array is not allocated or stored for elastic scattering since it is - ! already stored in nuc % elastic - associate (rxn => nuc % reactions(1)) - rxn % MT = 2 - rxn % Q_value = ZERO - allocate(rxn % products(1)) - rxn % products(1) % particle = NEUTRON - allocate(Constant1D :: rxn % products(1) % yield) - select type(yield => rxn % products(1) % yield) - type is (Constant1D) - yield % y = 1 - end select - rxn % threshold = 1 - rxn % scatter_in_cm = .true. - allocate(rxn % products(1) % distribution(1)) - allocate(UncorrelatedAngleEnergy :: rxn % products(1) % distribution(1) % obj) - end associate - - ! Add contribution of elastic scattering to total cross section - nuc % total = nuc % total + nuc % elastic - - ! By default, set nuclide to not fissionable and then change if fission - ! reactions are encountered - nuc % fissionable = .false. - nuc % has_partial_fission = .false. - nuc % n_fission = 0 - i_fission = 0 - - do i = 1, NMT - associate (rxn => nuc % reactions(i+1)) - ! read MT number, Q-value, and neutrons produced - rxn % MT = int(XSS(LMT + i - 1)) - rxn % Q_value = XSS(JXS4 + i - 1) - rxn % scatter_in_cm = (nint(XSS(JXS5 + i - 1)) < 0) - - if (.not. is_fission(rxn % MT)) then - allocate(rxn % products(1)) - rxn % products(1) % particle = NEUTRON - - y = abs(nint(XSS(JXS5 + i - 1))) - if (y > 100) then - ! Read energy-dependent multiplicities - - ! Set flag and allocate space for Tabulated1D to store yield - allocate(Tabulated1D :: rxn % products(1) % yield) - - ! Read yield function - select type (yield => rxn % products(1) % yield) - type is (Tabulated1D) - XSS_index = JXS(11) + int(y) - 101 - call yield % from_ace(XSS, XSS_index) - end select - else - ! Integral yield - allocate(Constant1D :: rxn % products(1) % yield) - select type (yield => rxn % products(1) % yield) - type is (Constant1D) - yield % y = y - end select - end if - end if - - ! read starting energy index - LOCA = int(XSS(LXS + i - 1)) - IE = int(XSS(JXS7 + LOCA - 1)) - rxn % threshold = IE - - ! read number of energies cross section values - NE = int(XSS(JXS7 + LOCA)) - allocate(rxn % sigma(NE)) - XSS_index = JXS7 + LOCA + 1 - rxn % sigma = get_real(NE) - end associate - end do - - ! Create set of MT values - do i = 1, size(nuc % reactions) - call MTs % append(nuc % reactions(i) % MT) - call nuc%reaction_index%add_key(nuc%reactions(i)%MT, i) - end do - - ! Create total, absorption, and fission cross sections - do i = 2, size(nuc % reactions) - associate (rxn => nuc % reactions(i)) - IE = rxn % threshold - NE = size(rxn % sigma) - - ! Skip total inelastic level scattering, gas production cross sections - ! (MT=200+), etc. - if (rxn % MT == N_LEVEL .or. rxn % MT == N_NONELASTIC) cycle - if (rxn % MT > N_5N2P .and. rxn % MT < N_P0) cycle - - ! Skip level cross sections if total is available - if (rxn % MT >= N_P0 .and. rxn % MT <= N_PC .and. MTs % contains(N_P)) cycle - if (rxn % MT >= N_D0 .and. rxn % MT <= N_DC .and. MTs % contains(N_D)) cycle - if (rxn % MT >= N_T0 .and. rxn % MT <= N_TC .and. MTs % contains(N_T)) cycle - if (rxn % MT >= N_3HE0 .and. rxn % MT <= N_3HEC .and. MTs % contains(N_3HE)) cycle - if (rxn % MT >= N_A0 .and. rxn % MT <= N_AC .and. MTs % contains(N_A)) cycle - if (rxn % MT >= N_2N0 .and. rxn % MT <= N_2NC .and. MTs % contains(N_2N)) cycle - - ! Add contribution to total cross section - nuc % total(IE:IE+NE-1) = nuc % total(IE:IE+NE-1) + rxn % sigma - - ! Add contribution to absorption cross section - if (is_disappearance(rxn % MT)) then - nuc % absorption(IE:IE+NE-1) = nuc % absorption(IE:IE+NE-1) + rxn % sigma - end if - - ! Information about fission reactions - if (rxn % MT == N_FISSION) then - allocate(nuc % index_fission(1)) - elseif (rxn % MT == N_F) then - allocate(nuc % index_fission(PARTIAL_FISSION_MAX)) - nuc % has_partial_fission = .true. - end if - - ! Add contribution to fission cross section - if (is_fission(rxn % MT)) then - nuc % fissionable = .true. - nuc % fission(IE:IE+NE-1) = nuc % fission(IE:IE+NE-1) + rxn % sigma - - ! Also need to add fission cross sections to absorption - nuc % absorption(IE:IE+NE-1) = nuc % absorption(IE:IE+NE-1) + rxn % sigma - - ! If total fission reaction is present, there's no need to store the - ! reaction cross-section since it was copied to nuc % fission - if (rxn % MT == N_FISSION) deallocate(rxn % sigma) - - ! Keep track of this reaction for easy searching later - i_fission = i_fission + 1 - nuc % index_fission(i_fission) = i - nuc % n_fission = nuc % n_fission + 1 - end if - end associate - end do - - ! Clear MTs set - call MTs % clear() - - end subroutine read_reactions - -!=============================================================================== -! READ_ANGULAR_DIST parses the angular distribution for each reaction with -! secondary neutrons -!=============================================================================== - - subroutine read_angular_dist(nuc) - type(Nuclide), intent(inout) :: nuc - - integer :: LOCB ! location of angular distribution for given MT - integer :: NE ! number of incoming energies - integer :: NP ! number of points for cosine distribution - integer :: i ! index in reactions array - integer :: j ! index over incoming energies - integer :: k ! index over energy distributions - integer :: interp - integer, allocatable :: LC(:) ! locator - - ! loop over all reactions with secondary neutrons -- NXS(5) does not include - ! elastic scattering - do i = 1, NXS(5) + 1 - associate (rxn => nuc%reactions(i)) - ! find location of angular distribution - LOCB = int(XSS(JXS(8) + i - 1)) - - ! Angular distribution given as part of a correlated angle-energy distribution - if (LOCB == -1) cycle - - ! No angular distribution data are given for this reaction, isotropic - ! scattering is assumed (in CM if TY < 0 and in LAB if TY > 0) - if (LOCB == 0) cycle - - ! Loop over each separate energy distribution. Even though there is only - ! "one" angular distribution, it is repeated as many times as there are - ! energy distributions for this reaction since the - ! UncorrelatedAngleEnergy type holds one angle and energy distribution. - do k = 1, size(rxn % products(1) % distribution) - select type (aedist => rxn % products(1) % distribution(k) % obj) - type is (UncorrelatedAngleEnergy) - ! allocate space for incoming energies and locations - NE = int(XSS(JXS(9) + LOCB - 1)) - allocate(aedist % angle % energy(NE)) - allocate(aedist % angle % distribution(NE)) - allocate(LC(NE)) - - ! read incoming energy grid and location of nucs - XSS_index = JXS(9) + LOCB - aedist % angle % energy(:) = get_real(NE) - LC(:) = get_int(NE) - - ! determine dize of data block - do j = 1, NE - if (LC(j) == 0) then - ! isotropic - allocate(Uniform :: aedist % angle % distribution(j) % obj) - select type (adist => aedist % angle % distribution(j) % obj) - type is (Uniform) - adist % a = -ONE - adist % b = ONE - end select - - elseif (LC(j) > 0) then - ! 32 equiprobable bins - allocate(Equiprobable :: aedist % angle % distribution(j) % obj) - select type (adist => aedist % angle % distribution(j) % obj) - type is (Equiprobable) - allocate(adist % x(33)) - end select - - elseif (LC(j) < 0) then - ! tabular distribution - allocate(Tabular :: aedist % angle % distribution(j) % obj) - end if - end do - - ! read angular distribution -- currently this does not actually parse the - ! angular distribution tables for each incoming energy, that must be done - ! on-the-fly - do j = 1, NE - XSS_index = JXS(9) + abs(LC(j)) - 1 - select type(adist => aedist % angle % distribution(j) % obj) - type is (Equiprobable) - adist % x(:) = get_real(33) - type is (Tabular) - ! determine interpolation and number of points - interp = nint(XSS(XSS_index)) - NP = nint(XSS(XSS_index + 1)) - - ! Get probability density data - XSS_index = XSS_index + 2 - allocate(adist % x(NP), adist % p(NP), adist % c(NP)) - adist % x(:) = get_real(NP) - adist % p(:) = get_real(NP) - adist % c(:) = get_real(NP) - end select - end do - deallocate(LC) - - end select - end do - end associate - end do - - end subroutine read_angular_dist - -!=============================================================================== -! READ_ENERGY_DIST parses the secondary energy distribution for each reaction -! with seconary neutrons (except elastic scattering) -!=============================================================================== - - subroutine read_energy_dist(nuc) - type(Nuclide), intent(inout) :: nuc - - integer :: i ! loop index - integer :: n - integer :: IDAT ! locator for distribution data - integer :: LNW ! location of next energy law - integer :: LAW ! Type of energy law - - ! Loop over all reactions - do i = 1, NXS(5) - ! Determine how many energy distributions are present for this reaction - LNW = nint(XSS(JXS(10) + i - 1)) - n = 0 - do while (LNW > 0) - n = n + 1 - LNW = nint(XSS(JXS(11) + LNW - 1)) - end do - - ! Allocate space for distributions and probability of validity - associate (p => nuc % reactions(i + 1) % products(1)) - allocate(p % applicability(n)) - allocate(p % distribution(n)) - - LNW = nint(XSS(JXS(10) + i - 1)) - n = 0 - do while (LNW > 0) - n = n + 1 - - ! Determine energy law and location of data - LAW = nint(XSS(JXS(11) + LNW)) - IDAT = nint(XSS(JXS(11) + LNW + 1)) - - ! Read probability of law validity - call p % applicability(n) % from_ace(XSS, JXS(11) + LNW + 2) - - ! Read energy law data - call get_energy_dist(p % distribution(n) % obj, LAW, & - JXS(11), IDAT, nuc % awr, nuc % reactions(i + 1) % Q_value) - - ! <<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<< - ! Before the secondary distribution refactor, when the angle/energy - ! distribution was uncorrelated, no angle was actually sampled. With - ! the refactor, an angle is always sampled for an uncorrelated - ! distribution even when no angle distribution exists in the ACE file - ! (isotropic is assumed). To preserve the RNG stream, we explicitly - ! mark fission reactions so that we avoid the angle sampling. - if (any(nuc % reactions(i + 1) % MT == & - [N_FISSION, N_F, N_NF, N_2NF, N_3NF])) then - select type (aedist => p % distribution(n) % obj) - type is (UncorrelatedAngleEnergy) - aedist % fission = .true. - end select - end if - ! <<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<< - - ! Get locator for next distribution - LNW = nint(XSS(JXS(11) + LNW - 1)) - end do - end associate - end do - - end subroutine read_energy_dist - -!=============================================================================== -! GET_ENERGY_DIST reads in data for a single law for an energy distribution and -! calls itself recursively if there are multiple energy distributions for a -! single reaction -!=============================================================================== - - recursive subroutine get_energy_dist(aedist, law, LDIS, IDAT, awr, Q_value) - class(AngleEnergy), allocatable, intent(inout) :: aedist - integer, intent(in) :: law - integer, intent(in) :: LDIS - integer, intent(in) :: IDAT - real(8), intent(in) :: awr - real(8), intent(in) :: Q_value - - integer :: i, j - integer :: NR ! number of interpolation regions - integer :: NE ! number of incoming energies - integer :: NP ! number of outgoing energies/angles - integer :: interp - integer, allocatable :: L(:) ! locations of distributions for each Ein - integer, allocatable :: LC(:) ! locations of distributions for each Ein - - XSS_index = LDIS + IDAT - 1 - - if (law == 44) then - allocate(KalbachMann :: aedist) - elseif (law == 61) then - allocate(CorrelatedAngleEnergy :: aedist) - elseif (law == 66) then - allocate(NBodyPhaseSpace :: aedist) - else - allocate(UncorrelatedAngleEnergy :: aedist) - end if - - select type (aedist) - type is (UncorrelatedAngleEnergy) - ! ======================================================================== - ! UNCORRELATED ENERGY DISTRIBUTIONS - - select case (law) - case (1) - allocate(TabularEquiprobable :: aedist % energy) - select type (edist => aedist % energy) - type is (TabularEquiprobable) - NR = nint(XSS(XSS_index)) - NE = nint(XSS(XSS_index + 1 + 2*NR)) - if (NR > 0) then - call fatal_error("Multiple interpolation regions not yet supported & - &for tabular equiprobable energy distributions.") - end if - edist % n_region = NR - - ! Read incoming energies for which outgoing energies are tabulated - allocate(edist % energy_in(NE)) - XSS_index = XSS_index + 2 + 2*NR - edist % energy_in(:) = get_real(NE) - - ! Read outgoing energy tables - NP = nint(XSS(XSS_index)) - allocate(edist % energy_out(NP, NE)) - XSS_index = XSS_index + 1 - do i = 1, NE - edist % energy_out(:, i) = get_real(NP) - end do - end select - - case (3) - allocate(LevelInelastic :: aedist % energy) - select type (edist => aedist % energy) - type is (LevelInelastic) - edist % threshold = XSS(XSS_index) - edist % mass_ratio = XSS(XSS_index + 1) - end select - - case (4) - allocate(ContinuousTabular :: aedist % energy) - select type (edist => aedist % energy) - type is (ContinuousTabular) - NR = nint(XSS(XSS_index)) - XSS_index = XSS_index + 1 - if (NR > 1) then - call fatal_error("Multiple interpolation regions not yet supported & - &for continuous tabular energy distributions.") - end if - edist % n_region = NR - - ! Read breakpoints and interpolation parameters - if (NR > 0) then - allocate(edist % breakpoints(NR)) - allocate(edist % interpolation(NR)) - edist % breakpoints(:) = get_int(NR) - edist % interpolation(:) = get_int(NR) - end if - - ! Read incoming energies for which outgoing energies are tabulated and - ! locators - NE = nint(XSS(XSS_index)) - XSS_index = XSS_index + 1 - allocate(edist % energy(NE)) - allocate(L(NE)) - edist % energy(:) = get_real(NE) - L(:) = get_int(NE) - - ! Read outgoing energy tables - allocate(edist % distribution(NE)) - do i = 1, NE - ! Determine interpolation and number of discrete points - XSS_index = LDIS + L(i) - 1 - interp = nint(XSS(XSS_index)) - edist % distribution(i) % interpolation = mod(interp, 10) - edist % distribution(i) % n_discrete = (interp - & - edist % distribution(i) % interpolation)/10 - - ! check for discrete lines present - if (edist % distribution(i) % n_discrete > 0) then - call fatal_error("Discrete lines in continuous tabular & - &distribution not yet supported") - end if - - ! Determine number of points and allocate space - NP = nint(XSS(XSS_index + 1)) - allocate(edist % distribution(i) % e_out(NP)) - allocate(edist % distribution(i) % p(NP)) - allocate(edist % distribution(i) % c(NP)) - - ! Read tabular PDF for outgoing energy - XSS_index = XSS_index + 2 - edist % distribution(i) % e_out(:) = get_real(NP) - edist % distribution(i) % p(:) = get_real(NP) - edist % distribution(i) % c(:) = get_real(NP) - end do - - deallocate(L) - end select - - case (7) - allocate(MaxwellEnergy :: aedist % energy) - select type (edist => aedist % energy) - type is (MaxwellEnergy) - call edist % theta % from_ace(XSS, XSS_index) - edist % u = XSS(XSS_index + 2 + 2*edist % theta % n_regions + & - 2*edist % theta % n_pairs) - end select - - case (9) - allocate(Evaporation :: aedist % energy) - select type(edist => aedist % energy) - type is (Evaporation) - call edist % theta % from_ace(XSS, XSS_index) - edist % u = XSS(XSS_index + 2 + 2*edist % theta % n_regions + & - 2*edist % theta % n_pairs) - end select - - case (11) - allocate(WattEnergy :: aedist % energy) - select type(edist => aedist % energy) - type is (WattEnergy) - call edist % a % from_ace(XSS, XSS_index) - XSS_index = XSS_index + 2 + 2*edist % a % n_regions + 2*edist % a % n_pairs - call edist % b % from_ace(XSS, XSS_index) - XSS_index = XSS_index + 2 + 2*edist % b % n_regions + 2*edist % b % n_pairs - edist % u = XSS(XSS_index) - end select - - end select - - type is (KalbachMann) - ! ======================================================================== - ! CORRELATED KALBACH-MANN DISTRIBUTION - - NR = int(XSS(XSS_index)) - NE = int(XSS(XSS_index + 1 + 2*NR)) - if (NR > 0) then - call fatal_error("Multiple interpolation regions not yet supported & - &for Kalbach-Mann energy distributions.") - end if - aedist % n_region = NR - - ! Read incoming energies for which outgoing energies are tabulated and locators - allocate(aedist % energy(NE)) - allocate(L(NE)) - XSS_index = XSS_index + 2 + 2*NR - aedist % energy(:) = get_real(NE) - L(:) = get_int(NE) - - ! Read outgoing energy tables - allocate(aedist % distribution(NE)) - do i = 1, NE - ! Determine interpolation and number of discrete points - XSS_index = LDIS + L(i) - 1 - interp = nint(XSS(XSS_index)) - aedist % distribution(i) % interpolation = mod(interp, 10) - aedist % distribution(i) % n_discrete = (interp - aedist % distribution(i) % interpolation)/10 - - ! check for discrete lines present - if (aedist % distribution(i) % n_discrete > 0) then - call fatal_error("Discrete lines in Kalbach-Mann distribution not & - &yet supported") - end if - - ! Determine number of points and allocate space - NP = nint(XSS(XSS_index + 1)) - allocate(aedist % distribution(i) % e_out(NP)) - allocate(aedist % distribution(i) % p(NP)) - allocate(aedist % distribution(i) % c(NP)) - allocate(aedist % distribution(i) % r(NP)) - allocate(aedist % distribution(i) % a(NP)) - - ! Read tabular PDF for outgoing energy - XSS_index = XSS_index + 2 - aedist % distribution(i) % e_out(:) = get_real(NP) - aedist % distribution(i) % p(:) = get_real(NP) - aedist % distribution(i) % c(:) = get_real(NP) - aedist % distribution(i) % r(:) = get_real(NP) - aedist % distribution(i) % a(:) = get_real(NP) - end do - - deallocate(L) - - type is (CorrelatedAngleEnergy) - ! ======================================================================== - ! CORRELATED ANGLE-ENERGY DISTRIBUTION - - NR = int(XSS(XSS_index)) - NE = int(XSS(XSS_index + 1 + 2*NR)) - if (NR > 0) then - call fatal_error("Multiple interpolation regions not yet supported & - &for correlated angle-energy distributions.") - end if - aedist % n_region = NR - - ! Read incoming energies for which outgoing energies are tabulated and - ! locators - allocate(aedist % energy(NE)) - allocate(L(NE)) - XSS_index = XSS_index + 2 + 2*NR - aedist % energy(:) = get_real(NE) - L(:) = get_int(NE) - - ! Read outgoing energy tables - allocate(aedist % distribution(NE)) - do i = 1, NE - ! Determine interpolation and number of discrete points - XSS_index = LDIS + L(i) - 1 - interp = nint(XSS(XSS_index)) - aedist % distribution(i) % interpolation = mod(interp, 10) - aedist % distribution(i) % n_discrete = (interp - aedist % distribution(i) % interpolation)/10 - - ! check for discrete lines present - if (aedist % distribution(i) % n_discrete > 0) then - call fatal_error("Discrete lines in correlated angle-energy & - &distribution not yet supported") - end if - - ! Determine number of points and allocate space - NP = nint(XSS(XSS_index + 1)) - allocate(aedist % distribution(i) % e_out(NP)) - allocate(aedist % distribution(i) % p(NP)) - allocate(aedist % distribution(i) % c(NP)) - allocate(LC(NP)) - - ! Read tabular PDF for outgoing energy - XSS_index = XSS_index + 2 - aedist % distribution(i) % e_out(:) = get_real(NP) - aedist % distribution(i) % p(:) = get_real(NP) - aedist % distribution(i) % c(:) = get_real(NP) - LC(:) = get_int(NP) - - ! allocate angular distributions for each incoming/outgoing energy - allocate(aedist % distribution(i) % angle(NP)) - do j = 1, NP - if (LC(j) == 0) then - ! isotropic - allocate(Uniform :: aedist % distribution(i) % angle(j) % obj) - select type (adist => aedist % distribution(i) % angle(j) % obj) - type is (Uniform) - adist % a = -ONE - adist % b = ONE - end select - - elseif (LC(j) > 0) then - ! tabular distribution - allocate(Tabular :: aedist % distribution(i) % angle(j) % obj) - end if - end do - - ! read angular distributions - do j = 1, NP - XSS_index = LDIS + abs(LC(j)) - 1 - select type(adist => aedist % distribution(i) % angle(j) % obj) - type is (Tabular) - ! determine interpolation and number of points - interp = nint(XSS(XSS_index)) - NP = nint(XSS(XSS_index + 1)) - - ! Get probability density data - XSS_index = XSS_index + 2 - allocate(adist % x(NP), adist % p(NP), adist % c(NP)) - adist % x(:) = get_real(NP) - adist % p(:) = get_real(NP) - adist % c(:) = get_real(NP) - end select - end do - deallocate(LC) - - end do - - deallocate(L) - - type is (NBodyPhaseSpace) - ! ======================================================================== - ! N-BODY PHASE SPACE DISTRIBUTION - - aedist % n_bodies = int(XSS(XSS_index)) - aedist % mass_ratio = XSS(XSS_index + 1) - aedist % A = awr - aedist % Q = Q_value - end select - - end subroutine get_energy_dist - -!=============================================================================== -! READ_UNR_RES reads in unresolved resonance probability tables if present. -!=============================================================================== - - subroutine read_unr_res(nuc) - type(Nuclide), intent(inout) :: nuc - - integer :: JXS23 ! location of URR data - integer :: lc ! locator - integer :: N ! # of incident energies - integer :: M ! # of probabilities - integer :: i ! index over incoming energies - integer :: j ! index over values - integer :: k ! index over probabilities - - ! determine locator for URR data - JXS23 = JXS(23) - - ! check if URR data is present - if (JXS23 /= 0) then - nuc % urr_present = .true. - allocate(nuc % urr_data) - lc = JXS23 - else - nuc % urr_present = .false. - return - end if - - ! read parameters - nuc % urr_data % n_energy = int(XSS(lc)) - nuc % urr_data % n_prob = int(XSS(lc + 1)) - nuc % urr_data % interp = int(XSS(lc + 2)) - nuc % urr_data % inelastic_flag = int(XSS(lc + 3)) - nuc % urr_data % absorption_flag = int(XSS(lc + 4)) - if (int(XSS(lc + 5)) == 0) then - nuc % urr_data % multiply_smooth = .false. - else - nuc % urr_data % multiply_smooth = .true. - end if - - ! if the inelastic competition flag indicates that the inelastic cross - ! section should be determined from a normal reaction cross section, we need - ! to set up a pointer to that reaction - nuc % urr_inelastic = NONE - if (nuc % urr_data % inelastic_flag > 0) then - do i = 1, nuc % n_reaction - if (nuc % reactions(i) % MT == nuc % urr_data % inelastic_flag) then - nuc % urr_inelastic = i - end if - end do - - ! Abort if no corresponding inelastic reaction was found - if (nuc % urr_inelastic == NONE) then - call fatal_error("Could not find inelastic reaction specified on & - &unresolved resonance probability table.") - end if - end if - - ! allocate incident energies and probability tables - N = nuc % urr_data % n_energy - M = nuc % urr_data % n_prob - allocate(nuc % urr_data % energy(N)) - allocate(nuc % urr_data % prob(N,6,M)) - - ! read incident energies - XSS_index = lc + 6 - nuc % urr_data % energy = get_real(N) - - ! read probability tables - do i = 1, N - do j = 1, 6 - do k = 1, M - nuc % urr_data % prob(i,j,k) = XSS(XSS_index) - XSS_index = XSS_index + 1 - end do - end do - end do - - ! Check for negative values - if (any(nuc % urr_data % prob < ZERO)) then - if (master) call warning("Negative value(s) found on probability table & - &for nuclide " // nuc % name) - end if - - end subroutine read_unr_res -!=============================================================================== -! GENERATE_NU_FISSION precalculates the microscopic nu-fission cross section for -! a given nuclide. This is done so that the nu_total function does not need to -! be called during cross section lookups. -!=============================================================================== - - subroutine generate_nu_fission(nuc) - type(Nuclide), intent(inout) :: nuc - - integer :: i ! index on nuclide energy grid - - do i = 1, size(nuc % energy) - nuc % nu_fission(i) = nuc % nu(nuc % energy(i), EMISSION_TOTAL) * & - nuc % fission(i) - end do - end subroutine generate_nu_fission - -!=============================================================================== -! READ_THERMAL_DATA reads elastic and inelastic cross sections and corresponding -! secondary energy/angle distributions derived from experimental S(a,b) -! data. Namely, this routine reads the ITIE, ITCE, ITXE, and ITCA blocks. -!=============================================================================== - - subroutine read_thermal_data(table) - type(SAlphaBeta), intent(inout) :: table - - integer :: i ! index for incoming energies - integer :: j ! index for outgoing energies - integer :: k ! index for outoging angles - integer :: lc ! location in XSS array - integer :: NE_in ! number of incoming energies - integer :: NE_out ! number of outgoing energies - integer :: NMU ! number of outgoing angles - integer :: JXS4 ! location of elastic energy table - integer(8), allocatable :: LOCC(:) ! Location of inelastic data - - ! read secondary energy mode for inelastic scattering - table % secondary_mode = NXS(7) - - ! read number of inelastic energies and allocate arrays - NE_in = int(XSS(JXS(1))) - table % n_inelastic_e_in = NE_in - allocate(table % inelastic_e_in(NE_in)) - allocate(table % inelastic_sigma(NE_in)) - - ! read inelastic energies and cross-sections - XSS_index = JXS(1) + 1 - table % inelastic_e_in = get_real(NE_in) - table % inelastic_sigma = get_real(NE_in) - - ! set threshold value - table % threshold_inelastic = table % inelastic_e_in(NE_in) - - ! allocate space for outgoing energy/angle for inelastic - ! scattering - if (table % secondary_mode == SAB_SECONDARY_EQUAL .or. & - table % secondary_mode == SAB_SECONDARY_SKEWED) then - NMU = NXS(3) + 1 - table % n_inelastic_mu = NMU - NE_out = NXS(4) - table % n_inelastic_e_out = NE_out - allocate(table % inelastic_e_out(NE_out, NE_in)) - allocate(table % inelastic_mu(NMU, NE_out, NE_in)) - else if (table % secondary_mode == SAB_SECONDARY_CONT) then - NMU = NXS(3) - 1 - table % n_inelastic_mu = NMU - allocate(table % inelastic_data(NE_in)) - allocate(LOCC(NE_in)) - ! NE_out will be determined later - end if - - ! read outgoing energy/angle distribution for inelastic scattering - if (table % secondary_mode == SAB_SECONDARY_EQUAL .or. & - table % secondary_mode == SAB_SECONDARY_SKEWED) then - lc = JXS(3) - 1 - do i = 1, NE_in - do j = 1, NE_out - ! read outgoing energy - table % inelastic_e_out(j,i) = XSS(lc + 1) - - ! read outgoing angles for this outgoing energy - do k = 1, NMU - table % inelastic_mu(k,j,i) = XSS(lc + 1 + k) - end do - - ! advance pointer - lc = lc + 1 + NMU - end do - end do - else if (table % secondary_mode == SAB_SECONDARY_CONT) then - ! Get the location pointers to each Ein's DistEnergySAB data - LOCC = get_int(NE_in) - ! Get the number of outgoing energies and allocate space accordingly - do i = 1, NE_in - NE_out = int(XSS(XSS_index + i - 1)) - table % inelastic_data(i) % n_e_out = NE_out - allocate(table % inelastic_data(i) % e_out (NE_out)) - allocate(table % inelastic_data(i) % e_out_pdf (NE_out)) - allocate(table % inelastic_data(i) % e_out_cdf (NE_out)) - allocate(table % inelastic_data(i) % mu (NMU, NE_out)) - end do - - ! Now we can fill the inelastic_data(i) attributes - do i = 1, NE_in - XSS_index = int(LOCC(i)) - NE_out = table % inelastic_data(i) % n_e_out - do j = 1, NE_out - table % inelastic_data(i) % e_out(j) = XSS(XSS_index + 1) - table % inelastic_data(i) % e_out_pdf(j) = XSS(XSS_index + 2) - table % inelastic_data(i) % e_out_cdf(j) = XSS(XSS_index + 3) - table % inelastic_data(i) % mu(:, j) = & - XSS(XSS_index + 4: XSS_index + 4 + NMU - 1) - XSS_index = XSS_index + 4 + NMU - 1 - end do - end do - end if - - ! read number of elastic energies and allocate arrays - JXS4 = JXS(4) - if (JXS4 /= 0) then - NE_in = int(XSS(JXS4)) - table % n_elastic_e_in = NE_in - allocate(table % elastic_e_in(NE_in)) - allocate(table % elastic_P(NE_in)) - - ! read elastic energies and P - XSS_index = JXS4 + 1 - table % elastic_e_in = get_real(NE_in) - table % elastic_P = get_real(NE_in) - - ! set threshold - table % threshold_elastic = table % elastic_e_in(NE_in) - - ! determine whether sigma=P or sigma = P/E - table % elastic_mode = NXS(5) - else - table % threshold_elastic = ZERO - table % n_elastic_e_in = 0 - end if - - ! allocate space for outgoing energy/angle for elastic scattering - NMU = NXS(6) + 1 - table % n_elastic_mu = NMU - if (NMU > 0) then - allocate(table % elastic_mu(NMU, NE_in)) - end if - - ! read equiprobable outgoing cosines for elastic scattering each - ! incoming energy - if (JXS4 /= 0 .and. NMU /= 0) then - lc = JXS(6) - 1 - do i = 1, NE_in - do j = 1, NMU - table % elastic_mu(j,i) = XSS(lc + j) - end do - lc = lc + NMU - end do - end if - - end subroutine read_thermal_data - -!=============================================================================== -! GET_INT returns an array of integers read from the current position in the XSS -! array -!=============================================================================== - - function get_int(n_values) result(array) - - integer, intent(in) :: n_values ! number of values to read - integer :: array(n_values) ! array of values - - array = int(XSS(XSS_index:XSS_index + n_values - 1)) - XSS_index = XSS_index + n_values - - end function get_int - -!=============================================================================== -! GET_REAL returns an array of real(8)s read from the current position in the -! XSS array -!=============================================================================== - - function get_real(n_values) result(array) - - integer, intent(in) :: n_values ! number of values to read - real(8) :: array(n_values) ! array of values - - array = XSS(XSS_index:XSS_index + n_values - 1) - XSS_index = XSS_index + n_values - - end function get_real - -end module ace diff --git a/src/angle_distribution.F90 b/src/angle_distribution.F90 index 282d51b73..830ba836c 100644 --- a/src/angle_distribution.F90 +++ b/src/angle_distribution.F90 @@ -1,7 +1,10 @@ module angle_distribution - use constants, only: ZERO, ONE - use distribution_univariate, only: DistributionContainer + use constants, only: ZERO, ONE, HISTOGRAM, LINEAR_LINEAR + use distribution_univariate, only: DistributionContainer, Tabular + use hdf5, only: HID_T, HSIZE_T + use hdf5_interface, only: read_attribute, get_shape, read_dataset, & + open_dataset, close_dataset use random_lcg, only: prn use search, only: binary_search @@ -21,6 +24,7 @@ module angle_distribution type(DistributionContainer), allocatable :: distribution(:) contains procedure :: sample => angle_sample + procedure :: from_hdf5 => angle_from_hdf5 end type AngleDistribution contains @@ -60,4 +64,68 @@ contains if (abs(mu) > ONE) mu = sign(ONE, mu) end function angle_sample + subroutine angle_from_hdf5(this, group_id) + class(AngleDistribution), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + + integer :: i, j + integer :: n + integer :: n_energy + integer(HID_T) :: dset_id + integer(HSIZE_T) :: dims(1), dims2(2) + integer, allocatable :: offsets(:) + integer, allocatable :: interp(:) + real(8), allocatable :: temp(:,:) + + ! Get incoming energies + dset_id = open_dataset(group_id, 'energy') + call get_shape(dset_id, dims) + n_energy = int(dims(1), 4) + allocate(this%energy(n_energy)) + allocate(this%distribution(n_energy)) + call read_dataset(this%energy, dset_id) + call close_dataset(dset_id) + + ! Get outgoing energy distribution data + dset_id = open_dataset(group_id, 'mu') + call read_attribute(offsets, dset_id, 'offsets') + call read_attribute(interp, dset_id, 'interpolation') + call get_shape(dset_id, dims2) + allocate(temp(dims2(1), dims2(2))) + call read_dataset(temp, dset_id) + call close_dataset(dset_id) + + do i = 1, n_energy + ! Determine number of outgoing energies + j = offsets(i) + if (i < n_energy) then + n = offsets(i+1) - j + else + n = size(temp, 1) - j + end if + + ! Create and initialize tabular distribution + allocate(Tabular :: this%distribution(i)%obj) + select type (mudist => this%distribution(i)%obj) + type is (Tabular) + mudist % interpolation = interp(i) + allocate(mudist % x(n), mudist % p(n), mudist % c(n)) + mudist % x(:) = temp(j+1:j+n, 1) + mudist % p(:) = temp(j+1:j+n, 2) + + ! To get answers that match ACE data, for now we still use the tabulated + ! CDF values that were passed through to the HDF5 library. At a later + ! time, we can remove the CDF values from the HDF5 library and + ! reconstruct them using the PDF + if (.true.) then + mudist % c(:) = temp(j+1:j+n, 3) + else + call mudist % initialize(temp(j+1:j+n, 1), temp(j+1:j+n, 2), interp(i)) + end if + end select + + j = j + n + end do + end subroutine angle_from_hdf5 + end module angle_distribution diff --git a/src/angleenergy_header.F90 b/src/angleenergy_header.F90 index 483bad856..60d5443c4 100644 --- a/src/angleenergy_header.F90 +++ b/src/angleenergy_header.F90 @@ -1,5 +1,7 @@ module angleenergy_header + use hdf5, only: HID_T + !=============================================================================== ! ANGLEENERGY (abstract) defines a correlated or uncorrelated angle-energy ! distribution that is a function of incoming energy. Each derived type must @@ -10,6 +12,7 @@ module angleenergy_header type, abstract :: AngleEnergy contains procedure(angleenergy_sample_), deferred :: sample + procedure(angleenergy_from_hdf5_), deferred :: from_hdf5 end type AngleEnergy abstract interface @@ -20,6 +23,12 @@ module angleenergy_header real(8), intent(out) :: E_out real(8), intent(out) :: mu end subroutine angleenergy_sample_ + + subroutine angleenergy_from_hdf5_(this, group_id) + import AngleEnergy, HID_T + class(AngleEnergy), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + end subroutine angleenergy_from_hdf5_ end interface type :: AngleEnergyContainer diff --git a/src/constants.F90 b/src/constants.F90 index b3e5ed89b..838830887 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -237,6 +237,13 @@ module constants ASCII = 1, & ! ASCII cross section file BINARY = 2 ! Binary cross section file + ! Library types + integer, parameter :: & + LIBRARY_NEUTRON = 1, & + LIBRARY_THERMAL = 2, & + LIBRARY_PHOTON = 3, & + LIBRARY_MULTIGROUP = 4 + ! Probability table parameters integer, parameter :: & URR_CUM_PROB = 1, & diff --git a/src/endf_header.F90 b/src/endf_header.F90 index e9a073f4e..8b6ad63f1 100644 --- a/src/endf_header.F90 +++ b/src/endf_header.F90 @@ -2,13 +2,16 @@ module endf_header use constants, only: ZERO, HISTOGRAM, LINEAR_LINEAR, LINEAR_LOG, & LOG_LINEAR, LOG_LOG + use hdf5_interface + use hdf5, only: HID_T, HSIZE_T use search, only: binary_search -implicit none + implicit none type, abstract :: Function1D contains procedure(function1d_evaluate_), deferred :: evaluate + procedure(function1d_from_hdf5_), deferred :: from_hdf5 end type Function1D abstract interface @@ -18,6 +21,12 @@ implicit none real(8), intent(in) :: x real(8) :: y end function function1d_evaluate_ + + subroutine function1d_from_hdf5_(this, dset_id) + import Function1D, HID_T + class(Function1D), intent(inout) :: this + integer(HID_T), intent(in) :: dset_id + end subroutine function1d_from_hdf5_ end interface !=============================================================================== @@ -27,6 +36,7 @@ implicit none type, extends(Function1D) :: Constant1D real(8) :: y contains + procedure :: from_hdf5 => constant1d_from_hdf5 procedure :: evaluate => constant1d_evaluate end type Constant1D @@ -37,6 +47,7 @@ implicit none type, extends(Function1D) :: Polynomial real(8), allocatable :: coef(:) ! coefficients contains + procedure :: from_hdf5 => polynomial_from_hdf5 procedure :: evaluate => polynomial_evaluate procedure :: from_ace => polynomial_from_ace end type Polynomial @@ -54,6 +65,7 @@ implicit none real(8), allocatable :: y(:) ! values of ordinate contains procedure :: from_ace => tabulated1d_from_ace + procedure :: from_hdf5 => tabulated1d_from_hdf5 procedure :: evaluate => tabulated1d_evaluate end type Tabulated1D @@ -63,6 +75,13 @@ contains ! Constant1D implementation !=============================================================================== + subroutine constant1d_from_hdf5(this, dset_id) + class(Constant1D), intent(inout) :: this + integer(HID_T), intent(in) :: dset_id + + call read_dataset(this % y, dset_id) + end subroutine constant1d_from_hdf5 + pure function constant1d_evaluate(this, x) result(y) class(Constant1D), intent(in) :: this real(8), intent(in) :: x @@ -93,6 +112,17 @@ contains this % coef(:) = xss(idx + 1 : idx + nc) end subroutine polynomial_from_ace + subroutine polynomial_from_hdf5(this, dset_id) + class(Polynomial), intent(inout) :: this + integer(HID_T), intent(in) :: dset_id + + integer(HSIZE_T) :: dims(1) + + call get_shape(dset_id, dims) + allocate(this % coef(dims(1))) + call read_dataset(this % coef, dset_id) + end subroutine polynomial_from_hdf5 + pure function polynomial_evaluate(this, x) result(y) class(Polynomial), intent(in) :: this real(8), intent(in) :: x @@ -148,6 +178,28 @@ contains this%y(:) = xss(idx + 2*nr + 2 + ne : idx + 2*nr + 1 + 2*ne) end subroutine tabulated1d_from_ace + subroutine tabulated1d_from_hdf5(this, dset_id) + class(Tabulated1D), intent(inout) :: this + integer(HID_T), intent(in) :: dset_id + + real(8), allocatable :: xy(:,:) + integer(HSIZE_T) :: dims(2) + + call read_attribute(this%nbt, dset_id, 'breakpoints') + call read_attribute(this%int, dset_id, 'interpolation') + this%n_regions = size(this%nbt) + + call get_shape(dset_id, dims) + this%n_pairs = int(dims(1), 4) + allocate(this%x(this%n_pairs)) + allocate(this%y(this%n_pairs)) + + allocate(xy(dims(1), dims(2))) + call read_dataset(xy, dset_id) + this%x(:) = xy(:,1) + this%y(:) = xy(:,2) + end subroutine tabulated1d_from_hdf5 + pure function tabulated1d_evaluate(this, x) result(y) class(Tabulated1D), intent(in) :: this real(8), intent(in) :: x ! x value to find y at diff --git a/src/energy_distribution.F90 b/src/energy_distribution.F90 index 3a42bb73d..54ae3f90c 100644 --- a/src/energy_distribution.F90 +++ b/src/energy_distribution.F90 @@ -1,7 +1,9 @@ module energy_distribution - use constants, only: ZERO, ONE, TWO, PI, HISTOGRAM, LINEAR_LINEAR + use constants, only: ZERO, ONE, HALF, TWO, PI, HISTOGRAM, LINEAR_LINEAR use endf_header, only: Tabulated1D + use hdf5_interface + use hdf5 use math, only: maxwell_spectrum, watt_spectrum use random_lcg, only: prn use search, only: binary_search @@ -16,6 +18,7 @@ module energy_distribution type, abstract :: EnergyDistribution contains procedure(energy_distribution_sample_), deferred :: sample + procedure(energy_distribution_from_hdf5_), deferred :: from_hdf5 end type EnergyDistribution abstract interface @@ -25,6 +28,13 @@ module energy_distribution real(8), intent(in) :: E_in real(8) :: E_out end function energy_distribution_sample_ + + subroutine energy_distribution_from_hdf5_(this, group_id) + import EnergyDistribution + import HID_T + class(EnergyDistribution), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + end subroutine energy_distribution_from_hdf5_ end interface type :: EnergyDistributionContainer @@ -50,8 +60,23 @@ module energy_distribution ! each incoming energy contains procedure :: sample => equiprobable_sample + procedure :: from_hdf5 => equiprobable_from_hdf5 end type TabularEquiprobable +!=============================================================================== +! DISCRETEPHOTON gives the energy distribution for a discrete photon (usually +! used for photon production from an incident-neutron reaction) +!=============================================================================== + + type, extends(EnergyDistribution) :: DiscretePhoton + integer :: primary_flag + real(8) :: energy + real(8) :: A + contains + procedure :: sample => discrete_photon_sample + procedure :: from_hdf5 => discrete_photon_from_hdf5 + end type DiscretePhoton + !=============================================================================== ! LEVELINELASTIC gives the energy distribution for level inelastic scattering by ! neutrons as in ENDF MT=51--90. @@ -62,6 +87,7 @@ module energy_distribution real(8) :: mass_ratio contains procedure :: sample => level_inelastic_sample + procedure :: from_hdf5 => level_inelastic_from_hdf5 end type LevelInelastic !=============================================================================== @@ -86,6 +112,7 @@ module energy_distribution type(CTTable), allocatable :: distribution(:) contains procedure :: sample => continuous_sample + procedure :: from_hdf5 => continuous_from_hdf5 end type ContinuousTabular !=============================================================================== @@ -98,6 +125,7 @@ module energy_distribution real(8) :: u ! restriction energy contains procedure :: sample => maxwellenergy_sample + procedure :: from_hdf5 => maxwellenergy_from_hdf5 end type MaxwellEnergy !=============================================================================== @@ -110,6 +138,7 @@ module energy_distribution real(8) :: u contains procedure :: sample => evaporation_sample + procedure :: from_hdf5 => evaporation_from_hdf5 end type Evaporation !=============================================================================== @@ -123,6 +152,7 @@ module energy_distribution real(8) :: u contains procedure :: sample => watt_sample + procedure :: from_hdf5 => watt_from_hdf5 end type WattEnergy contains @@ -186,6 +216,31 @@ contains end if end function equiprobable_sample + subroutine equiprobable_from_hdf5(this, group_id) + class(TabularEquiprobable), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + end subroutine equiprobable_from_hdf5 + + function discrete_photon_sample(this, E_in) result(E_out) + class(DiscretePhoton), intent(in) :: this + real(8), intent(in) :: E_in + real(8) :: E_out + + if (this % primary_flag == 2) then + E_out = this % energy + this % A/(this % A + 1)*E_in + else + E_out = this % energy + end if + end function discrete_photon_sample + + subroutine discrete_photon_from_hdf5(this, group_id) + class(DiscretePhoton), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + + call read_attribute(this % primary_flag, group_id, 'primary_flag') + call read_attribute(this % energy, group_id, 'energy') + call read_attribute(this % A, group_id, 'atomic_weight_ratio') + end subroutine discrete_photon_from_hdf5 function level_inelastic_sample(this, E_in) result(E_out) class(LevelInelastic), intent(in) :: this @@ -195,6 +250,13 @@ contains E_out = this%mass_ratio*(E_in - this%threshold) end function level_inelastic_sample + subroutine level_inelastic_from_hdf5(this, group_id) + class(LevelInelastic), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + + call read_attribute(this%threshold, group_id, 'threshold') + call read_attribute(this%mass_ratio, group_id, 'mass_ratio') + end subroutine level_inelastic_from_hdf5 function continuous_sample(this, E_in) result(E_out) class(ContinuousTabular), intent(in) :: this @@ -307,6 +369,111 @@ contains end if end function continuous_sample + subroutine continuous_from_hdf5(this, group_id) + class(ContinuousTabular), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + + integer :: i, j, k + integer :: n + integer :: n_energy + integer(HID_T) :: dset_id + integer(HSIZE_T) :: dims(1), dims2(2) + integer, allocatable :: temp(:,:) + integer, allocatable :: offsets(:) + integer, allocatable :: interp(:) + integer, allocatable :: n_discrete(:) + real(8), allocatable :: eout(:,:) + + ! Open incoming energy dataset + dset_id = open_dataset(group_id, 'energy') + + ! Get interpolation parameters + call read_attribute(temp, dset_id, 'interpolation') + allocate(this%breakpoints(size(temp, 1))) + allocate(this%interpolation(size(temp, 1))) + this%breakpoints(:) = temp(:, 1) + this%interpolation(:) = temp(:, 2) + this%n_region = size(this%breakpoints) + + ! Get incoming energies + call get_shape(dset_id, dims) + n_energy = int(dims(1), 4) + allocate(this%energy(n_energy)) + allocate(this%distribution(n_energy)) + call read_dataset(this%energy, dset_id) + call close_dataset(dset_id) + + ! Get outgoing energy distribution data + dset_id = open_dataset(group_id, 'distribution') + call read_attribute(offsets, dset_id, 'offsets') + call read_attribute(interp, dset_id, 'interpolation') + call read_attribute(n_discrete, dset_id, 'n_discrete_lines') + call get_shape(dset_id, dims2) + allocate(eout(dims2(1), dims2(2))) + call read_dataset(eout, dset_id) + call close_dataset(dset_id) + + do i = 1, n_energy + ! Determine number of outgoing energies + j = offsets(i) + if (i < n_energy) then + n = offsets(i+1) - j + else + n = size(eout, 1) - j + end if + + associate (d => this % distribution(i)) + ! Assign interpolation scheme and number of discrete lines + d % interpolation = interp(i) + d % n_discrete = n_discrete(i) + + ! Allocate arrays for energies and PDF/CDF + allocate(d % e_out(n)) + allocate(d % p(n)) + allocate(d % c(n)) + + ! Copy data + d % e_out(:) = eout(j+1:j+n, 1) + d % p(:) = eout(j+1:j+n, 2) + + ! To get answers that match ACE data, for now we still use the tabulated + ! CDF values that were passed through to the HDF5 library. At a later + ! time, we can remove the CDF values from the HDF5 library and + ! reconstruct them using the PDF + if (.true.) then + d % c(:) = eout(j+1:j+n, 3) + else + ! Calculate cumulative distribution function -- discrete portion + do k = 1, n_discrete(i) + if (k == 1) then + d % c(k) = d % p(k) + else + d % c(k) = d % c(k-1) + d % p(k) + end if + end do + + ! Continuous portion + do k = d % n_discrete + 1, n + if (k == d % n_discrete + 1) then + d % c(k) = sum(d % p(1:d % n_discrete)) + else + if (d % interpolation == HISTOGRAM) then + d % c(k) = d % c(k-1) + d % p(k-1) * & + (d % e_out(k) - d % e_out(k-1)) + elseif (d % interpolation == LINEAR_LINEAR) then + d % c(k) = d % c(k-1) + HALF*(d % p(k-1) + d % p(k)) * & + (d % e_out(k) - d % e_out(k-1)) + end if + end if + end do + + ! Normalize density and distribution functions + d % p(:) = d % p(:)/d % c(n) + d % c(:) = d % c(:)/d % c(n) + end if + end associate + end do + end subroutine continuous_from_hdf5 function maxwellenergy_sample(this, E_in) result(E_out) class(MaxwellEnergy), intent(in) :: this @@ -327,6 +494,18 @@ contains end do end function maxwellenergy_sample + subroutine maxwellenergy_from_hdf5(this, group_id) + class(MaxwellEnergy), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + + integer(HID_T) :: dset_id + + call read_attribute(this%u, group_id, 'u') + dset_id = open_dataset(group_id, 'theta') + call this%theta%from_hdf5(dset_id) + call close_dataset(dset_id) + end subroutine maxwellenergy_from_hdf5 + function evaporation_sample(this, E_in) result(E_out) class(Evaporation), intent(in) :: this real(8), intent(in) :: E_in ! incoming energy @@ -351,6 +530,18 @@ contains E_out = x*theta end function evaporation_sample + subroutine evaporation_from_hdf5(this, group_id) + class(Evaporation), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + + integer(HID_T) :: dset_id + + call read_attribute(this%u, group_id, 'u') + dset_id = open_dataset(group_id, 'theta') + call this%theta%from_hdf5(dset_id) + call close_dataset(dset_id) + end subroutine evaporation_from_hdf5 + function watt_sample(this, E_in) result(E_out) class(WattEnergy), intent(in) :: this real(8), intent(in) :: E_in ! incoming energy @@ -373,4 +564,21 @@ contains end do end function watt_sample + subroutine watt_from_hdf5(this, group_id) + class(WattEnergy), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + + integer(HID_T) :: dset_id + + call read_attribute(this%u, group_id, 'u') + + dset_id = open_dataset(group_id, 'a') + call this%a%from_hdf5(dset_id) + call close_dataset(dset_id) + + dset_id = open_dataset(group_id, 'b') + call this%b%from_hdf5(dset_id) + call close_dataset(dset_id) + end subroutine watt_from_hdf5 + end module energy_distribution diff --git a/src/global.F90 b/src/global.F90 index 357887199..1b1e5632a 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -65,11 +65,7 @@ module global ! ============================================================================ ! CROSS SECTION RELATED VARIABLES NEEDED REGARDLESS OF CE OR MG - ! Cross section arrays - type(XsListing), allocatable, target :: xs_listings(:) ! cross_sections.xml listings - integer :: n_nuclides_total ! Number of nuclide cross section tables - integer :: n_listings ! Number of listings in cross_sections.xml ! Cross section caches type(NuclideMicroXS), allocatable :: micro_xs(:) ! Cache for each nuclide @@ -77,7 +73,6 @@ module global ! Dictionaries to look up cross sections and listings type(DictCharInt) :: nuclide_dict - type(DictCharInt) :: xs_listing_dict ! Default xs identifier (e.g. 70c or 300K) character(5):: default_xs @@ -107,7 +102,8 @@ module global ! Whether or not windowed multipole cross sections should be used. logical :: multipole_active = .false. - ! Total amount of nuclide ZAID and dictionary of nuclide ZAID and index + ! Total amount of nuclide ZAID and dictionary of nuclide ZAID and index -- + ! this is used when sampling unresolved resonance probability tables integer(8) :: n_nuc_zaid_total type(DictIntInt) :: nuc_zaid_dict @@ -498,7 +494,6 @@ contains end if if (allocated(sab_tables)) deallocate(sab_tables) - if (allocated(xs_listings)) deallocate(xs_listings) if (allocated(micro_xs)) deallocate(micro_xs) ! Deallocate external source @@ -553,7 +548,6 @@ contains call plot_dict % clear() call nuclide_dict % clear() call sab_dict % clear() - call xs_listing_dict % clear() ! Clear statepoint and sourcepoint batch set call statepoint_batch % clear() diff --git a/src/initialize.F90 b/src/initialize.F90 index 48b3ee61a..37eeb1f3e 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -1,6 +1,5 @@ module initialize - use ace, only: read_ace_xs use bank_header, only: Bank use constants use dict_header, only: DictIntInt, ElemKeyValueII @@ -109,19 +108,6 @@ contains end if if (run_mode /= MODE_PLOTTING) then - ! With the AWRs from the xs_listings, change all material specifications - ! so that they contain atom percents summing to 1 - call normalize_ao() - - ! Read ACE-format cross sections - call time_read_xs%start() - if (run_CE) then - call read_ace_xs() - else - call read_mgxs() - end if - call time_read_xs%stop() - ! Construct information needed for nuclear data if (run_CE) then ! Set undefined cell temperatures to match the material data. @@ -140,7 +126,10 @@ contains end select else ! Create material macroscopic data for MGXS + call time_read_xs%start() + call read_mgxs() call create_macro_xs() + call time_read_xs%stop() end if ! Allocate and setup tally stride, matching_bins, and tally maps @@ -800,71 +789,6 @@ contains end subroutine adjust_indices -!=============================================================================== -! NORMALIZE_AO normalizes the atom or weight percentages for each material -!=============================================================================== - - subroutine normalize_ao() - - integer :: index_list ! index in xs_listings array - integer :: i ! index in materials array - integer :: j ! index over nuclides in material - real(8) :: sum_percent ! summation - real(8) :: awr ! atomic weight ratio - real(8) :: x ! atom percent - logical :: percent_in_atom ! nuclides specified in atom percent? - logical :: density_in_atom ! density specified in atom/b-cm? - type(Material), pointer :: mat => null() - - ! first find the index in the xs_listings array for each nuclide in each - ! material - do i = 1, n_materials - mat => materials(i) - - percent_in_atom = (mat%atom_density(1) > ZERO) - density_in_atom = (mat%density > ZERO) - - sum_percent = ZERO - do j = 1, mat%n_nuclides - ! determine atomic weight ratio - index_list = xs_listing_dict%get_key(mat%names(j)) - awr = xs_listings(index_list)%awr - - ! if given weight percent, convert all values so that they are divided - ! by awr. thus, when a sum is done over the values, it's actually - ! sum(w/awr) - if (.not. percent_in_atom) then - mat%atom_density(j) = -mat%atom_density(j) / awr - end if - end do - - ! determine normalized atom percents. if given atom percents, this is - ! straightforward. if given weight percents, the value is w/awr and is - ! divided by sum(w/awr) - sum_percent = sum(mat%atom_density) - mat%atom_density = mat%atom_density / sum_percent - - ! Change density in g/cm^3 to atom/b-cm. Since all values are now in atom - ! percent, the sum needs to be re-evaluated as 1/sum(x*awr) - if (.not. density_in_atom) then - sum_percent = ZERO - do j = 1, mat%n_nuclides - index_list = xs_listing_dict%get_key(mat%names(j)) - awr = xs_listings(index_list)%awr - x = mat%atom_density(j) - sum_percent = sum_percent + x*awr - end do - sum_percent = ONE / sum_percent - mat%density = -mat%density * N_AVOGADRO & - / MASS_NEUTRON * sum_percent - end if - - ! Calculate nuclide atom densities - mat%atom_density = mat%density * mat%atom_density - end do - - end subroutine normalize_ao - !=============================================================================== ! CALCULATE_WORK determines how many particles each processor should simulate !=============================================================================== diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 3c6143e5a..0fc96ef4b 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -12,17 +12,22 @@ module input_xml use global use list_header, only: ListChar, ListInt, ListReal use mesh_header, only: RegularMesh + use multipole, only: multipole_read use output, only: write_message use plot_header use random_lcg, only: prn, seed use surface_header - use stl_vector, only: VectorInt - use string, only: str_to_int, str_to_real, tokenize, & - to_lower, to_str, starts_with, ends_with + use set_header, only: SetChar + use stl_vector, only: VectorInt, VectorReal, VectorChar + use string, only: to_lower, to_str, str_to_int, str_to_real, & + starts_with, ends_with, tokenize, split_string use tally_header, only: TallyObject, TallyFilter use tally_initialize, only: add_tallies use xml_interface + use hdf5 + use hdf5_interface + implicit none save @@ -39,15 +44,8 @@ contains subroutine read_input_xml() call read_settings_xml() - if (run_mode /= MODE_PLOTTING) then - if (run_CE) then - call read_ce_cross_sections_xml() - else - call read_mg_cross_sections_xml() - end if - end if call read_geometry_xml() - call read_materials_xml() + call read_materials() call read_tallies_xml() if (cmfd_run) call configure_cmfd() @@ -127,46 +125,42 @@ contains ! Find cross_sections.xml file -- the first place to look is the ! settings.xml file. If no file is found there, then we check the ! CROSS_SECTIONS environment variable - if (run_mode /= MODE_PLOTTING) then - if (.not. check_for_node(doc, "cross_sections") .and. & - run_mode /= MODE_PLOTTING) then - ! No cross_sections.xml file specified in settings.xml, check - ! environment variable - if (run_CE) then - call get_environment_variable("OPENMC_CROSS_SECTIONS", env_variable) + if (.not. check_for_node(doc, "cross_sections")) then + ! No cross_sections.xml file specified in settings.xml, check + ! environment variable + if (run_CE) then + call get_environment_variable("OPENMC_CROSS_SECTIONS", env_variable) + if (len_trim(env_variable) == 0) then + call get_environment_variable("CROSS_SECTIONS", env_variable) if (len_trim(env_variable) == 0) then - call get_environment_variable("CROSS_SECTIONS", env_variable) - if (len_trim(env_variable) == 0) then - call fatal_error("No cross_sections.xml file was specified in & - &settings.xml or in the OPENMC_CROSS_SECTIONS environment & - &variable. OpenMC needs such a file to identify where to & - &find ACE cross section libraries. Please consult the & - &user's guide at http://mit-crpg.github.io/openmc for & - &information on how to set up ACE cross section libraries.") - else - call warning("The CROSS_SECTIONS environment variable is & - &deprecated. Please update your environment to use & - &OPENMC_CROSS_SECTIONS instead.") - end if - end if - path_cross_sections = trim(env_variable) - else - call get_environment_variable("OPENMC_MG_CROSS_SECTIONS", & - env_variable) - if (len_trim(env_variable) == 0) then - call fatal_error("No mgxs.xml file was specified in & - &settings.xml or in the OPENMC_MG_CROSS_SECTIONS environment & + call fatal_error("No cross_sections.xml file was specified in & + &settings.xml or in the OPENMC_CROSS_SECTIONS environment & &variable. OpenMC needs such a file to identify where to & - &find the cross section libraries. Please consult the user's & - &guide at http://mit-crpg.github.io/openmc for information on & - &how to set up the cross section libraries.") + &find ACE cross section libraries. Please consult the & + &user's guide at http://mit-crpg.github.io/openmc for & + &information on how to set up ACE cross section libraries.") else - path_cross_sections = trim(env_variable) + call warning("The CROSS_SECTIONS environment variable is & + &deprecated. Please update your environment to use & + &OPENMC_CROSS_SECTIONS instead.") end if end if + path_cross_sections = trim(env_variable) else - call get_node_value(doc, "cross_sections", path_cross_sections) + call get_environment_variable("OPENMC_MG_CROSS_SECTIONS", env_variable) + if (len_trim(env_variable) == 0) then + call fatal_error("No mgxs.xml file was specified in & + &settings.xml or in the OPENMC_MG_CROSS_SECTIONS environment & + &variable. OpenMC needs such a file to identify where to & + &find ACE cross section libraries. Please consult the user's & + &guide at http://mit-crpg.github.io/openmc for information on & + &how to set up ACE cross section libraries.") + else + path_cross_sections = trim(env_variable) + end if end if + else + call get_node_value(doc, "cross_sections", path_cross_sections) end if ! Find the windowed multipole library @@ -2060,7 +2054,55 @@ contains ! for errors and placing properly-formatted data in the right data structures !=============================================================================== - subroutine read_materials_xml() + subroutine read_materials() + integer :: i, j + type(DictCharInt) :: library_dict + type(Library), allocatable :: libraries(:) + + if (run_CE) then + call read_ce_cross_sections_xml(libraries) + else + call read_mg_cross_sections_xml(libraries) + end if + + ! Creating dictionary that maps the name of the material to the entry + do i = 1, size(libraries) + do j = 1, size(libraries(i) % materials) + call library_dict % add_key(to_lower(libraries(i) % materials(j)), i) + end do + end do + + ! Check that 0K nuclides are listed in the cross_sections.xml file + if (allocated(nuclides_0K)) then + do i = 1, size(nuclides_0K) + if (.not. library_dict % has_key(to_lower(nuclides_0K(i) % name_0K))) then + call fatal_error("Could not find resonant scatterer " & + // trim(nuclides_0K(i) % name_0K) & + // " in cross_sections.xml file!") + end if + end do + end if + + ! Parse data from materials.xml + call read_materials_xml(libraries, library_dict) + + ! Read continuous-energy cross sections + if (run_CE) then + call time_read_xs%start() + call read_ce_cross_sections(libraries, library_dict) + call time_read_xs%stop() + end if + + ! Normalize atom/weight percents + call normalize_ao() + + ! Clear dictionary + call library_dict % clear() + end subroutine read_materials + + subroutine read_materials_xml(libraries, library_dict) + type(Library), intent(in) :: libraries(:) + type(DictCharInt), intent(inout) :: library_dict integer :: i ! loop index for materials integer :: j ! loop index for nuclides @@ -2068,23 +2110,20 @@ contains integer :: n ! number of nuclides integer :: n_sab ! number of sab tables for a material integer :: n_nuc_ele ! number of nuclides in an element - integer :: index_list ! index in xs_listings array + integer :: i_library ! index in libraries array integer :: index_nuclide ! index in nuclides - integer :: index_nuc_zaid ! index in nuclide ZAID integer :: index_sab ! index in sab_tables real(8) :: val ! value entered for density real(8) :: temp_dble ! temporary double prec. real logical :: file_exists ! does materials.xml exist? logical :: sum_density ! density is taken to be sum of nuclide densities - integer :: zaid ! ZAID of nuclide - character(12) :: name ! name of isotope, e.g. 92235.03c - character(12) :: alias ! alias of nuclide, e.g. U-235.03c + character(20) :: name ! name of isotope, e.g. 92235.03c character(MAX_WORD_LEN) :: units ! units on density character(MAX_LINE_LEN) :: filename ! absolute path to materials.xml character(MAX_LINE_LEN) :: temp_str ! temporary string when reading - type(ListChar) :: list_names ! temporary list of nuclide names - type(ListReal) :: list_density ! temporary list of nuclide densities - type(ListInt) :: list_iso_lab ! temporary list of isotropic lab scatterers + type(VectorChar) :: names ! temporary list of nuclide names + type(VectorReal) :: densities ! temporary list of nuclide densities + type(VectorInt) :: list_iso_lab ! temporary list of isotropic lab scatterers type(Material), pointer :: mat => null() type(Node), pointer :: doc => null() type(Node), pointer :: node_mat => null() @@ -2128,7 +2167,6 @@ contains ! Initialize count for number of nuclides/S(a,b) tables index_nuclide = 0 - index_nuc_zaid = 0 index_sab = 0 do i = 1, n_materials @@ -2153,13 +2191,6 @@ contains ! Copy material name if (check_for_node(node_mat, "name")) then call get_node_value(node_mat, "name", mat % name) - mat % name = to_lower(mat % name) - end if - - if (run_mode == MODE_PLOTTING) then - ! add to the dictionary and skip xs processing - call material_dict % add_key(mat % id, i) - cycle end if ! ======================================================================= @@ -2277,12 +2308,12 @@ contains name = to_lower(name) ! save name and density to list - call list_names % append(name) + call names % push_back(name) ! Check if no atom/weight percents were specified or if both atom and ! weight percents were specified if (units == 'macro') then - call list_density % append(ONE) + call densities % push_back(ONE) else call fatal_error("Units can only be macro for macroscopic data " & // trim(name)) @@ -2318,15 +2349,15 @@ contains if (check_for_node(node_nuc, "scattering")) then call get_node_value(node_nuc, "scattering", temp_str) if (adjustl(to_lower(temp_str)) == "iso-in-lab") then - call list_iso_lab % append(1) + call list_iso_lab % push_back(1) else if (adjustl(to_lower(temp_str)) == "data") then - call list_iso_lab % append(0) + call list_iso_lab % push_back(0) else call fatal_error("Scattering must be isotropic in lab or follow& & the ACE file data") end if else - call list_iso_lab % append(0) + call list_iso_lab % push_back(0) end if end if @@ -2335,15 +2366,14 @@ contains if (check_for_node(node_nuc, "xs")) & call get_node_value(node_nuc, "xs", name) name = trim(temp_str) // "." // trim(name) - name = to_lower(name) ! save name and density to list - call list_names % append(name) + call names % push_back(name) ! Check if no atom/weight percents were specified or if both atom and ! weight percents were specified if (units == 'macro') then - call list_density % append(ONE) + call densities % push_back(ONE) else if (.not. check_for_node(node_nuc, "ao") .and. & .not. check_for_node(node_nuc, "wo")) then @@ -2358,10 +2388,10 @@ contains ! Copy atom/weight percents if (check_for_node(node_nuc, "ao")) then call get_node_value(node_nuc, "ao", temp_dble) - call list_density % append(temp_dble) + call densities % push_back(temp_dble) else call get_node_value(node_nuc, "wo", temp_dble) - call list_density % append(-temp_dble) + call densities % push_back(-temp_dble) end if end if end do INDIVIDUAL_NUCLIDES @@ -2408,20 +2438,20 @@ contains end if ! Get current number of nuclides - n_nuc_ele = list_names % size() + n_nuc_ele = names % size() ! Expand element into naturally-occurring isotopes if (check_for_node(node_ele, "ao")) then call get_node_value(node_ele, "ao", temp_dble) - call expand_natural_element(name, temp_str, temp_dble, & - list_names, list_density) + call expand_natural_element(name, temp_str, temp_dble, names, & + densities) else call fatal_error("The ability to expand a natural element based on & &weight percentage is not yet supported.") end if ! Compute number of new nuclides from the natural element expansion - n_nuc_ele = list_names % size() - n_nuc_ele + n_nuc_ele = names % size() - n_nuc_ele ! Check enforced isotropic lab scattering if (run_CE) then @@ -2434,9 +2464,9 @@ contains ! Set ace or iso-in-lab scattering for each nuclide in element do k = 1, n_nuc_ele if (adjustl(to_lower(temp_str)) == "iso-in-lab") then - call list_iso_lab % append(1) + call list_iso_lab % push_back(1) else if (adjustl(to_lower(temp_str)) == "data") then - call list_iso_lab % append(0) + call list_iso_lab % push_back(0) else call fatal_error("Scattering must be isotropic in lab or follow& & the ACE file data") @@ -2450,7 +2480,7 @@ contains ! COPY NUCLIDES TO ARRAYS IN MATERIAL ! allocate arrays in Material object - n = list_names % size() + n = names % size() mat % n_nuclides = n allocate(mat % names(n)) allocate(mat % nuclide(n)) @@ -2459,27 +2489,21 @@ contains ALL_NUCLIDES: do j = 1, mat % n_nuclides ! Check that this nuclide is listed in the cross_sections.xml file - name = trim(list_names % get_item(j)) - if (.not. xs_listing_dict % has_key(to_lower(name))) then + name = trim(names % data(j)) + if (.not. library_dict % has_key(to_lower(name))) then call fatal_error("Could not find nuclide " // trim(name) & // " in cross_sections data file!") end if + i_library = library_dict % get_key(to_lower(name)) if (run_CE) then ! Check to make sure cross-section is continuous energy neutron table - n = len_trim(name) - if (name(n:n) /= 'c') then + if (libraries(i_library) % type /= LIBRARY_NEUTRON) then call fatal_error("Cross-section table " // trim(name) & // " is not a continuous-energy neutron table.") end if end if - ! Find xs_listing and set the name/alias according to the listing - index_list = xs_listing_dict % get_key(to_lower(name)) - name = xs_listings(index_list) % name - alias = xs_listings(index_list) % alias - zaid = xs_listings(index_list) % zaid - ! If this nuclide hasn't been encountered yet, we need to add its name ! and alias to the nuclide_dict if (.not. nuclide_dict % has_key(to_lower(name))) then @@ -2487,26 +2511,21 @@ contains mat % nuclide(j) = index_nuclide call nuclide_dict % add_key(to_lower(name), index_nuclide) - call nuclide_dict % add_key(to_lower(alias), index_nuclide) else mat % nuclide(j) = nuclide_dict % get_key(to_lower(name)) end if - ! Construct dict of nuclide zaid - if (.not. nuc_zaid_dict % has_key(zaid)) then - index_nuc_zaid = index_nuc_zaid + 1 - call nuc_zaid_dict % add_key(zaid, index_nuc_zaid) - end if - ! Copy name and atom/weight percent mat % names(j) = name - mat % atom_density(j) = list_density % get_item(j) + mat % atom_density(j) = densities % data(j) ! Cast integer isotropic lab scattering flag to boolean - if (list_iso_lab % get_item(j) == 1) then - mat % p0(j) = .true. - else - mat % p0(j) = .false. + if (run_CE) then + if (list_iso_lab % data(j) == 1) then + mat % p0(j) = .true. + else + mat % p0(j) = .false. + end if end if end do ALL_NUCLIDES @@ -2523,8 +2542,8 @@ contains if (sum_density) mat % density = sum(mat % atom_density) ! Clear lists - call list_names % clear() - call list_density % clear() + call names % clear() + call densities % clear() call list_iso_lab % clear() ! ======================================================================= @@ -2562,15 +2581,22 @@ contains mat % sab_names(j) = name ! Check that this nuclide is listed in the cross_sections.xml file - if (.not. xs_listing_dict % has_key(to_lower(name))) then + if (.not. library_dict % has_key(to_lower(name))) then call fatal_error("Could not find S(a,b) table " // trim(name) & // " in cross_sections.xml file!") end if ! Find index in xs_listing and set the name and alias according to the ! listing - index_list = xs_listing_dict % get_key(to_lower(name)) - name = xs_listings(index_list) % name + i_library = library_dict % get_key(to_lower(name)) + + if (run_CE) then + ! Check to make sure cross-section is continuous energy neutron table + if (libraries(i_library) % type /= LIBRARY_THERMAL) then + call fatal_error("Cross-section table " // trim(name) & + // " is not a S(a,b) table.") + end if + end if ! If this S(a,b) table hasn't been encountered yet, we need to add its ! name and alias to the sab_dict @@ -2592,7 +2618,6 @@ contains ! Set total number of nuclides and S(a,b) tables n_nuclides_total = index_nuclide n_sab_tables = index_sab - n_nuc_zaid_total = index_nuc_zaid ! Close materials XML file call close_xmldoc(doc) @@ -3270,10 +3295,10 @@ contains ! If a specific nuclide was specified word = to_lower(sarray(j)) - ! Append default_xs specifier to nuclide if needed - if ((default_xs /= '') .and. (.not. ends_with(sarray(j), 'c'))) then - word = trim(word) // "." // trim(default_xs) - end if +!!$ ! Append default_xs specifier to nuclide if needed +!!$ if ((default_xs /= '') .and. (.not. ends_with(sarray(j), 'c'))) then +!!$ word = trim(word) // "." // trim(default_xs) +!!$ end if ! Search through nuclides pair_list => nuclide_dict % keys() @@ -4516,19 +4541,19 @@ contains ! file contains a listing of the CE and MG cross sections that may be used. !=============================================================================== - subroutine read_ce_cross_sections_xml() + subroutine read_ce_cross_sections_xml(libraries) + type(Library), allocatable, intent(out) :: libraries(:) integer :: i ! loop index - integer :: filetype ! default file type - integer :: recl ! default record length - integer :: entries ! default number of entries + integer :: n + integer :: n_libraries logical :: file_exists ! does cross_sections.xml exist? - character(MAX_WORD_LEN) :: directory ! directory with cross sections - character(MAX_LINE_LEN) :: temp_str - type(XsListing), pointer :: listing => null() - type(Node), pointer :: doc => null() - type(Node), pointer :: node_ace => null() - type(NodeList), pointer :: node_ace_list => null() + character(MAX_WORD_LEN) :: directory ! directory with cross sections + character(MAX_WORD_LEN) :: words(MAX_WORDS) + character(10000) :: temp_str + type(Node), pointer :: doc + type(Node), pointer :: node_library + type(NodeList), pointer :: node_library_list ! Check if cross_sections.xml exists inquire(FILE=path_cross_sections, EXIST=file_exists) @@ -4553,118 +4578,64 @@ contains directory = path_cross_sections(1:i) end if - ! determine whether binary/ascii - temp_str = '' - if (check_for_node(doc, "filetype")) & - call get_node_value(doc, "filetype", temp_str) - if (trim(temp_str) == 'ascii') then - filetype = ASCII - elseif (trim(temp_str) == 'binary') then - filetype = BINARY - elseif (len_trim(temp_str) == 0) then - filetype = ASCII - else - call fatal_error("Unknown filetype in cross_sections.xml: " & - // trim(temp_str)) - end if - - ! copy default record length and entries for binary files - if (filetype == BINARY) then - call get_node_value(doc, "record_length", recl) - call get_node_value(doc, "entries", entries) - end if - - ! Get node list of all - call get_node_list(doc, "ace_table", node_ace_list) - n_listings = get_list_size(node_ace_list) + ! Get node list of all + call get_node_list(doc, "library", node_library_list) + n_libraries = get_list_size(node_library_list) ! Allocate xs_listings array - if (n_listings == 0) then - call fatal_error("No ACE table listings present in cross_sections.xml & + if (n_libraries == 0) then + call fatal_error("No cross section libraries present in cross_sections.xml & &file!") else - allocate(xs_listings(n_listings)) + allocate(libraries(n_libraries)) end if - do i = 1, n_listings - listing => xs_listings(i) - + do i = 1, n_libraries ! Get pointer to ace table XML node - call get_list_item(node_ace_list, i, node_ace) + call get_list_item(node_library_list, i, node_library) - ! copy a number of attributes - call get_node_value(node_ace, "name", listing % name) - if (check_for_node(node_ace, "alias")) & - call get_node_value(node_ace, "alias", listing % alias) - call get_node_value(node_ace, "zaid", listing % zaid) - call get_node_value(node_ace, "awr", listing % awr) - if (check_for_node(node_ace, "temperature")) & - call get_node_value(node_ace, "temperature", listing % kT) - call get_node_value(node_ace, "location", listing % location) - - ! determine type of cross section - if (ends_with(listing % name, 'c')) then - listing % type = ACE_NEUTRON - elseif (ends_with(listing % name, 't')) then - listing % type = ACE_THERMAL + ! Get list of materials + if (check_for_node(node_library, "materials")) then + call get_node_value(node_library, "materials", temp_str) + call split_string(temp_str, words, n) + allocate(libraries(i) % materials(n)) + libraries(i) % materials(:) = words(1:n) end if - ! set filetype, record length, and number of entries - if (check_for_node(node_ace, "filetype")) then - temp_str = '' - call get_node_value(node_ace, "filetype", temp_str) - if (temp_str == 'ascii') then - listing % filetype = ASCII - else if (temp_str == 'binary') then - listing % filetype = BINARY - end if + ! Get type of library + if (check_for_node(node_library, "type")) then + call get_node_value(node_library, "type", temp_str) + select case(to_lower(temp_str)) + case ('neutron') + libraries(i) % type = LIBRARY_NEUTRON + case ('thermal') + libraries(i) % type = LIBRARY_THERMAL + end select else - listing % filetype = filetype - end if - - ! Set record length and entries for binary files - if (filetype == BINARY) then - listing % recl = recl - listing % entries = entries - end if - - ! determine metastable state - if (.not. check_for_node(node_ace, "metastable")) then - listing % metastable = .false. - else - listing % metastable = .true. + call fatal_error("Missing library type") end if ! determine path of cross section table - if (check_for_node(node_ace, "path")) then - call get_node_value(node_ace, "path", temp_str) + if (check_for_node(node_library, "path")) then + call get_node_value(node_library, "path", temp_str) else - call fatal_error("Path missing for isotope " // listing % name) + call fatal_error("Missing library path") end if if (starts_with(temp_str, '/')) then - listing % path = trim(temp_str) + libraries(i) % path = trim(temp_str) else if (ends_with(directory,'/')) then - listing % path = trim(directory) // trim(temp_str) + libraries(i) % path = trim(directory) // trim(temp_str) else - listing % path = trim(directory) // '/' // trim(temp_str) + libraries(i) % path = trim(directory) // '/' // trim(temp_str) end if end if - ! create dictionary entry for both name and alias - call xs_listing_dict % add_key(to_lower(listing % name), i) - if (check_for_node(node_ace, "alias")) then - call xs_listing_dict % add_key(to_lower(listing % alias), i) - end if - end do - - ! Check that 0K nuclides are listed in the cross_sections.xml file - do i = 1, n_res_scatterers_total - if (.not. xs_listing_dict % has_key(trim(nuclides_0K(i) % name_0K))) then - call fatal_error("Could not find nuclide " & - // trim(nuclides_0K(i) % name_0K) & - // " in cross_sections.xml file!") + inquire(FILE=libraries(i) % path, EXIST=file_exists) + if (.not. file_exists) then + call warning("Cross section library " // trim(libraries(i) % path) // & + " does not exist.") end if end do @@ -4673,11 +4644,12 @@ contains end subroutine read_ce_cross_sections_xml - subroutine read_mg_cross_sections_xml() + subroutine read_mg_cross_sections_xml(libraries) + type(Library), allocatable, intent(out) :: libraries(:) integer :: i ! loop index - logical :: file_exists ! does mgxs.xml exist? - type(XsListing), pointer :: listing => null() + integer :: n_libraries + logical :: file_exists ! does cross_sections.xml exist? type(Node), pointer :: doc => null() type(Node), pointer :: node_xsdata => null() type(NodeList), pointer :: node_xsdata_list => null() @@ -4736,55 +4708,23 @@ contains ! Get node list of all call get_node_list(doc, "xsdata", node_xsdata_list) - n_listings = get_list_size(node_xsdata_list) + n_libraries = get_list_size(node_xsdata_list) ! Allocate xs_listings array - if (n_listings == 0) then + if (n_libraries == 0) then call fatal_error("At least one element must be present in & &mgxs.xml file!") else - allocate(xs_listings(n_listings)) + allocate(libraries(n_libraries)) end if - do i = 1, n_listings - listing => xs_listings(i) - + do i = 1, n_libraries ! Get pointer to xsdata table XML node call get_list_item(node_xsdata_list, i, node_xsdata) - ! copy a number of attributes - call get_node_value(node_xsdata, "name", listing % name) - listing % name = to_lower(listing % name) - listing % alias = listing % name - if (check_for_node(node_xsdata, "alias")) & - call get_node_value(node_xsdata, "alias", listing % alias) - listing % alias = to_lower(listing % alias) - if (check_for_node(node_xsdata, "zaid")) then - call get_node_value(node_xsdata, "zaid", listing % zaid) - else - listing % zaid = -1 - end if - if (check_for_node(node_xsdata, "awr")) then - call get_node_value(node_xsdata, "awr", listing % awr) - else - ! Set to a default of 1; this allows a macroscopic library to still - ! be used with materials with atom/b-cm units for testing purposes - listing % awr = ONE - end if - if (check_for_node(node_xsdata, "kT")) then - call get_node_value(node_xsdata, "kT", listing % kT) - else - listing % kT = 293.6_8 * K_BOLTZMANN - end if - - ! determine type of cross section - if (ends_with(listing % name, 'c')) then - listing % type = NEUTRON - end if - - ! create dictionary entry for both name and alias - call xs_listing_dict % add_key(to_lower(listing % name), i) - call xs_listing_dict % add_key(to_lower(listing % alias), i) + ! Get name of material + allocate(libraries(i) % materials(1)) + call get_node_value(node_xsdata, "name", libraries(i) % materials(1)) end do ! Close cross sections XML file @@ -4800,14 +4740,12 @@ contains ! evaluations of particular isotopes don't exist. !=============================================================================== - subroutine expand_natural_element(name, xs, density, list_names, & - list_density) - - character(*), intent(in) :: name - character(*), intent(in) :: xs - real(8), intent(in) :: density - type(ListChar), intent(inout) :: list_names - type(ListReal), intent(inout) :: list_density + subroutine expand_natural_element(name, xs, density, names, densities) + character(*), intent(in) :: name + character(*), intent(in) :: xs + real(8), intent(in) :: density + type(VectorChar), intent(inout) :: names + type(VectorReal), intent(inout) :: densities character(2) :: element_name @@ -4815,670 +4753,670 @@ contains select case (to_lower(element_name)) case ('h') - call list_names % append('1001.' // xs) - call list_density % append(density * 0.999885_8) - call list_names % append('1002.' // xs) - call list_density % append(density * 0.000115_8) + call names % push_back('H1.' // xs) + call densities % push_back(density * 0.999885_8) + call names % push_back('H2.' // xs) + call densities % push_back(density * 0.000115_8) case ('he') - call list_names % append('2003.' // xs) - call list_density % append(density * 0.00000134_8) - call list_names % append('2004.' // xs) - call list_density % append(density * 0.99999866_8) + call names % push_back('He3.' // xs) + call densities % push_back(density * 0.00000134_8) + call names % push_back('He4.' // xs) + call densities % push_back(density * 0.99999866_8) case ('li') - call list_names % append('3006.' // xs) - call list_density % append(density * 0.0759_8) - call list_names % append('3007.' // xs) - call list_density % append(density * 0.9241_8) + call names % push_back('Li6.' // xs) + call densities % push_back(density * 0.0759_8) + call names % push_back('Li7.' // xs) + call densities % push_back(density * 0.9241_8) case ('be') - call list_names % append('4009.' // xs) - call list_density % append(density) + call names % push_back('Be9.' // xs) + call densities % push_back(density) case ('b') - call list_names % append('5010.' // xs) - call list_density % append(density * 0.199_8) - call list_names % append('5011.' // xs) - call list_density % append(density * 0.801_8) + call names % push_back('B10.' // xs) + call densities % push_back(density * 0.199_8) + call names % push_back('B11.' // xs) + call densities % push_back(density * 0.801_8) case ('c') ! No evaluations split up Carbon into isotopes yet - call list_names % append('6000.' // xs) - call list_density % append(density) + call names % push_back('C0.' // xs) + call densities % push_back(density) case ('n') - call list_names % append('7014.' // xs) - call list_density % append(density * 0.99636_8) - call list_names % append('7015.' // xs) - call list_density % append(density * 0.00364_8) + call names % push_back('N14.' // xs) + call densities % push_back(density * 0.99636_8) + call names % push_back('N15.' // xs) + call densities % push_back(density * 0.00364_8) case ('o') if (default_expand == JEFF_32) then - call list_names % append('8016.' // xs) - call list_density % append(density * 0.99757_8) - call list_names % append('8017.' // xs) - call list_density % append(density * 0.00038_8) - call list_names % append('8018.' // xs) - call list_density % append(density * 0.00205_8) + call names % push_back('O16.' // xs) + call densities % push_back(density * 0.99757_8) + call names % push_back('O17.' // xs) + call densities % push_back(density * 0.00038_8) + call names % push_back('O18.' // xs) + call densities % push_back(density * 0.00205_8) elseif (default_expand >= JENDL_32 .and. default_expand <= JENDL_40) then - call list_names % append('8016.' // xs) - call list_density % append(density) + call names % push_back('O16.' // xs) + call densities % push_back(density) else - call list_names % append('8016.' // xs) - call list_density % append(density * 0.99962_8) - call list_names % append('8017.' // xs) - call list_density % append(density * 0.00038_8) + call names % push_back('O16.' // xs) + call densities % push_back(density * 0.99962_8) + call names % push_back('O17.' // xs) + call densities % push_back(density * 0.00038_8) end if case ('f') - call list_names % append('9019.' // xs) - call list_density % append(density) + call names % push_back('F19.' // xs) + call densities % push_back(density) case ('ne') - call list_names % append('10020.' // xs) - call list_density % append(density * 0.9048_8) - call list_names % append('10021.' // xs) - call list_density % append(density * 0.0027_8) - call list_names % append('10022.' // xs) - call list_density % append(density * 0.0925_8) + call names % push_back('Ne20.' // xs) + call densities % push_back(density * 0.9048_8) + call names % push_back('Ne21.' // xs) + call densities % push_back(density * 0.0027_8) + call names % push_back('Ne22.' // xs) + call densities % push_back(density * 0.0925_8) case ('na') - call list_names % append('11023.' // xs) - call list_density % append(density) + call names % push_back('Na23.' // xs) + call densities % push_back(density) case ('mg') - call list_names % append('12024.' // xs) - call list_density % append(density * 0.7899_8) - call list_names % append('12025.' // xs) - call list_density % append(density * 0.1000_8) - call list_names % append('12026.' // xs) - call list_density % append(density * 0.1101_8) + call names % push_back('Mg24.' // xs) + call densities % push_back(density * 0.7899_8) + call names % push_back('Mg25.' // xs) + call densities % push_back(density * 0.1000_8) + call names % push_back('Mg26.' // xs) + call densities % push_back(density * 0.1101_8) case ('al') - call list_names % append('13027.' // xs) - call list_density % append(density) + call names % push_back('Al27.' // xs) + call densities % push_back(density) case ('si') - call list_names % append('14028.' // xs) - call list_density % append(density * 0.92223_8) - call list_names % append('14029.' // xs) - call list_density % append(density * 0.04685_8) - call list_names % append('14030.' // xs) - call list_density % append(density * 0.03092_8) + call names % push_back('Si28.' // xs) + call densities % push_back(density * 0.92223_8) + call names % push_back('Si29.' // xs) + call densities % push_back(density * 0.04685_8) + call names % push_back('Si30.' // xs) + call densities % push_back(density * 0.03092_8) case ('p') - call list_names % append('15031.' // xs) - call list_density % append(density) + call names % push_back('P31.' // xs) + call densities % push_back(density) case ('s') - call list_names % append('16032.' // xs) - call list_density % append(density * 0.9499_8) - call list_names % append('16033.' // xs) - call list_density % append(density * 0.0075_8) - call list_names % append('16034.' // xs) - call list_density % append(density * 0.0425_8) - call list_names % append('16036.' // xs) - call list_density % append(density * 0.0001_8) + call names % push_back('S32.' // xs) + call densities % push_back(density * 0.9499_8) + call names % push_back('S33.' // xs) + call densities % push_back(density * 0.0075_8) + call names % push_back('S34.' // xs) + call densities % push_back(density * 0.0425_8) + call names % push_back('S36.' // xs) + call densities % push_back(density * 0.0001_8) case ('cl') - call list_names % append('17035.' // xs) - call list_density % append(density * 0.7576_8) - call list_names % append('17037.' // xs) - call list_density % append(density * 0.2424_8) + call names % push_back('Cl35.' // xs) + call densities % push_back(density * 0.7576_8) + call names % push_back('Cl37.' // xs) + call densities % push_back(density * 0.2424_8) case ('ar') - call list_names % append('18036.' // xs) - call list_density % append(density * 0.003336_8) - call list_names % append('18038.' // xs) - call list_density % append(density * 0.000629_8) - call list_names % append('18040.' // xs) - call list_density % append(density * 0.996035_8) + call names % push_back('Ar36.' // xs) + call densities % push_back(density * 0.003336_8) + call names % push_back('Ar38.' // xs) + call densities % push_back(density * 0.000629_8) + call names % push_back('Ar40.' // xs) + call densities % push_back(density * 0.996035_8) case ('k') - call list_names % append('19039.' // xs) - call list_density % append(density * 0.932581_8) - call list_names % append('19040.' // xs) - call list_density % append(density * 0.000117_8) - call list_names % append('19041.' // xs) - call list_density % append(density * 0.067302_8) + call names % push_back('K39.' // xs) + call densities % push_back(density * 0.932581_8) + call names % push_back('K40.' // xs) + call densities % push_back(density * 0.000117_8) + call names % push_back('K41.' // xs) + call densities % push_back(density * 0.067302_8) case ('ca') - call list_names % append('20040.' // xs) - call list_density % append(density * 0.96941_8) - call list_names % append('20042.' // xs) - call list_density % append(density * 0.00647_8) - call list_names % append('20043.' // xs) - call list_density % append(density * 0.00135_8) - call list_names % append('20044.' // xs) - call list_density % append(density * 0.02086_8) - call list_names % append('20046.' // xs) - call list_density % append(density * 0.00004_8) - call list_names % append('20048.' // xs) - call list_density % append(density * 0.00187_8) + call names % push_back('Ca40.' // xs) + call densities % push_back(density * 0.96941_8) + call names % push_back('Ca42.' // xs) + call densities % push_back(density * 0.00647_8) + call names % push_back('Ca43.' // xs) + call densities % push_back(density * 0.00135_8) + call names % push_back('Ca44.' // xs) + call densities % push_back(density * 0.02086_8) + call names % push_back('Ca46.' // xs) + call densities % push_back(density * 0.00004_8) + call names % push_back('Ca48.' // xs) + call densities % push_back(density * 0.00187_8) case ('sc') - call list_names % append('21045.' // xs) - call list_density % append(density) + call names % push_back('Sc45.' // xs) + call densities % push_back(density) case ('ti') - call list_names % append('22046.' // xs) - call list_density % append(density * 0.0825_8) - call list_names % append('22047.' // xs) - call list_density % append(density * 0.0744_8) - call list_names % append('22048.' // xs) - call list_density % append(density * 0.7372_8) - call list_names % append('22049.' // xs) - call list_density % append(density * 0.0541_8) - call list_names % append('22050.' // xs) - call list_density % append(density * 0.0518_8) + call names % push_back('Ti46.' // xs) + call densities % push_back(density * 0.0825_8) + call names % push_back('Ti47.' // xs) + call densities % push_back(density * 0.0744_8) + call names % push_back('Ti48.' // xs) + call densities % push_back(density * 0.7372_8) + call names % push_back('Ti49.' // xs) + call densities % push_back(density * 0.0541_8) + call names % push_back('Ti50.' // xs) + call densities % push_back(density * 0.0518_8) case ('v') if (default_expand == ENDF_BVII0 .or. default_expand == JEFF_311 & .or. default_expand == JEFF_32 .or. & (default_expand >= JENDL_32 .and. default_expand <= JENDL_33)) then - call list_names % append('23000.' // xs) - call list_density % append(density) + call names % push_back('V0.' // xs) + call densities % push_back(density) else - call list_names % append('23050.' // xs) - call list_density % append(density * 0.0025_8) - call list_names % append('23051.' // xs) - call list_density % append(density * 0.9975_8) + call names % push_back('V50.' // xs) + call densities % push_back(density * 0.0025_8) + call names % push_back('V51.' // xs) + call densities % push_back(density * 0.9975_8) end if case ('cr') - call list_names % append('24050.' // xs) - call list_density % append(density * 0.04345_8) - call list_names % append('24052.' // xs) - call list_density % append(density * 0.83789_8) - call list_names % append('24053.' // xs) - call list_density % append(density * 0.09501_8) - call list_names % append('24054.' // xs) - call list_density % append(density * 0.02365_8) + call names % push_back('Cr50.' // xs) + call densities % push_back(density * 0.04345_8) + call names % push_back('Cr52.' // xs) + call densities % push_back(density * 0.83789_8) + call names % push_back('Cr53.' // xs) + call densities % push_back(density * 0.09501_8) + call names % push_back('Cr54.' // xs) + call densities % push_back(density * 0.02365_8) case ('mn') - call list_names % append('25055.' // xs) - call list_density % append(density) + call names % push_back('Mn55.' // xs) + call densities % push_back(density) case ('fe') - call list_names % append('26054.' // xs) - call list_density % append(density * 0.05845_8) - call list_names % append('26056.' // xs) - call list_density % append(density * 0.91754_8) - call list_names % append('26057.' // xs) - call list_density % append(density * 0.02119_8) - call list_names % append('26058.' // xs) - call list_density % append(density * 0.00282_8) + call names % push_back('Fe54.' // xs) + call densities % push_back(density * 0.05845_8) + call names % push_back('Fe56.' // xs) + call densities % push_back(density * 0.91754_8) + call names % push_back('Fe57.' // xs) + call densities % push_back(density * 0.02119_8) + call names % push_back('Fe58.' // xs) + call densities % push_back(density * 0.00282_8) case ('co') - call list_names % append('27059.' // xs) - call list_density % append(density) + call names % push_back('Co59.' // xs) + call densities % push_back(density) case ('ni') - call list_names % append('28058.' // xs) - call list_density % append(density * 0.68077_8) - call list_names % append('28060.' // xs) - call list_density % append(density * 0.26223_8) - call list_names % append('28061.' // xs) - call list_density % append(density * 0.011399_8) - call list_names % append('28062.' // xs) - call list_density % append(density * 0.036346_8) - call list_names % append('28064.' // xs) - call list_density % append(density * 0.009255_8) + call names % push_back('Ni58.' // xs) + call densities % push_back(density * 0.68077_8) + call names % push_back('Ni60.' // xs) + call densities % push_back(density * 0.26223_8) + call names % push_back('Ni61.' // xs) + call densities % push_back(density * 0.011399_8) + call names % push_back('Ni62.' // xs) + call densities % push_back(density * 0.036346_8) + call names % push_back('Ni64.' // xs) + call densities % push_back(density * 0.009255_8) case ('cu') - call list_names % append('29063.' // xs) - call list_density % append(density * 0.6915_8) - call list_names % append('29065.' // xs) - call list_density % append(density * 0.3085_8) + call names % push_back('Cu63.' // xs) + call densities % push_back(density * 0.6915_8) + call names % push_back('Cu65.' // xs) + call densities % push_back(density * 0.3085_8) case ('zn') if (default_expand == ENDF_BVII0 .or. default_expand == & JEFF_311 .or. default_expand == JEFF_312) then - call list_names % append('30000.' // xs) - call list_density % append(density) + call names % push_back('Zn0.' // xs) + call densities % push_back(density) else - call list_names % append('30064.' // xs) - call list_density % append(density * 0.4917_8) - call list_names % append('30066.' // xs) - call list_density % append(density * 0.2773_8) - call list_names % append('30067.' // xs) - call list_density % append(density * 0.0404_8) - call list_names % append('30068.' // xs) - call list_density % append(density * 0.1845_8) - call list_names % append('30070.' // xs) - call list_density % append(density * 0.0061_8) + call names % push_back('Zn64.' // xs) + call densities % push_back(density * 0.4917_8) + call names % push_back('Zn66.' // xs) + call densities % push_back(density * 0.2773_8) + call names % push_back('Zn67.' // xs) + call densities % push_back(density * 0.0404_8) + call names % push_back('Zn68.' // xs) + call densities % push_back(density * 0.1845_8) + call names % push_back('Zn70.' // xs) + call densities % push_back(density * 0.0061_8) end if case ('ga') if (default_expand == JEFF_311 .or. default_expand == JEFF_312) then - call list_names % append('31000.' // xs) - call list_density % append(density) + call names % push_back('Ga0.' // xs) + call densities % push_back(density) else - call list_names % append('31069.' // xs) - call list_density % append(density * 0.60108_8) - call list_names % append('31071.' // xs) - call list_density % append(density * 0.39892_8) + call names % push_back('Ha69.' // xs) + call densities % push_back(density * 0.60108_8) + call names % push_back('Ga71.' // xs) + call densities % push_back(density * 0.39892_8) end if case ('ge') - call list_names % append('32070.' // xs) - call list_density % append(density * 0.2057_8) - call list_names % append('32072.' // xs) - call list_density % append(density * 0.2745_8) - call list_names % append('32073.' // xs) - call list_density % append(density * 0.0775_8) - call list_names % append('32074.' // xs) - call list_density % append(density * 0.3650_8) - call list_names % append('32076.' // xs) - call list_density % append(density * 0.0773_8) + call names % push_back('Ge70.' // xs) + call densities % push_back(density * 0.2057_8) + call names % push_back('Ge72.' // xs) + call densities % push_back(density * 0.2745_8) + call names % push_back('Ge73.' // xs) + call densities % push_back(density * 0.0775_8) + call names % push_back('Ge74.' // xs) + call densities % push_back(density * 0.3650_8) + call names % push_back('Ge76.' // xs) + call densities % push_back(density * 0.0773_8) case ('as') - call list_names % append('33075.' // xs) - call list_density % append(density) + call names % push_back('As75.' // xs) + call densities % push_back(density) case ('se') - call list_names % append('34074.' // xs) - call list_density % append(density * 0.0089_8) - call list_names % append('34076.' // xs) - call list_density % append(density * 0.0937_8) - call list_names % append('34077.' // xs) - call list_density % append(density * 0.0763_8) - call list_names % append('34078.' // xs) - call list_density % append(density * 0.2377_8) - call list_names % append('34080.' // xs) - call list_density % append(density * 0.4961_8) - call list_names % append('34082.' // xs) - call list_density % append(density * 0.0873_8) + call names % push_back('Se74.' // xs) + call densities % push_back(density * 0.0089_8) + call names % push_back('Se76.' // xs) + call densities % push_back(density * 0.0937_8) + call names % push_back('Se77.' // xs) + call densities % push_back(density * 0.0763_8) + call names % push_back('Se78.' // xs) + call densities % push_back(density * 0.2377_8) + call names % push_back('Se80.' // xs) + call densities % push_back(density * 0.4961_8) + call names % push_back('Se82.' // xs) + call densities % push_back(density * 0.0873_8) case ('br') - call list_names % append('35079.' // xs) - call list_density % append(density * 0.5069_8) - call list_names % append('35081.' // xs) - call list_density % append(density * 0.4931_8) + call names % push_back('Br79.' // xs) + call densities % push_back(density * 0.5069_8) + call names % push_back('Br81.' // xs) + call densities % push_back(density * 0.4931_8) case ('kr') - call list_names % append('36078.' // xs) - call list_density % append(density * 0.00355_8) - call list_names % append('36080.' // xs) - call list_density % append(density * 0.02286_8) - call list_names % append('36082.' // xs) - call list_density % append(density * 0.11593_8) - call list_names % append('36083.' // xs) - call list_density % append(density * 0.11500_8) - call list_names % append('36084.' // xs) - call list_density % append(density * 0.56987_8) - call list_names % append('36086.' // xs) - call list_density % append(density * 0.17279_8) + call names % push_back('Kr78.' // xs) + call densities % push_back(density * 0.00355_8) + call names % push_back('Kr80.' // xs) + call densities % push_back(density * 0.02286_8) + call names % push_back('Kr82.' // xs) + call densities % push_back(density * 0.11593_8) + call names % push_back('Kr83.' // xs) + call densities % push_back(density * 0.11500_8) + call names % push_back('Kr84.' // xs) + call densities % push_back(density * 0.56987_8) + call names % push_back('Kr86.' // xs) + call densities % push_back(density * 0.17279_8) case ('rb') - call list_names % append('37085.' // xs) - call list_density % append(density * 0.7217_8) - call list_names % append('37087.' // xs) - call list_density % append(density * 0.2783_8) + call names % push_back('Rb85.' // xs) + call densities % push_back(density * 0.7217_8) + call names % push_back('Rb87.' // xs) + call densities % push_back(density * 0.2783_8) case ('sr') - call list_names % append('38084.' // xs) - call list_density % append(density * 0.0056_8) - call list_names % append('38086.' // xs) - call list_density % append(density * 0.0986_8) - call list_names % append('38087.' // xs) - call list_density % append(density * 0.0700_8) - call list_names % append('38088.' // xs) - call list_density % append(density * 0.8258_8) + call names % push_back('Sr84.' // xs) + call densities % push_back(density * 0.0056_8) + call names % push_back('Sr86.' // xs) + call densities % push_back(density * 0.0986_8) + call names % push_back('Sr87.' // xs) + call densities % push_back(density * 0.0700_8) + call names % push_back('Sr88.' // xs) + call densities % push_back(density * 0.8258_8) case ('y') - call list_names % append('39089.' // xs) - call list_density % append(density) + call names % push_back('Y89.' // xs) + call densities % push_back(density) case ('zr') - call list_names % append('40090.' // xs) - call list_density % append(density * 0.5145_8) - call list_names % append('40091.' // xs) - call list_density % append(density * 0.1122_8) - call list_names % append('40092.' // xs) - call list_density % append(density * 0.1715_8) - call list_names % append('40094.' // xs) - call list_density % append(density * 0.1738_8) - call list_names % append('40096.' // xs) - call list_density % append(density * 0.0280_8) + call names % push_back('Zr90.' // xs) + call densities % push_back(density * 0.5145_8) + call names % push_back('Zr91.' // xs) + call densities % push_back(density * 0.1122_8) + call names % push_back('Zr92.' // xs) + call densities % push_back(density * 0.1715_8) + call names % push_back('Zr94.' // xs) + call densities % push_back(density * 0.1738_8) + call names % push_back('Zr96.' // xs) + call densities % push_back(density * 0.0280_8) case ('nb') - call list_names % append('41093.' // xs) - call list_density % append(density) + call names % push_back('Nb93.' // xs) + call densities % push_back(density) case ('mo') - call list_names % append('42092.' // xs) - call list_density % append(density * 0.1453_8) - call list_names % append('42094.' // xs) - call list_density % append(density * 0.0915_8) - call list_names % append('42095.' // xs) - call list_density % append(density * 0.1584_8) - call list_names % append('42096.' // xs) - call list_density % append(density * 0.1667_8) - call list_names % append('42097.' // xs) - call list_density % append(density * 0.0960_8) - call list_names % append('42098.' // xs) - call list_density % append(density * 0.2439_8) - call list_names % append('42100.' // xs) - call list_density % append(density * 0.0982_8) + call names % push_back('Mo92.' // xs) + call densities % push_back(density * 0.1453_8) + call names % push_back('Mo94.' // xs) + call densities % push_back(density * 0.0915_8) + call names % push_back('Mo95.' // xs) + call densities % push_back(density * 0.1584_8) + call names % push_back('Mo96.' // xs) + call densities % push_back(density * 0.1667_8) + call names % push_back('Mo97.' // xs) + call densities % push_back(density * 0.0960_8) + call names % push_back('Mo98.' // xs) + call densities % push_back(density * 0.2439_8) + call names % push_back('Mo100.' // xs) + call densities % push_back(density * 0.0982_8) case ('ru') - call list_names % append('44096.' // xs) - call list_density % append(density * 0.0554_8) - call list_names % append('44098.' // xs) - call list_density % append(density * 0.0187_8) - call list_names % append('44099.' // xs) - call list_density % append(density * 0.1276_8) - call list_names % append('44100.' // xs) - call list_density % append(density * 0.1260_8) - call list_names % append('44101.' // xs) - call list_density % append(density * 0.1706_8) - call list_names % append('44102.' // xs) - call list_density % append(density * 0.3155_8) - call list_names % append('44104.' // xs) - call list_density % append(density * 0.1862_8) + call names % push_back('Ru96.' // xs) + call densities % push_back(density * 0.0554_8) + call names % push_back('Ru98.' // xs) + call densities % push_back(density * 0.0187_8) + call names % push_back('Ru99.' // xs) + call densities % push_back(density * 0.1276_8) + call names % push_back('Ru100.' // xs) + call densities % push_back(density * 0.1260_8) + call names % push_back('Ru101.' // xs) + call densities % push_back(density * 0.1706_8) + call names % push_back('Ru102.' // xs) + call densities % push_back(density * 0.3155_8) + call names % push_back('Ru104.' // xs) + call densities % push_back(density * 0.1862_8) case ('rh') - call list_names % append('45103.' // xs) - call list_density % append(density) + call names % push_back('Rh103.' // xs) + call densities % push_back(density) case ('pd') - call list_names % append('46102.' // xs) - call list_density % append(density * 0.0102_8) - call list_names % append('46104.' // xs) - call list_density % append(density * 0.1114_8) - call list_names % append('46105.' // xs) - call list_density % append(density * 0.2233_8) - call list_names % append('46106.' // xs) - call list_density % append(density * 0.2733_8) - call list_names % append('46108.' // xs) - call list_density % append(density * 0.2646_8) - call list_names % append('46110.' // xs) - call list_density % append(density * 0.1172_8) + call names % push_back('Pd102.' // xs) + call densities % push_back(density * 0.0102_8) + call names % push_back('Pd104.' // xs) + call densities % push_back(density * 0.1114_8) + call names % push_back('Pd105.' // xs) + call densities % push_back(density * 0.2233_8) + call names % push_back('Pd106.' // xs) + call densities % push_back(density * 0.2733_8) + call names % push_back('Pd108.' // xs) + call densities % push_back(density * 0.2646_8) + call names % push_back('Pd110.' // xs) + call densities % push_back(density * 0.1172_8) case ('ag') - call list_names % append('47107.' // xs) - call list_density % append(density * 0.51839_8) - call list_names % append('47109.' // xs) - call list_density % append(density * 0.48161_8) + call names % push_back('Ag107.' // xs) + call densities % push_back(density * 0.51839_8) + call names % push_back('Ag109.' // xs) + call densities % push_back(density * 0.48161_8) case ('cd') - call list_names % append('48106.' // xs) - call list_density % append(density * 0.0125_8) - call list_names % append('48108.' // xs) - call list_density % append(density * 0.0089_8) - call list_names % append('48110.' // xs) - call list_density % append(density * 0.1249_8) - call list_names % append('48111.' // xs) - call list_density % append(density * 0.1280_8) - call list_names % append('48112.' // xs) - call list_density % append(density * 0.2413_8) - call list_names % append('48113.' // xs) - call list_density % append(density * 0.1222_8) - call list_names % append('48114.' // xs) - call list_density % append(density * 0.2873_8) - call list_names % append('48116.' // xs) - call list_density % append(density * 0.0749_8) + call names % push_back('Cd106.' // xs) + call densities % push_back(density * 0.0125_8) + call names % push_back('Cd108.' // xs) + call densities % push_back(density * 0.0089_8) + call names % push_back('Cd110.' // xs) + call densities % push_back(density * 0.1249_8) + call names % push_back('Cd111.' // xs) + call densities % push_back(density * 0.1280_8) + call names % push_back('Cd112.' // xs) + call densities % push_back(density * 0.2413_8) + call names % push_back('Cd113.' // xs) + call densities % push_back(density * 0.1222_8) + call names % push_back('Cd114.' // xs) + call densities % push_back(density * 0.2873_8) + call names % push_back('Cd116.' // xs) + call densities % push_back(density * 0.0749_8) case ('in') - call list_names % append('49113.' // xs) - call list_density % append(density * 0.0429_8) - call list_names % append('49115.' // xs) - call list_density % append(density * 0.9571_8) + call names % push_back('In113.' // xs) + call densities % push_back(density * 0.0429_8) + call names % push_back('In115.' // xs) + call densities % push_back(density * 0.9571_8) case ('sn') - call list_names % append('50112.' // xs) - call list_density % append(density * 0.0097_8) - call list_names % append('50114.' // xs) - call list_density % append(density * 0.0066_8) - call list_names % append('50115.' // xs) - call list_density % append(density * 0.0034_8) - call list_names % append('50116.' // xs) - call list_density % append(density * 0.1454_8) - call list_names % append('50117.' // xs) - call list_density % append(density * 0.0768_8) - call list_names % append('50118.' // xs) - call list_density % append(density * 0.2422_8) - call list_names % append('50119.' // xs) - call list_density % append(density * 0.0859_8) - call list_names % append('50120.' // xs) - call list_density % append(density * 0.3258_8) - call list_names % append('50122.' // xs) - call list_density % append(density * 0.0463_8) - call list_names % append('50124.' // xs) - call list_density % append(density * 0.0579_8) + call names % push_back('Sn112.' // xs) + call densities % push_back(density * 0.0097_8) + call names % push_back('Sn114.' // xs) + call densities % push_back(density * 0.0066_8) + call names % push_back('Sn115.' // xs) + call densities % push_back(density * 0.0034_8) + call names % push_back('Sn116.' // xs) + call densities % push_back(density * 0.1454_8) + call names % push_back('Sn117.' // xs) + call densities % push_back(density * 0.0768_8) + call names % push_back('Sn118.' // xs) + call densities % push_back(density * 0.2422_8) + call names % push_back('Sn119.' // xs) + call densities % push_back(density * 0.0859_8) + call names % push_back('Sn120.' // xs) + call densities % push_back(density * 0.3258_8) + call names % push_back('Sn122.' // xs) + call densities % push_back(density * 0.0463_8) + call names % push_back('Sn124.' // xs) + call densities % push_back(density * 0.0579_8) case ('sb') - call list_names % append('51121.' // xs) - call list_density % append(density * 0.5721_8) - call list_names % append('51123.' // xs) - call list_density % append(density * 0.4279_8) + call names % push_back('Sb121.' // xs) + call densities % push_back(density * 0.5721_8) + call names % push_back('Sb123.' // xs) + call densities % push_back(density * 0.4279_8) case ('te') - call list_names % append('52120.' // xs) - call list_density % append(density * 0.0009_8) - call list_names % append('52122.' // xs) - call list_density % append(density * 0.0255_8) - call list_names % append('52123.' // xs) - call list_density % append(density * 0.0089_8) - call list_names % append('52124.' // xs) - call list_density % append(density * 0.0474_8) - call list_names % append('52125.' // xs) - call list_density % append(density * 0.0707_8) - call list_names % append('52126.' // xs) - call list_density % append(density * 0.1884_8) - call list_names % append('52128.' // xs) - call list_density % append(density * 0.3174_8) - call list_names % append('52130.' // xs) - call list_density % append(density * 0.3408_8) + call names % push_back('Te120.' // xs) + call densities % push_back(density * 0.0009_8) + call names % push_back('Te122.' // xs) + call densities % push_back(density * 0.0255_8) + call names % push_back('Te123.' // xs) + call densities % push_back(density * 0.0089_8) + call names % push_back('Te124.' // xs) + call densities % push_back(density * 0.0474_8) + call names % push_back('Te125.' // xs) + call densities % push_back(density * 0.0707_8) + call names % push_back('Te126.' // xs) + call densities % push_back(density * 0.1884_8) + call names % push_back('Te128.' // xs) + call densities % push_back(density * 0.3174_8) + call names % push_back('Te130.' // xs) + call densities % push_back(density * 0.3408_8) case ('i') - call list_names % append('53127.' // xs) - call list_density % append(density) + call names % push_back('I127.' // xs) + call densities % push_back(density) case ('xe') - call list_names % append('54124.' // xs) - call list_density % append(density * 0.000952_8) - call list_names % append('54126.' // xs) - call list_density % append(density * 0.000890_8) - call list_names % append('54128.' // xs) - call list_density % append(density * 0.019102_8) - call list_names % append('54129.' // xs) - call list_density % append(density * 0.264006_8) - call list_names % append('54130.' // xs) - call list_density % append(density * 0.040710_8) - call list_names % append('54131.' // xs) - call list_density % append(density * 0.212324_8) - call list_names % append('54132.' // xs) - call list_density % append(density * 0.269086_8) - call list_names % append('54134.' // xs) - call list_density % append(density * 0.104357_8) - call list_names % append('54136.' // xs) - call list_density % append(density * 0.088573_8) + call names % push_back('Xe124.' // xs) + call densities % push_back(density * 0.000952_8) + call names % push_back('Xe126.' // xs) + call densities % push_back(density * 0.000890_8) + call names % push_back('Xe128.' // xs) + call densities % push_back(density * 0.019102_8) + call names % push_back('Xe129.' // xs) + call densities % push_back(density * 0.264006_8) + call names % push_back('Xe130.' // xs) + call densities % push_back(density * 0.040710_8) + call names % push_back('Xe131.' // xs) + call densities % push_back(density * 0.212324_8) + call names % push_back('Xe132.' // xs) + call densities % push_back(density * 0.269086_8) + call names % push_back('Xe134.' // xs) + call densities % push_back(density * 0.104357_8) + call names % push_back('Xe136.' // xs) + call densities % push_back(density * 0.088573_8) case ('cs') - call list_names % append('55133.' // xs) - call list_density % append(density) + call names % push_back('Cs133.' // xs) + call densities % push_back(density) case ('ba') - call list_names % append('56130.' // xs) - call list_density % append(density * 0.00106_8) - call list_names % append('56132.' // xs) - call list_density % append(density * 0.00101_8) - call list_names % append('56134.' // xs) - call list_density % append(density * 0.02417_8) - call list_names % append('56135.' // xs) - call list_density % append(density * 0.06592_8) - call list_names % append('56136.' // xs) - call list_density % append(density * 0.07854_8) - call list_names % append('56137.' // xs) - call list_density % append(density * 0.11232_8) - call list_names % append('56138.' // xs) - call list_density % append(density * 0.71698_8) + call names % push_back('Ba130.' // xs) + call densities % push_back(density * 0.00106_8) + call names % push_back('Ba132.' // xs) + call densities % push_back(density * 0.00101_8) + call names % push_back('Ba134.' // xs) + call densities % push_back(density * 0.02417_8) + call names % push_back('Ba135.' // xs) + call densities % push_back(density * 0.06592_8) + call names % push_back('Ba136.' // xs) + call densities % push_back(density * 0.07854_8) + call names % push_back('Ba137.' // xs) + call densities % push_back(density * 0.11232_8) + call names % push_back('Ba138.' // xs) + call densities % push_back(density * 0.71698_8) case ('la') - call list_names % append('57138.' // xs) - call list_density % append(density * 0.0008881_8) - call list_names % append('57139.' // xs) - call list_density % append(density * 0.9991119_8) + call names % push_back('La138.' // xs) + call densities % push_back(density * 0.0008881_8) + call names % push_back('La139.' // xs) + call densities % push_back(density * 0.9991119_8) case ('ce') - call list_names % append('58136.' // xs) - call list_density % append(density * 0.00185_8) - call list_names % append('58138.' // xs) - call list_density % append(density * 0.00251_8) - call list_names % append('58140.' // xs) - call list_density % append(density * 0.88450_8) - call list_names % append('58142.' // xs) - call list_density % append(density * 0.11114_8) + call names % push_back('Ce136.' // xs) + call densities % push_back(density * 0.00185_8) + call names % push_back('Ce138.' // xs) + call densities % push_back(density * 0.00251_8) + call names % push_back('Ce140.' // xs) + call densities % push_back(density * 0.88450_8) + call names % push_back('Ce142.' // xs) + call densities % push_back(density * 0.11114_8) case ('pr') - call list_names % append('59141.' // xs) - call list_density % append(density) + call names % push_back('Pr141.' // xs) + call densities % push_back(density) case ('nd') - call list_names % append('60142.' // xs) - call list_density % append(density * 0.27152_8) - call list_names % append('60143.' // xs) - call list_density % append(density * 0.12174_8) - call list_names % append('60144.' // xs) - call list_density % append(density * 0.23798_8) - call list_names % append('60145.' // xs) - call list_density % append(density * 0.08293_8) - call list_names % append('60146.' // xs) - call list_density % append(density * 0.17189_8) - call list_names % append('60148.' // xs) - call list_density % append(density * 0.05756_8) - call list_names % append('60150.' // xs) - call list_density % append(density * 0.05638_8) + call names % push_back('Nd142.' // xs) + call densities % push_back(density * 0.27152_8) + call names % push_back('Nd143.' // xs) + call densities % push_back(density * 0.12174_8) + call names % push_back('Nd144.' // xs) + call densities % push_back(density * 0.23798_8) + call names % push_back('Nd145.' // xs) + call densities % push_back(density * 0.08293_8) + call names % push_back('Nd146.' // xs) + call densities % push_back(density * 0.17189_8) + call names % push_back('Nd148.' // xs) + call densities % push_back(density * 0.05756_8) + call names % push_back('Nd150.' // xs) + call densities % push_back(density * 0.05638_8) case ('sm') - call list_names % append('62144.' // xs) - call list_density % append(density * 0.0307_8) - call list_names % append('62147.' // xs) - call list_density % append(density * 0.1499_8) - call list_names % append('62148.' // xs) - call list_density % append(density * 0.1124_8) - call list_names % append('62149.' // xs) - call list_density % append(density * 0.1382_8) - call list_names % append('62150.' // xs) - call list_density % append(density * 0.0738_8) - call list_names % append('62152.' // xs) - call list_density % append(density * 0.2675_8) - call list_names % append('62154.' // xs) - call list_density % append(density * 0.2275_8) + call names % push_back('Sm144.' // xs) + call densities % push_back(density * 0.0307_8) + call names % push_back('Sm147.' // xs) + call densities % push_back(density * 0.1499_8) + call names % push_back('Sm148.' // xs) + call densities % push_back(density * 0.1124_8) + call names % push_back('Sm149.' // xs) + call densities % push_back(density * 0.1382_8) + call names % push_back('Sm150.' // xs) + call densities % push_back(density * 0.0738_8) + call names % push_back('Sm152.' // xs) + call densities % push_back(density * 0.2675_8) + call names % push_back('Sm154.' // xs) + call densities % push_back(density * 0.2275_8) case ('eu') - call list_names % append('63151.' // xs) - call list_density % append(density * 0.4781_8) - call list_names % append('63153.' // xs) - call list_density % append(density * 0.5219_8) + call names % push_back('Eu151.' // xs) + call densities % push_back(density * 0.4781_8) + call names % push_back('Eu153.' // xs) + call densities % push_back(density * 0.5219_8) case ('gd') - call list_names % append('64152.' // xs) - call list_density % append(density * 0.0020_8) - call list_names % append('64154.' // xs) - call list_density % append(density * 0.0218_8) - call list_names % append('64155.' // xs) - call list_density % append(density * 0.1480_8) - call list_names % append('64156.' // xs) - call list_density % append(density * 0.2047_8) - call list_names % append('64157.' // xs) - call list_density % append(density * 0.1565_8) - call list_names % append('64158.' // xs) - call list_density % append(density * 0.2484_8) - call list_names % append('64160.' // xs) - call list_density % append(density * 0.2186_8) + call names % push_back('Gd152.' // xs) + call densities % push_back(density * 0.0020_8) + call names % push_back('Gd154.' // xs) + call densities % push_back(density * 0.0218_8) + call names % push_back('Gd155.' // xs) + call densities % push_back(density * 0.1480_8) + call names % push_back('Gd156.' // xs) + call densities % push_back(density * 0.2047_8) + call names % push_back('Gd157.' // xs) + call densities % push_back(density * 0.1565_8) + call names % push_back('Gd158.' // xs) + call densities % push_back(density * 0.2484_8) + call names % push_back('Gd160.' // xs) + call densities % push_back(density * 0.2186_8) case ('tb') - call list_names % append('65159.' // xs) - call list_density % append(density) + call names % push_back('Tb159.' // xs) + call densities % push_back(density) case ('dy') - call list_names % append('66156.' // xs) - call list_density % append(density * 0.00056_8) - call list_names % append('66158.' // xs) - call list_density % append(density * 0.00095_8) - call list_names % append('66160.' // xs) - call list_density % append(density * 0.02329_8) - call list_names % append('66161.' // xs) - call list_density % append(density * 0.18889_8) - call list_names % append('66162.' // xs) - call list_density % append(density * 0.25475_8) - call list_names % append('66163.' // xs) - call list_density % append(density * 0.24896_8) - call list_names % append('66164.' // xs) - call list_density % append(density * 0.28260_8) + call names % push_back('Dy156.' // xs) + call densities % push_back(density * 0.00056_8) + call names % push_back('Dy158.' // xs) + call densities % push_back(density * 0.00095_8) + call names % push_back('Dy160.' // xs) + call densities % push_back(density * 0.02329_8) + call names % push_back('Dy161.' // xs) + call densities % push_back(density * 0.18889_8) + call names % push_back('Dy162.' // xs) + call densities % push_back(density * 0.25475_8) + call names % push_back('Dy163.' // xs) + call densities % push_back(density * 0.24896_8) + call names % push_back('Dy164.' // xs) + call densities % push_back(density * 0.28260_8) case ('ho') - call list_names % append('67165.' // xs) - call list_density % append(density) + call names % push_back('Ho165.' // xs) + call densities % push_back(density) case ('er') - call list_names % append('68162.' // xs) - call list_density % append(density * 0.00139_8) - call list_names % append('68164.' // xs) - call list_density % append(density * 0.01601_8) - call list_names % append('68166.' // xs) - call list_density % append(density * 0.33503_8) - call list_names % append('68167.' // xs) - call list_density % append(density * 0.22869_8) - call list_names % append('68168.' // xs) - call list_density % append(density * 0.26978_8) - call list_names % append('68170.' // xs) - call list_density % append(density * 0.14910_8) + call names % push_back('Er162.' // xs) + call densities % push_back(density * 0.00139_8) + call names % push_back('Er164.' // xs) + call densities % push_back(density * 0.01601_8) + call names % push_back('Er166.' // xs) + call densities % push_back(density * 0.33503_8) + call names % push_back('Er167.' // xs) + call densities % push_back(density * 0.22869_8) + call names % push_back('Er168.' // xs) + call densities % push_back(density * 0.26978_8) + call names % push_back('Er170.' // xs) + call densities % push_back(density * 0.14910_8) case ('tm') - call list_names % append('69169.' // xs) - call list_density % append(density) + call names % push_back('Tm169.' // xs) + call densities % push_back(density) case ('yb') - call list_names % append('70168.' // xs) - call list_density % append(density * 0.00123_8) - call list_names % append('70170.' // xs) - call list_density % append(density * 0.02982_8) - call list_names % append('70171.' // xs) - call list_density % append(density * 0.1409_8) - call list_names % append('70172.' // xs) - call list_density % append(density * 0.2168_8) - call list_names % append('70173.' // xs) - call list_density % append(density * 0.16103_8) - call list_names % append('70174.' // xs) - call list_density % append(density * 0.32026_8) - call list_names % append('70176.' // xs) - call list_density % append(density * 0.12996_8) + call names % push_back('Yb168.' // xs) + call densities % push_back(density * 0.00123_8) + call names % push_back('Yb170.' // xs) + call densities % push_back(density * 0.02982_8) + call names % push_back('Yb171.' // xs) + call densities % push_back(density * 0.1409_8) + call names % push_back('Yb172.' // xs) + call densities % push_back(density * 0.2168_8) + call names % push_back('Yb173.' // xs) + call densities % push_back(density * 0.16103_8) + call names % push_back('Yb174.' // xs) + call densities % push_back(density * 0.32026_8) + call names % push_back('Yb176.' // xs) + call densities % push_back(density * 0.12996_8) case ('lu') - call list_names % append('71175.' // xs) - call list_density % append(density * 0.97401_8) - call list_names % append('71176.' // xs) - call list_density % append(density * 0.02599_8) + call names % push_back('Lu175.' // xs) + call densities % push_back(density * 0.97401_8) + call names % push_back('Lu176.' // xs) + call densities % push_back(density * 0.02599_8) case ('hf') - call list_names % append('72174.' // xs) - call list_density % append(density * 0.0016_8) - call list_names % append('72176.' // xs) - call list_density % append(density * 0.0526_8) - call list_names % append('72177.' // xs) - call list_density % append(density * 0.1860_8) - call list_names % append('72178.' // xs) - call list_density % append(density * 0.2728_8) - call list_names % append('72179.' // xs) - call list_density % append(density * 0.1362_8) - call list_names % append('72180.' // xs) - call list_density % append(density * 0.3508_8) + call names % push_back('Hf174.' // xs) + call densities % push_back(density * 0.0016_8) + call names % push_back('Hf176.' // xs) + call densities % push_back(density * 0.0526_8) + call names % push_back('Hf177.' // xs) + call densities % push_back(density * 0.1860_8) + call names % push_back('Hf178.' // xs) + call densities % push_back(density * 0.2728_8) + call names % push_back('Hf179.' // xs) + call densities % push_back(density * 0.1362_8) + call names % push_back('Hf180.' // xs) + call densities % push_back(density * 0.3508_8) case ('ta') if (default_expand == ENDF_BVII0 .or. & (default_expand >= JEFF_311 .and. default_expand <= JEFF_312) .or. & (default_expand >= JENDL_32 .and. default_expand <= JENDL_40)) then - call list_names % append('73181.' // xs) - call list_density % append(density) + call names % push_back('Ta181.' // xs) + call densities % push_back(density) else - call list_names % append('73180.' // xs) - call list_density % append(density * 0.0001201_8) - call list_names % append('73181.' // xs) - call list_density % append(density * 0.9998799_8) + call names % push_back('Ta180.' // xs) + call densities % push_back(density * 0.0001201_8) + call names % push_back('Ta181.' // xs) + call densities % push_back(density * 0.9998799_8) end if case ('w') @@ -5486,139 +5424,139 @@ contains .or. default_expand == JEFF_312 .or. & (default_expand >= JENDL_32 .and. default_expand <= JENDL_33)) then ! Combine W-180 with W-182 - call list_names % append('74182.' // xs) - call list_density % append(density * 0.2662_8) - call list_names % append('74183.' // xs) - call list_density % append(density * 0.1431_8) - call list_names % append('74184.' // xs) - call list_density % append(density * 0.3064_8) - call list_names % append('74186.' // xs) - call list_density % append(density * 0.2843_8) + call names % push_back('W182.' // xs) + call densities % push_back(density * 0.2662_8) + call names % push_back('W183.' // xs) + call densities % push_back(density * 0.1431_8) + call names % push_back('W184.' // xs) + call densities % push_back(density * 0.3064_8) + call names % push_back('W186.' // xs) + call densities % push_back(density * 0.2843_8) else - call list_names % append('74180.' // xs) - call list_density % append(density * 0.0012_8) - call list_names % append('74182.' // xs) - call list_density % append(density * 0.2650_8) - call list_names % append('74183.' // xs) - call list_density % append(density * 0.1431_8) - call list_names % append('74184.' // xs) - call list_density % append(density * 0.3064_8) - call list_names % append('74186.' // xs) - call list_density % append(density * 0.2843_8) + call names % push_back('W180.' // xs) + call densities % push_back(density * 0.0012_8) + call names % push_back('W182.' // xs) + call densities % push_back(density * 0.2650_8) + call names % push_back('W183.' // xs) + call densities % push_back(density * 0.1431_8) + call names % push_back('W184.' // xs) + call densities % push_back(density * 0.3064_8) + call names % push_back('W186.' // xs) + call densities % push_back(density * 0.2843_8) end if case ('re') - call list_names % append('75185.' // xs) - call list_density % append(density * 0.3740_8) - call list_names % append('75187.' // xs) - call list_density % append(density * 0.6260_8) + call names % push_back('Re185.' // xs) + call densities % push_back(density * 0.3740_8) + call names % push_back('Re187.' // xs) + call densities % push_back(density * 0.6260_8) case ('os') if (default_expand == JEFF_311 .or. default_expand == JEFF_312) then - call list_names % append('76000.' // xs) - call list_density % append(density) + call names % push_back('Os0.' // xs) + call densities % push_back(density) else - call list_names % append('76184.' // xs) - call list_density % append(density * 0.0002_8) - call list_names % append('76186.' // xs) - call list_density % append(density * 0.0159_8) - call list_names % append('76187.' // xs) - call list_density % append(density * 0.0196_8) - call list_names % append('76188.' // xs) - call list_density % append(density * 0.1324_8) - call list_names % append('76189.' // xs) - call list_density % append(density * 0.1615_8) - call list_names % append('76190.' // xs) - call list_density % append(density * 0.2626_8) - call list_names % append('76192.' // xs) - call list_density % append(density * 0.4078_8) + call names % push_back('Os184.' // xs) + call densities % push_back(density * 0.0002_8) + call names % push_back('Os186.' // xs) + call densities % push_back(density * 0.0159_8) + call names % push_back('Os187.' // xs) + call densities % push_back(density * 0.0196_8) + call names % push_back('Os188.' // xs) + call densities % push_back(density * 0.1324_8) + call names % push_back('Os189.' // xs) + call densities % push_back(density * 0.1615_8) + call names % push_back('Os190.' // xs) + call densities % push_back(density * 0.2626_8) + call names % push_back('Os192.' // xs) + call densities % push_back(density * 0.4078_8) end if case ('ir') - call list_names % append('77191.' // xs) - call list_density % append(density * 0.373_8) - call list_names % append('77193.' // xs) - call list_density % append(density * 0.627_8) + call names % push_back('Ir191.' // xs) + call densities % push_back(density * 0.373_8) + call names % push_back('Ir193.' // xs) + call densities % push_back(density * 0.627_8) case ('pt') if (default_expand == JEFF_311 .or. default_expand == JEFF_312) then - call list_names % append('78000.' // xs) - call list_density % append(density) + call names % push_back('Pt0.' // xs) + call densities % push_back(density) else - call list_names % append('78190.' // xs) - call list_density % append(density * 0.00012_8) - call list_names % append('78192.' // xs) - call list_density % append(density * 0.00782_8) - call list_names % append('78194.' // xs) - call list_density % append(density * 0.3286_8) - call list_names % append('78195.' // xs) - call list_density % append(density * 0.3378_8) - call list_names % append('78196.' // xs) - call list_density % append(density * 0.2521_8) - call list_names % append('78198.' // xs) - call list_density % append(density * 0.07356_8) + call names % push_back('Pt190.' // xs) + call densities % push_back(density * 0.00012_8) + call names % push_back('Pt192.' // xs) + call densities % push_back(density * 0.00782_8) + call names % push_back('Pt194.' // xs) + call densities % push_back(density * 0.3286_8) + call names % push_back('Pt195.' // xs) + call densities % push_back(density * 0.3378_8) + call names % push_back('Pt196.' // xs) + call densities % push_back(density * 0.2521_8) + call names % push_back('Pt198.' // xs) + call densities % push_back(density * 0.07356_8) end if case ('au') - call list_names % append('79197.' // xs) - call list_density % append(density) + call names % push_back('Au197.' // xs) + call densities % push_back(density) case ('hg') - call list_names % append('80196.' // xs) - call list_density % append(density * 0.0015_8) - call list_names % append('80198.' // xs) - call list_density % append(density * 0.0997_8) - call list_names % append('80199.' // xs) - call list_density % append(density * 0.1687_8) - call list_names % append('80200.' // xs) - call list_density % append(density * 0.2310_8) - call list_names % append('80201.' // xs) - call list_density % append(density * 0.1318_8) - call list_names % append('80202.' // xs) - call list_density % append(density * 0.2986_8) - call list_names % append('80204.' // xs) - call list_density % append(density * 0.0687_8) + call names % push_back('Hg196.' // xs) + call densities % push_back(density * 0.0015_8) + call names % push_back('Hg198.' // xs) + call densities % push_back(density * 0.0997_8) + call names % push_back('Hg199.' // xs) + call densities % push_back(density * 0.1687_8) + call names % push_back('Hg200.' // xs) + call densities % push_back(density * 0.2310_8) + call names % push_back('Hg201.' // xs) + call densities % push_back(density * 0.1318_8) + call names % push_back('Hg202.' // xs) + call densities % push_back(density * 0.2986_8) + call names % push_back('Hg204.' // xs) + call densities % push_back(density * 0.0687_8) case ('tl') if (default_expand == JEFF_311 .or. default_expand == JEFF_312) then - call list_names % append('81000.' // xs) - call list_density % append(density) + call names % push_back('Tl0.' // xs) + call densities % push_back(density) else - call list_names % append('81203.' // xs) - call list_density % append(density * 0.2952_8) - call list_names % append('81205.' // xs) - call list_density % append(density * 0.7048_8) + call names % push_back('Tl203.' // xs) + call densities % push_back(density * 0.2952_8) + call names % push_back('Tl205.' // xs) + call densities % push_back(density * 0.7048_8) end if case ('pb') - call list_names % append('82204.' // xs) - call list_density % append(density * 0.014_8) - call list_names % append('82206.' // xs) - call list_density % append(density * 0.241_8) - call list_names % append('82207.' // xs) - call list_density % append(density * 0.221_8) - call list_names % append('82208.' // xs) - call list_density % append(density * 0.524_8) + call names % push_back('Pb204.' // xs) + call densities % push_back(density * 0.014_8) + call names % push_back('Pb206.' // xs) + call densities % push_back(density * 0.241_8) + call names % push_back('Pb207.' // xs) + call densities % push_back(density * 0.221_8) + call names % push_back('Pb208.' // xs) + call densities % push_back(density * 0.524_8) case ('bi') - call list_names % append('83209.' // xs) - call list_density % append(density) + call names % push_back('Bi209.' // xs) + call densities % push_back(density) case ('th') - call list_names % append('90232.' // xs) - call list_density % append(density) + call names % push_back('Th232.' // xs) + call densities % push_back(density) case ('pa') - call list_names % append('91231.' // xs) - call list_density % append(density) + call names % push_back('Pa231.' // xs) + call densities % push_back(density) case ('u') - call list_names % append('92234.' // xs) - call list_density % append(density * 0.000054_8) - call list_names % append('92235.' // xs) - call list_density % append(density * 0.007204_8) - call list_names % append('92238.' // xs) - call list_density % append(density * 0.992742_8) + call names % push_back('U234.' // xs) + call densities % push_back(density * 0.000054_8) + call names % push_back('U235.' // xs) + call densities % push_back(density * 0.007204_8) + call names % push_back('U238.' // xs) + call densities % push_back(density * 0.992742_8) case default call fatal_error("Cannot expand element: " // name) @@ -5714,4 +5652,405 @@ contains end do end subroutine generate_rpn +!=============================================================================== +! NORMALIZE_AO normalizes the atom or weight percentages for each material +!=============================================================================== + + subroutine normalize_ao() + integer :: i ! index in materials array + integer :: j ! index over nuclides in material + real(8) :: sum_percent ! summation + real(8) :: awr ! atomic weight ratio + real(8) :: x ! atom percent + logical :: percent_in_atom ! nuclides specified in atom percent? + logical :: density_in_atom ! density specified in atom/b-cm? + + do i = 1, size(materials) + associate (mat => materials(i)) + percent_in_atom = (mat % atom_density(1) > ZERO) + density_in_atom = (mat % density > ZERO) + + sum_percent = ZERO + do j = 1, size(mat % nuclide) + ! determine atomic weight ratio + if (run_CE) then + awr = nuclides(mat % nuclide(j)) % awr + else + awr = ONE + end if + + ! if given weight percent, convert all values so that they are divided + ! by awr. thus, when a sum is done over the values, it's actually + ! sum(w/awr) + if (.not. percent_in_atom) then + mat % atom_density(j) = -mat % atom_density(j) / awr + end if + end do + + ! determine normalized atom percents. if given atom percents, this is + ! straightforward. if given weight percents, the value is w/awr and is + ! divided by sum(w/awr) + sum_percent = sum(mat % atom_density) + mat % atom_density = mat % atom_density / sum_percent + + ! Change density in g/cm^3 to atom/b-cm. Since all values are now in atom + ! percent, the sum needs to be re-evaluated as 1/sum(x*awr) + if (.not. density_in_atom) then + sum_percent = ZERO + do j = 1, mat % n_nuclides + if (run_CE) then + awr = nuclides(mat % nuclide(j)) % awr + else + awr = ONE + end if + x = mat % atom_density(j) + sum_percent = sum_percent + x*awr + end do + sum_percent = ONE / sum_percent + mat%density = -mat % density * N_AVOGADRO & + / MASS_NEUTRON * sum_percent + end if + + ! Calculate nuclide atom densities + mat % atom_density = mat % density * mat % atom_density + end associate + end do + + end subroutine normalize_ao + +!=============================================================================== +! ASSIGN_SAB_TABLES assigns S(alpha,beta) tables to specific nuclides within +! materials so the code knows when to apply bound thermal scattering data +!=============================================================================== + + subroutine assign_sab_tables() + integer :: i ! index in materials array + integer :: j ! index over nuclides in material + integer :: k ! index over S(a,b) tables in material + integer :: m ! position for sorting + integer :: temp_nuclide ! temporary value for sorting + integer :: temp_table ! temporary value for sorting + + do i = 1, size(materials) + ! Skip materials with no S(a,b) tables + if (.not. allocated(materials(i) % i_sab_tables)) cycle + + associate (mat => materials(i)) + + ASSIGN_SAB: do k = 1, size(mat % i_sab_tables) + ! In order to know which nuclide the S(a,b) table applies to, we need + ! to search through the list of nuclides for one which has a matching + ! zaid + associate (sab => sab_tables(mat % i_sab_tables(k))) + FIND_NUCLIDE: do j = 1, size(mat % nuclide) + if (any(sab % zaid == nuclides(mat % nuclide(j)) % zaid)) then + mat % i_sab_nuclides(k) = j + exit FIND_NUCLIDE + end if + end do FIND_NUCLIDE + end associate + + ! Check to make sure S(a,b) table matched a nuclide + if (mat % i_sab_nuclides(k) == NONE) then + call fatal_error("S(a,b) table " // trim(mat % & + sab_names(k)) // " did not match any nuclide on material " & + // trim(to_str(mat % id))) + end if + end do ASSIGN_SAB + + ! If there are multiple S(a,b) tables, we need to make sure that the + ! entries in i_sab_nuclides are sorted or else they won't be applied + ! correctly in the cross_section module. The algorithm here is a simple + ! insertion sort -- don't need anything fancy! + + if (size(mat % i_sab_tables) > 1) then + SORT_SAB: do k = 2, size(mat % i_sab_tables) + ! Save value to move + m = k + temp_nuclide = mat % i_sab_nuclides(k) + temp_table = mat % i_sab_tables(k) + + MOVE_OVER: do + ! Check if insertion value is greater than (m-1)th value + if (temp_nuclide >= mat % i_sab_nuclides(m-1)) exit + + ! Move values over until hitting one that's not larger + mat % i_sab_nuclides(m) = mat % i_sab_nuclides(m-1) + mat % i_sab_tables(m) = mat % i_sab_tables(m-1) + m = m - 1 + + ! Exit if we've reached the beginning of the list + if (m == 1) exit + end do MOVE_OVER + + ! Put the original value into its new position + mat % i_sab_nuclides(m) = temp_nuclide + mat % i_sab_tables(m) = temp_table + end do SORT_SAB + end if + + ! Deallocate temporary arrays for names of nuclides and S(a,b) tables + if (allocated(mat % names)) deallocate(mat % names) + end associate + end do + end subroutine assign_sab_tables + + subroutine read_ce_cross_sections(libraries, library_dict) + type(Library), intent(in) :: libraries(:) + type(DictCharInt), intent(inout) :: library_dict + + integer :: i, j + integer :: i_library + integer :: i_nuclide + integer :: i_sab + integer :: index_nuc_zaid ! index in nuclide ZAID + integer :: zaid ! ZAID of nuclide + integer(HID_T) :: file_id + integer(HID_T) :: group_id + logical :: mp_found ! if windowed multipole libraries were found + character(MAX_WORD_LEN) :: name + type(SetChar) :: already_read + + allocate(nuclides(n_nuclides_total)) + allocate(sab_tables(n_sab_tables)) +!$omp parallel + allocate(micro_xs(n_nuclides_total)) +!$omp end parallel + + index_nuc_zaid = 0 + + ! Read cross sections + do i = 1, size(materials) + do j = 1, size(materials(i) % names) + name = materials(i) % names(j) + + if (.not. already_read % contains(name)) then + i_library = library_dict % get_key(to_lower(name)) + i_nuclide = nuclide_dict % get_key(to_lower(name)) + + call write_message('Reading ' // trim(name) // ' from ' // & + trim(libraries(i_library) % path), 6) + + ! Read nuclide data from HDF5 + file_id = file_open(libraries(i_library) % path, 'r') + group_id = open_group(file_id, name) + call nuclides(i_nuclide) % from_hdf5(group_id) + call close_group(group_id) + call file_close(file_id) + + ! Assign resonant scattering data + if (treat_res_scat) call read_0K_elastic_scattering(& + nuclides(i_nuclide), libraries, library_dict) + + ! Determine if minimum/maximum energy for this nuclide is greater/less + ! than the previous + energy_min_neutron = max(energy_min_neutron, nuclides(i_nuclide) % energy(1)) + energy_max_neutron = min(energy_max_neutron, nuclides(i_nuclide) % energy(& + size(nuclides(i_nuclide) % energy))) + + ! Add name and alias to dictionary + call already_read % add(name) + + ! Construct dictionary mapping nuclide zaids to [1,N] -- used for + ! unresolved resonance probability tables + zaid = nuclides(i_nuclide) % zaid + if (.not. nuc_zaid_dict % has_key(zaid)) then + index_nuc_zaid = index_nuc_zaid + 1 + call nuc_zaid_dict % add_key(zaid, index_nuc_zaid) + end if + + ! Read multipole file into the appropriate entry on the nuclides array + if (multipole_active) call read_multipole_data(i_nuclide) + end if + + ! Check if material is fissionable + if (nuclides(materials(i) % nuclide(j)) % fissionable) then + materials(i) % fissionable = .true. + end if + end do + end do + + do i = 1, size(materials) + ! Skip materials with no S(a,b) tables + if (.not. allocated(materials(i) % sab_names)) cycle + + do j = 1, size(materials(i) % sab_names) + ! Get name of S(a,b) table + name = materials(i) % sab_names(j) + + if (.not. already_read % contains(name)) then + i_library = library_dict % get_key(to_lower(name)) + i_sab = sab_dict % get_key(to_lower(name)) + + call write_message('Reading ' // trim(name) // ' from ' // & + trim(libraries(i_library) % path), 6) + + ! Read S(a,b) data from HDF5 + file_id = file_open(libraries(i_library) % path, 'r') + group_id = open_group(file_id, name) + call sab_tables(i_sab) % from_hdf5(group_id) + call close_group(group_id) + call file_close(file_id) + + ! Add name to dictionary + call already_read % add(name) + end if + end do + end do + + n_nuc_zaid_total = index_nuc_zaid + + ! Associate S(a,b) tables with specific nuclides + call assign_sab_tables() + + ! Show which nuclide results in lowest energy for neutron transport + do i = 1, size(nuclides) + if (nuclides(i) % energy(nuclides(i) % n_grid) == energy_max_neutron) then + call write_message("Maximum neutron transport energy: " // & + trim(to_str(energy_max_neutron)) // " MeV for " // & + trim(adjustl(nuclides(i) % name)), 6) + exit + end if + end do + + ! If the user wants multipole, make sure we found a multipole library. + if (multipole_active) then + mp_found = .false. + do i = 1, size(nuclides) + if (nuclides(i) % mp_present) then + mp_found = .true. + exit + end if + end do + if (.not. mp_found) call warning("Windowed multipole functionality is & + &turned on, but no multipole libraries were found. Set the & + & element in settings.xml or the & + &OPENMC_MULTIPOLE_LIBRARY environment variable.") + end if + + end subroutine read_ce_cross_sections + +!=============================================================================== +! READ_0K_ELASTIC_SCATTERING +!=============================================================================== + + subroutine read_0K_elastic_scattering(nuc, libraries, library_dict) + type(Nuclide), intent(inout) :: nuc + type(Library), intent(in) :: libraries(:) + type(DictCharInt), intent(inout) :: library_dict + + integer :: i, j + integer :: i_library + integer(HID_T) :: file_id + integer(HID_T) :: group_id + real(8) :: xs_cdf_sum + character(MAX_WORD_LEN) :: name + type(Nuclide) :: resonant_nuc + + do i = 1, size(nuclides_0K) + if (nuc % name == nuclides_0K(i) % name) then + ! Copy basic information from settings.xml + nuc % resonant = .true. + nuc % name_0K = trim(nuclides_0K(i) % name_0K) + nuc % scheme = trim(nuclides_0K(i) % scheme) + nuc % E_min = nuclides_0K(i) % E_min + nuc % E_max = nuclides_0K(i) % E_max + + ! Get index in libraries array + name = nuc % name_0K + i_library = library_dict % get_key(to_lower(name)) + + call write_message('Reading ' // trim(name) // ' 0K data from ' // & + trim(libraries(i_library) % path), 6) + + ! Read nuclide data from HDF5 + file_id = file_open(libraries(i_library) % path, 'r') + group_id = open_group(file_id, name) + call resonant_nuc % from_hdf5(group_id) + call close_group(group_id) + call file_close(file_id) + + ! Copy 0K energy grid and elastic scattering cross section + call move_alloc(TO=nuc % energy_0K, FROM=resonant_nuc % energy) + call move_alloc(TO=nuc % elastic_0K, FROM=resonant_nuc % elastic) + nuc % n_grid_0K = size(nuc % energy_0K) + + ! Build CDF for 0K elastic scattering + xs_cdf_sum = ZERO + allocate(nuc % xs_cdf(size(nuc % energy_0K))) + + do j = 1, size(nuc % energy_0K) - 1 + ! Negative cross sections result in a CDF that is not monotonically + ! increasing. Set all negative xs values to ZERO. + if (nuc % elastic_0K(j) < ZERO) nuc % elastic_0K(j) = ZERO + + ! build xs cdf + xs_cdf_sum = xs_cdf_sum & + + (sqrt(nuc % energy_0K(j)) * nuc % elastic_0K(j) & + + sqrt(nuc % energy_0K(j+1)) * nuc % elastic_0K(j+1)) / TWO & + * (nuc % energy_0K(j+1) - nuc % energy_0K(j)) + nuc % xs_cdf(j) = xs_cdf_sum + end do + + exit + end if + end do + + end subroutine read_0K_elastic_scattering + +!=============================================================================== +! READ_MULTIPOLE_DATA checks for the existence of a multipole library in the +! directory and loads it using multipole_read +!=============================================================================== + + subroutine read_multipole_data(i_table) + + integer, intent(in) :: i_table ! index in nuclides/sab_tables + + integer :: i + logical :: file_exists ! Does multipole library exist? + character(7) :: readable ! Is multipole library readable? + character(6) :: zaid_string ! String of the ZAID + character(MAX_FILE_LEN+9) :: filename ! Path to multipole xs library + + ! For the time being, and I know this is a bit hacky, we just assume + ! that the file will be zaid.h5. + associate (nuc => nuclides(i_table)) + + write(zaid_string, '(I6.6)') nuc % zaid + filename = trim(path_multipole) // zaid_string // ".h5" + + ! Check if Multipole library exists and is readable + inquire(FILE=filename, EXIST=file_exists, READ=readable) + if (.not. file_exists) then + nuc % mp_present = .false. + return + elseif (readable(1:3) == 'NO') then + call fatal_error("Multipole library '" // trim(filename) // "' is not & + &readable! Change file permissions with chmod command.") + end if + + ! Display message + call write_message("Loading Multipole XS table: " // filename, 6) + + allocate(nuc % multipole) + + ! Call the read routine + call multipole_read(filename, nuc % multipole, i_table) + nuc % mp_present = .true. + + ! Recreate nu-fission cross section + if (nuc % fissionable) then + do i = 1, size(nuc % energy) + nuc % nu_fission(i) = nuc % nu(nuc % energy(i), EMISSION_TOTAL) * & + nuc % fission(i) + end do + else + nuc % nu_fission(:) = ZERO + end if + + end associate + + end subroutine read_multipole_data + end module input_xml diff --git a/src/material_header.F90 b/src/material_header.F90 index 91c4cdfb8..be4c860e2 100644 --- a/src/material_header.F90 +++ b/src/material_header.F90 @@ -27,8 +27,8 @@ module material_header integer, allocatable :: i_sab_tables(:) ! index in sab_tables ! Temporary names read during initialization - character(12), allocatable :: names(:) ! isotope names - character(12), allocatable :: sab_names(:) ! name of S(a,b) table + character(20), allocatable :: names(:) ! isotope names + character(20), allocatable :: sab_names(:) ! name of S(a,b) table ! Does this material contain fissionable nuclides? logical :: fissionable = .false. diff --git a/src/mgxs_data.F90 b/src/mgxs_data.F90 index 38d6ebc6c..491bd8409 100644 --- a/src/mgxs_data.F90 +++ b/src/mgxs_data.F90 @@ -23,10 +23,9 @@ contains integer :: i ! index in materials array integer :: j ! index over nuclides in material - integer :: i_listing ! index in xs_listings array + integer :: i_xsdata ! index in list integer :: i_nuclide ! index in nuclides - character(12) :: name ! name of isotope, e.g. 92235.03c - character(12) :: alias ! alias of isotope, e.g. U-235.03c + character(20) :: name ! name of isotope, e.g. 92235.03c integer :: representation ! Data representation type(Material), pointer :: mat type(SetChar) :: already_read @@ -37,6 +36,7 @@ contains character(MAX_LINE_LEN) :: temp_str logical :: get_kfiss, get_fiss integer :: l + type(DictCharInt) :: xsdata_dict ! Check if cross_sections.xml exists inquire(FILE=path_cross_sections, EXIST=file_exists) @@ -53,7 +53,17 @@ contains ! Get node list of all call get_node_list(doc, "xsdata", node_xsdata_list) - n_listings = get_list_size(node_xsdata_list) + + ! Build dictionary mapping nuclide names to an index in the node + ! list + do i = 1, get_list_size(node_xsdata_list) + ! Get pointer to xsdata table XML node + call get_list_item(node_xsdata_list, i, node_xsdata) + + ! Get name and create pair (name, i) + call get_node_value(node_xsdata, "name", name) + call xsdata_dict % add_key(to_lower(name), i) + end do ! allocate arrays for ACE table storage and cross section cache allocate(nuclides_MG(n_nuclides_total)) @@ -89,13 +99,11 @@ contains name = mat % names(j) if (.not. already_read % contains(name)) then - i_listing = xs_listing_dict % get_key(to_lower(name)) + i_xsdata = xsdata_dict % get_key(to_lower(name)) i_nuclide = mat % nuclide(j) - name = xs_listings(i_listing) % name - alias = xs_listings(i_listing) % alias ! Get pointer to xsdata table XML node - call get_list_item(node_xsdata_list, i_listing, node_xsdata) + call get_list_item(node_xsdata_list, i_xsdata, node_xsdata) call write_message("Loading " // trim(name) // " Data...", 5) @@ -125,11 +133,10 @@ contains ! Now read in the data specific to the type we just declared call nuclides_MG(i_nuclide) % obj % init_file(node_xsdata, & - energy_groups, get_kfiss, get_fiss, max_order, i_listing) + energy_groups, get_kfiss, get_fiss, max_order) - ! Add name and alias to dictionary + ! Add name to dictionary call already_read % add(name) - call already_read % add(alias) end if end do NUCLIDE_LOOP end do MATERIAL_LOOP @@ -185,8 +192,8 @@ contains allocate(MgxsAngle :: macro_xs(i_mat) % obj) end select call macro_xs(i_mat) % obj % combine(mat, nuclides_MG, energy_groups, & - max_order, scatt_type, i_mat) + max_order, scatt_type) end do end subroutine create_macro_xs -end module mgxs_data \ No newline at end of file +end module mgxs_data diff --git a/src/mgxs_header.F90 b/src/mgxs_header.F90 index 753f4e149..06dd1e213 100644 --- a/src/mgxs_header.F90 +++ b/src/mgxs_header.F90 @@ -21,7 +21,6 @@ module mgxs_header character(len=104) :: name ! name of dataset, e.g. 92235.03c integer :: zaid ! Z and A identifier, e.g. 92235 real(8) :: awr ! Atomic Weight Ratio - integer :: listing ! index in xs_listings real(8) :: kT ! temperature in MeV (k*T) ! Fission information @@ -54,8 +53,8 @@ module mgxs_header !=============================================================================== abstract interface - subroutine mgxs_init_file_(this,node_xsdata,groups,get_kfiss,get_fiss, & - max_order,i_listing) + subroutine mgxs_init_file_(this, node_xsdata, groups, get_kfiss, get_fiss, & + max_order) import Mgxs, Node class(Mgxs), intent(inout) :: this ! Working Object type(Node), pointer, intent(in) :: node_xsdata ! Data from MGXS xml @@ -63,7 +62,6 @@ module mgxs_header logical, intent(in) :: get_kfiss ! Need Kappa-Fission? logical, intent(in) :: get_fiss ! Should we get fiss data? integer, intent(in) :: max_order ! Maximum requested order - integer, intent(in) :: i_listing ! Index of listings array end subroutine mgxs_init_file_ subroutine mgxs_print_(this, unit) @@ -96,8 +94,7 @@ module mgxs_header end function mgxs_calc_f_ - subroutine mgxs_combine_(this,mat,nuclides,groups,max_order,scatt_type, & - i_listing) + subroutine mgxs_combine_(this, mat, nuclides, groups, max_order, scatt_type) import Mgxs, Material, MgxsContainer class(Mgxs), intent(inout) :: this ! The Mgxs to initialize type(Material), pointer, intent(in) :: mat ! base material @@ -105,7 +102,6 @@ module mgxs_header integer, intent(in) :: groups ! Number of E groups integer, intent(in) :: max_order ! Maximum requested order integer, intent(in) :: scatt_type ! Legendre or Tabular Scatt? - integer, intent(in) :: i_listing ! Index in listings end subroutine mgxs_combine_ function mgxs_sample_fission_(this, gin, uvw) result(gout) @@ -201,10 +197,9 @@ module mgxs_header ! the xsdata object node itself. !=============================================================================== - subroutine mgxs_init_file(this, node_xsdata, i_listing) + subroutine mgxs_init_file(this, node_xsdata) class(Mgxs), intent(inout) :: this ! Working Object type(Node), pointer, intent(in) :: node_xsdata ! Data from MGXS xml - integer, intent(in) :: i_listing ! Index in listings array character(MAX_LINE_LEN) :: temp_str @@ -254,20 +249,16 @@ module mgxs_header call fatal_error("Fissionable element must be set!") end if - ! Keep track of what listing is associated with this nuclide - this % listing = i_listing - end subroutine mgxs_init_file subroutine mgxsiso_init_file(this, node_xsdata, groups, get_kfiss, get_fiss, & - max_order, i_listing) + max_order) class(MgxsIso), intent(inout) :: this ! Working Object type(Node), pointer, intent(in) :: node_xsdata ! Data from MGXS xml integer, intent(in) :: groups ! Number of Energy groups logical, intent(in) :: get_kfiss ! Need Kappa-Fission? logical, intent(in) :: get_fiss ! Need fiss data? integer, intent(in) :: max_order ! Maximum requested order - integer, intent(in) :: i_listing ! Index in listings array type(Node), pointer :: node_legendre_mu character(MAX_LINE_LEN) :: temp_str @@ -282,7 +273,7 @@ module mgxs_header integer :: legendre_mu_points, imu ! Call generic data gathering routine (will populate the metadata) - call mgxs_init_file(this, node_xsdata, i_listing) + call mgxs_init_file(this, node_xsdata) ! Load the more specific data allocate(this % nu_fission(groups)) @@ -564,14 +555,13 @@ module mgxs_header end subroutine mgxsiso_init_file subroutine mgxsang_init_file(this, node_xsdata, groups, get_kfiss, get_fiss, & - max_order, i_listing) + max_order) class(MgxsAngle), intent(inout) :: this ! Working Object type(Node), pointer, intent(in) :: node_xsdata ! Data from MGXS xml integer, intent(in) :: groups ! Number of Energy groups logical, intent(in) :: get_kfiss ! Need Kappa-Fission? logical, intent(in) :: get_fiss ! Should we get fiss data? integer, intent(in) :: max_order ! Maximum requested order - integer, intent(in) :: i_listing ! Index in listings array type(Node), pointer :: node_legendre_mu character(MAX_LINE_LEN) :: temp_str @@ -586,7 +576,7 @@ module mgxs_header integer :: legendre_mu_points, imu, ipol, iazi ! Call generic data gathering routine (will populate the metadata) - call mgxs_init_file(this, node_xsdata, i_listing) + call mgxs_init_file(this, node_xsdata) if (check_for_node(node_xsdata, "num_polar")) then call get_node_value(node_xsdata, "num_polar", this % n_pol) @@ -1318,11 +1308,10 @@ module mgxs_header ! objects !=============================================================================== - subroutine mgxs_combine(this, mat, scatt_type, i_listing) + subroutine mgxs_combine(this, mat, scatt_type) class(Mgxs), intent(inout) :: this ! The Mgxs to initialize type(Material), pointer, intent(in) :: mat ! base material integer, intent(in) :: scatt_type ! How is data presented - integer, intent(in) :: i_listing ! Index in listings ! Fill in meta-data from material information if (mat % name == "") then @@ -1331,7 +1320,6 @@ module mgxs_header this % name = mat % name end if this % zaid = -mat % id - this % listing = i_listing this % fissionable = mat % fissionable this % scatt_type = scatt_type @@ -1342,15 +1330,13 @@ module mgxs_header end subroutine mgxs_combine - subroutine mgxsiso_combine(this, mat, nuclides, groups, max_order, scatt_type, & - i_listing) + subroutine mgxsiso_combine(this, mat, nuclides, groups, max_order, scatt_type) class(MgxsIso), intent(inout) :: this ! The Mgxs to initialize type(Material), pointer, intent(in) :: mat ! base material type(MgxsContainer), intent(in) :: nuclides(:) ! List of nuclides to harvest from integer, intent(in) :: groups ! Number of E groups integer, intent(in) :: max_order ! Maximum requested order integer, intent(in) :: scatt_type ! How is data presented - integer, intent(in) :: i_listing ! Index in listings integer :: i ! loop index over nuclides integer :: gin, gout ! group indices @@ -1361,7 +1347,7 @@ module mgxs_header real(8), allocatable :: scatt_coeffs(:, :, :) ! Set the meta-data - call mgxs_combine(this, mat, scatt_type, i_listing) + call mgxs_combine(this, mat, scatt_type) ! Determine the scattering type of our data and ensure all scattering orders ! are the same. @@ -1538,15 +1524,13 @@ module mgxs_header end subroutine mgxsiso_combine - subroutine mgxsang_combine(this, mat, nuclides, groups, max_order, scatt_type, & - i_listing) + subroutine mgxsang_combine(this, mat, nuclides, groups, max_order, scatt_type) class(MgxsAngle), intent(inout) :: this ! The Mgxs to initialize type(Material), pointer, intent(in) :: mat ! base material type(MgxsContainer), intent(in) :: nuclides(:) ! List of nuclides to harvest from integer, intent(in) :: groups ! Number of E groups integer, intent(in) :: max_order ! Maximum requested order integer, intent(in) :: scatt_type ! Legendre or Tabular Scatt? - integer, intent(in) :: i_listing ! Index in listings integer :: i ! loop index over nuclides integer :: gin, gout ! group indices @@ -1558,7 +1542,7 @@ module mgxs_header real(8), allocatable :: mult_denom(:, :, :, :), scatt_coeffs(:, :, :, :, :) ! Set the meta-data - call mgxs_combine(this, mat, scatt_type, i_listing) + call mgxs_combine(this, mat, scatt_type) ! Get the number of each polar and azi angles and make sure all the ! NuclideAngle types have the same number of these angles @@ -1939,4 +1923,4 @@ module mgxs_header end subroutine find_angle -end module mgxs_header \ No newline at end of file +end module mgxs_header diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index 2c59e700d..be61ff8b9 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -5,13 +5,18 @@ module nuclide_header use constants use dict_header, only: DictIntInt use endf, only: reaction_name, is_fission, is_disappearance - use endf_header, only: Function1D + use endf_header, only: Function1D, Constant1D, Polynomial, Tabulated1D use error, only: fatal_error, warning + use hdf5, only: HID_T, HSIZE_T, SIZE_T, h5iget_name_f + use h5lt, only: h5ltpath_valid_f + use hdf5_interface, only: read_attribute, open_group, close_group, & + open_dataset, read_dataset, close_dataset, get_shape use list_header, only: ListInt use math, only: evaluate_legendre use multipole_header, only: MultipoleArray use product_header, only: AngleEnergyContainer use reaction_header, only: Reaction + use secondary_uncorrelated, only: UncorrelatedAngleEnergy use stl_vector, only: VectorInt use string use urr_header, only: UrrData @@ -26,14 +31,14 @@ module nuclide_header type :: Nuclide ! Nuclide meta-data - character(12) :: name ! name of nuclide, e.g. 92235.03c + character(20) :: name ! name of nuclide, e.g. U235.71c integer :: zaid ! Z and A identifier, e.g. 92235 + integer :: metastable ! metastable state real(8) :: awr ! Atomic Weight Ratio - integer :: listing ! index in xs_listings real(8) :: kT ! temperature in MeV (k*T) ! Fission information - logical :: fissionable ! nuclide is fissionable? + logical :: fissionable = .false. ! nuclide is fissionable? ! Energy grid information integer :: n_grid ! # of nuclide grid points @@ -61,13 +66,13 @@ module nuclide_header ! Fission information logical :: has_partial_fission = .false. ! nuclide has partial fission reactions? - integer :: n_fission ! # of fission reactions + integer :: n_fission = 0 ! # of fission reactions integer :: n_precursor = 0 ! # of delayed neutron precursors integer, allocatable :: index_fission(:) ! indices in reactions class(Function1D), allocatable :: total_nu ! Unresolved resonance data - logical :: urr_present + logical :: urr_present = .false. integer :: urr_inelastic type(UrrData), pointer :: urr_data => null() @@ -84,7 +89,9 @@ module nuclide_header contains procedure :: clear => nuclide_clear procedure :: print => nuclide_print + procedure :: from_hdf5 => nuclide_from_hdf5 procedure :: nu => nuclide_nu + procedure, private :: create_derived => nuclide_create_derived end type Nuclide !=============================================================================== @@ -144,24 +151,14 @@ module nuclide_header end type MaterialMacroXS !=============================================================================== -! XSLISTING contains data read from a CE or MG cross_sections.xml file -! (or equivalent) +! LIBRARY contains data read from a cross_sections.xml file !=============================================================================== - type XsListing - character(12) :: name ! table name, e.g. 92235.70c - character(12) :: alias ! table alias, e.g. U-235.70c - integer :: type ! type of table (cont-E neutron, S(A,b), etc) - integer :: zaid ! ZAID identifier = 1000*Z + A - integer :: filetype ! ASCII or BINARY - integer :: location ! location of table within library - integer :: recl ! record length for library - integer :: entries ! number of entries per record - real(8) :: awr ! atomic weight ratio (# of neutron masses) - real(8) :: kT ! Boltzmann constant * temperature (MeV) - logical :: metastable ! is this nuclide metastable? - character(MAX_FILE_LEN) :: path ! path to library containing table - end type XsListing + type Library + integer :: type + character(MAX_WORD_LEN), allocatable :: materials(:) + character(MAX_FILE_LEN) :: path + end type Library contains @@ -173,18 +170,254 @@ module nuclide_header class(Nuclide), intent(inout) :: this ! The Nuclide object to clear if (associated(this % urr_data)) deallocate(this % urr_data) - - call this % reaction_index % clear() - if (associated(this % multipole)) deallocate(this % multipole) end subroutine nuclide_clear + subroutine nuclide_from_hdf5(this, group_id) + class(Nuclide), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + + integer :: i + integer :: Z + integer :: A + integer :: n_reaction + integer :: hdf5_err + integer(HID_T) :: urr_group, nu_group + integer(HID_T) :: energy_dset + integer(HID_T) :: rx_group + integer(HID_T) :: total_nu + integer(SIZE_T) :: name_len, name_file_len + integer(HSIZE_T) :: dims(1) + character(MAX_WORD_LEN) :: temp + logical :: exists + + ! Get name of nuclide from group + name_len = len(this % name) + call h5iget_name_f(group_id, this % name, name_len, name_file_len, hdf5_err) + + ! Get rid of leading '/' + this % name = trim(this % name(2:)) + + call read_attribute(Z, group_id, 'Z') + call read_attribute(A, group_id, 'A') + call read_attribute(this % metastable, group_id, 'metastable') + this % zaid = 1000*Z + A + 400*this % metastable + call read_attribute(this % awr, group_id, 'atomic_weight_ratio') + call read_attribute(this % kT, group_id, 'temperature') + call read_attribute(n_reaction, group_id, 'n_reaction') + this % n_reaction = n_reaction + + ! Read energy grid + energy_dset = open_dataset(group_id, 'energy') + call get_shape(energy_dset, dims) + this % n_grid = int(dims(1), 4) + allocate(this % energy(this % n_grid)) + call read_dataset(this % energy, energy_dset) + call close_dataset(energy_dset) + + ! Read reactions + allocate(this % reactions(n_reaction)) + do i = 1, size(this % reactions) + rx_group = open_group(group_id, 'reaction_' // trim(to_str(i - 1))) + call this % reactions(i) % from_hdf5(rx_group) + call close_group(rx_group) + end do + + ! Read unresolved resonance probability tables if present + call h5ltpath_valid_f(group_id, 'urr', .true., exists, hdf5_err) + if (exists) then + this % urr_present = .true. + allocate(this % urr_data) + urr_group = open_group(group_id, 'urr') + call this % urr_data % from_hdf5(urr_group) + + ! if the inelastic competition flag indicates that the inelastic cross + ! section should be determined from a normal reaction cross section, we need + ! to get the index of the reaction + if (this % urr_data % inelastic_flag > 0) then + do i = 1, size(this % reactions) + if (this % reactions(i) % MT == this % urr_data % inelastic_flag) then + this % urr_inelastic = i + end if + end do + + ! Abort if no corresponding inelastic reaction was found + if (this % urr_inelastic == NONE) then + call fatal_error("Could not find inelastic reaction specified on & + &unresolved resonance probability table.") + end if + end if + + ! Check for negative values + if (any(this % urr_data % prob < ZERO)) then + call warning("Negative value(s) found on probability table & + &for nuclide " // this % name) + end if + end if + + ! Check for nu-total + call h5ltpath_valid_f(group_id, 'total_nu', .true., exists, hdf5_err) + if (exists) then + nu_group = open_group(group_id, 'total_nu') + + ! Read total nu data + total_nu = open_dataset(nu_group, 'yield') + call read_attribute(temp, total_nu, 'type') + select case (temp) + case ('constant') + allocate(Constant1D :: this % total_nu) + case ('tabulated') + allocate(Tabulated1D :: this % total_nu) + case ('polynomial') + allocate(Polynomial :: this % total_nu) + end select + call this % total_nu % from_hdf5(total_nu) + call close_dataset(total_nu) + + call close_group(nu_group) + end if + + ! Create derived cross section data + call this % create_derived() + + end subroutine nuclide_from_hdf5 + + subroutine nuclide_create_derived(this) + class(Nuclide), intent(inout) :: this + + integer :: i + integer :: j + integer :: k + integer :: m + integer :: n + integer :: i_fission + type(ListInt) :: MTs + + ! Allocate and initialize derived cross sections + allocate(this % total(this % n_grid)) + allocate(this % elastic(this % n_grid)) + allocate(this % fission(this % n_grid)) + allocate(this % nu_fission(this % n_grid)) + allocate(this % absorption(this % n_grid)) + this % total(:) = ZERO + this % elastic(:) = ZERO + this % fission(:) = ZERO + this % nu_fission(:) = ZERO + this % absorption(:) = ZERO + + i_fission = 0 + + do i = 1, size(this % reactions) + call MTs % append(this % reactions(i) % MT) + call this % reaction_index % add_key(this % reactions(i) % MT, i) + + associate (rx => this % reactions(i)) + j = rx % threshold + n = size(rx % sigma) + + ! Skip total inelastic level scattering, gas production cross sections + ! (MT=200+), etc. + if (rx % MT == N_LEVEL .or. rx % MT == N_NONELASTIC) cycle + if (rx % MT > N_5N2P .and. rx % MT < N_P0) cycle + + ! Skip level cross sections if total is available + if (rx % MT >= N_P0 .and. rx % MT <= N_PC .and. MTs % contains(N_P)) cycle + if (rx % MT >= N_D0 .and. rx % MT <= N_DC .and. MTs % contains(N_D)) cycle + if (rx % MT >= N_T0 .and. rx % MT <= N_TC .and. MTs % contains(N_T)) cycle + if (rx % MT >= N_3HE0 .and. rx % MT <= N_3HEC .and. MTs % contains(N_3HE)) cycle + if (rx % MT >= N_A0 .and. rx % MT <= N_AC .and. MTs % contains(N_A)) cycle + if (rx % MT >= N_2N0 .and. rx % MT <= N_2NC .and. MTs % contains(N_2N)) cycle + + ! Copy elastic + if (rx % MT == ELASTIC) this % elastic(:) = rx % sigma + + ! Add contribution to total cross section + this % total(j:j+n-1) = this % total(j:j+n-1) + rx % sigma + + ! Add contribution to absorption cross section + if (is_disappearance(rx % MT)) then + this % absorption(j:j+n-1) = this % absorption(j:j+n-1) + rx % sigma + end if + + ! Information about fission reactions + if (rx % MT == N_FISSION) then + allocate(this % index_fission(1)) + elseif (rx % MT == N_F) then + allocate(this % index_fission(PARTIAL_FISSION_MAX)) + this % has_partial_fission = .true. + end if + + ! Add contribution to fission cross section + if (is_fission(rx % MT)) then + this % fissionable = .true. + this % fission(j:j+n-1) = this % fission(j:j+n-1) + rx % sigma + + ! Also need to add fission cross sections to absorption + this % absorption(j:j+n-1) = this % absorption(j:j+n-1) + rx % sigma + + ! If total fission reaction is present, there's no need to store the + ! reaction cross-section since it was copied to this % fission + if (rx % MT == N_FISSION) deallocate(rx % sigma) + + ! Keep track of this reaction for easy searching later + i_fission = i_fission + 1 + this % index_fission(i_fission) = i + this % n_fission = this % n_fission + 1 + + ! <<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<< + ! Before the secondary distribution refactor, when the angle/energy + ! distribution was uncorrelated, no angle was actually sampled. With + ! the refactor, an angle is always sampled for an uncorrelated + ! distribution even when no angle distribution exists in the ACE file + ! (isotropic is assumed). To preserve the RNG stream, we explicitly + ! mark fission reactions so that we avoid the angle sampling. + do k = 1, size(rx % products) + if (rx % products(k) % particle == NEUTRON) then + do m = 1, size(rx % products(k) % distribution) + associate (aedist => rx % products(k) % distribution(m) % obj) + select type (aedist) + type is (UncorrelatedAngleEnergy) + aedist % fission = .true. + end select + end associate + end do + end if + end do + ! <<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<< + end if + end associate + end do + + ! Determine number of delayed neutron precursors + if (this % fissionable) then + do i = 1, size(this % reactions(this % index_fission(1)) % products) + if (this % reactions(this % index_fission(1)) % products(i) % & + emission_mode == EMISSION_DELAYED) then + this % n_precursor = this % n_precursor + 1 + end if + end do + end if + + ! Calculate nu-fission cross section + if (this % fissionable) then + do i = 1, size(this % energy) + this % nu_fission(i) = this % nu(this % energy(i), EMISSION_TOTAL) * & + this % fission(i) + end do + else + this % nu_fission(:) = ZERO + end if + + ! Clear MTs set + call MTs % clear() + end subroutine nuclide_create_derived + !=============================================================================== ! NUCLIDE_NU is an interface to the number of fission neutrons produced !=============================================================================== - function nuclide_nu(this, E, emission_mode, group) result(nu) + pure function nuclide_nu(this, E, emission_mode, group) result(nu) class(Nuclide), intent(in) :: this real(8), intent(in) :: E integer, intent(in) :: emission_mode @@ -237,8 +470,8 @@ module nuclide_header if (allocated(this % total_nu)) then nu = this % total_nu % evaluate(E) else - associate (rx => this % reactions(this % index_fission(1))) - nu = rx % products(1) % yield % evaluate(E) + associate (product => this % reactions(this % index_fission(1)) % products(1)) + nu = product % yield % evaluate(E) end associate end if end select diff --git a/src/output.F90 b/src/output.F90 index d7dfe7e1c..09df23f0d 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -741,7 +741,6 @@ contains integer :: filter_index ! index in results array for filters integer :: score_index ! scoring bin index integer :: i_nuclide ! index in nuclides array - integer :: i_listing ! index in xs_listings array integer :: n_order ! loop index for moment orders integer :: nm_order ! loop index for Ynm moment orders integer :: unit_tally ! tallies.out file unit @@ -908,13 +907,8 @@ contains write(UNIT=unit_tally, FMT='(1X,2A,1X,A)') repeat(" ", indent), & "Total Material" else - if (run_CE) then - i_listing = nuclides(i_nuclide) % listing - else - i_listing = nuclides_MG(i_nuclide) % obj % listing - end if write(UNIT=unit_tally, FMT='(1X,2A,1X,A)') repeat(" ", indent), & - trim(xs_listings(i_listing) % alias) + trim(nuclides(i_nuclide) % name) end if indent = indent + 2 diff --git a/src/product_header.F90 b/src/product_header.F90 index e20c173b4..fbe0ee71f 100644 --- a/src/product_header.F90 +++ b/src/product_header.F90 @@ -4,7 +4,15 @@ module product_header use constants, only: ZERO, MAX_WORD_LEN, EMISSION_PROMPT, EMISSION_DELAYED, & EMISSION_TOTAL, NEUTRON, PHOTON use endf_header, only: Tabulated1D, Function1D, Constant1D, Polynomial + use hdf5, only: HID_T + use hdf5_interface, only: read_attribute, open_group, close_group, & + open_dataset, close_dataset, read_dataset use random_lcg, only: prn + use secondary_correlated, only: CorrelatedAngleEnergy + use secondary_kalbach, only: KalbachMann + use secondary_nbody, only: NBodyPhaseSpace + use secondary_uncorrelated, only: UncorrelatedAngleEnergy + use string, only: to_str !=============================================================================== ! REACTIONPRODUCT stores a data for a reaction product including its yield and @@ -23,6 +31,7 @@ module product_header type(AngleEnergyContainer), allocatable :: distribution(:) contains procedure :: sample => reactionproduct_sample + procedure :: from_hdf5 => reactionproduct_from_hdf5 end type ReactionProduct contains @@ -59,4 +68,89 @@ contains end subroutine reactionproduct_sample + subroutine reactionproduct_from_hdf5(this, group_id) + class(ReactionProduct), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + + integer :: i + integer :: n + integer(HID_T) :: dgroup + integer(HID_T) :: app + integer(HID_T) :: yield + character(MAX_WORD_LEN) :: temp + + ! Read particle type + call read_attribute(temp, group_id, 'particle') + select case (temp) + case ('neutron') + this % particle = NEUTRON + case ('photon') + this % particle = PHOTON + end select + + ! Read emission mode and decay rate + call read_attribute(temp, group_id, 'emission_mode') + select case (temp) + case ('prompt') + this % emission_mode = EMISSION_PROMPT + case ('delayed') + this % emission_mode = EMISSION_DELAYED + case ('total') + this % emission_mode = EMISSION_TOTAL + end select + + ! Read decay rate for delayed emission + if (this % emission_mode == EMISSION_DELAYED) then + call read_attribute(this % decay_rate, group_id, 'decay_rate') + end if + + ! Read secondary particle yield + yield = open_dataset(group_id, 'yield') + call read_attribute(temp, yield, 'type') + select case (temp) + case ('constant') + allocate(Constant1D :: this % yield) + case ('tabulated') + allocate(Tabulated1D :: this % yield) + case ('polynomial') + allocate(Polynomial :: this % yield) + end select + call this % yield % from_hdf5(yield) + call close_dataset(yield) + + call read_attribute(n, group_id, 'n_distribution') + allocate(this%applicability(n)) + allocate(this%distribution(n)) + + do i = 1, n + dgroup = open_group(group_id, trim('distribution_' // to_str(i - 1))) + + ! Read applicability + if (n > 1) then + app = open_dataset(dgroup, 'applicability') + call this%applicability(i)%from_hdf5(app) + call close_dataset(app) + end if + + ! Read type of distribution and allocate accordingly + call read_attribute(temp, dgroup, 'type') + select case (temp) + case ('uncorrelated') + allocate(UncorrelatedAngleEnergy :: this%distribution(i)%obj) + case ('correlated') + allocate(CorrelatedAngleEnergy :: this%distribution(i)%obj) + case ('nbody') + allocate(NBodyPhaseSpace :: this%distribution(i)%obj) + case ('kalbach-mann') + allocate(KalbachMann :: this%distribution(i)%obj) + end select + + ! Read distribution data + call this%distribution(i)%obj%from_hdf5(dgroup) + + call close_group(dgroup) + end do + + end subroutine reactionproduct_from_hdf5 + end module product_header diff --git a/src/reaction_header.F90 b/src/reaction_header.F90 index 160ad6323..8af75b3cf 100644 --- a/src/reaction_header.F90 +++ b/src/reaction_header.F90 @@ -1,6 +1,10 @@ module reaction_header + use hdf5, only: HID_T, HSIZE_T + use hdf5_interface, only: read_attribute, open_group, close_group, & + open_dataset, read_dataset, close_dataset, get_shape use product_header, only: ReactionProduct + use string, only: to_str implicit none @@ -16,6 +20,44 @@ module reaction_header logical :: scatter_in_cm ! scattering system in center-of-mass? real(8), allocatable :: sigma(:) ! Cross section values type(ReactionProduct), allocatable :: products(:) + contains + procedure :: from_hdf5 => reaction_from_hdf5 end type Reaction +contains + + subroutine reaction_from_hdf5(this, group_id) + class(Reaction), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + + integer :: i + integer :: cm + integer :: n_product + integer(HID_T) :: pgroup + integer(HID_T) :: xs + integer(HSIZE_T) :: dims(1) + + call read_attribute(this % Q_value, group_id, 'Q_value') + call read_attribute(this % MT, group_id, 'MT') + call read_attribute(this % threshold, group_id, 'threshold_idx') + call read_attribute(cm, group_id, 'center_of_mass') + this % scatter_in_cm = (cm == 1) + + ! Read cross section + xs = open_dataset(group_id, 'xs') + call get_shape(xs, dims) + allocate(this % sigma(dims(1))) + call read_dataset(this % sigma, xs) + call close_dataset(xs) + + ! Read products + call read_attribute(n_product, group_id, 'n_product') + allocate(this % products(n_product)) + do i = 1, n_product + pgroup = open_group(group_id, 'product_' // trim(to_str(i - 1))) + call this % products(i) % from_hdf5(pgroup) + call close_group(pgroup) + end do + end subroutine reaction_from_hdf5 + end module reaction_header diff --git a/src/relaxng/materials.rnc b/src/relaxng/materials.rnc index 21b36c07e..1b5b7c705 100644 --- a/src/relaxng/materials.rnc +++ b/src/relaxng/materials.rnc @@ -11,8 +11,7 @@ element materials { } & element nuclide { - (element name { xsd:string { maxLength = "7" } } | - attribute name { xsd:string { maxLength = "7" } }) & + (element name { xsd:string } | attribute name { xsd:string }) & (element xs { xsd:string { maxLength = "5" } } | attribute xs { xsd:string { maxLength = "5" } })? & (element scattering { ( "data" | "iso-in-lab" ) } | @@ -44,8 +43,7 @@ element materials { }* & element sab { - (element name { xsd:string { maxLength = "7" } } | - attribute name { xsd:string { maxLength = "7" } }) & + (element name { xsd:string } | attribute name { xsd:string }) & (element xs { xsd:string { maxLength = "5" } } | attribute xs { xsd:string { maxLength = "5" } })? }* diff --git a/src/relaxng/materials.rng b/src/relaxng/materials.rng index 6a4bc3964..e93f20165 100644 --- a/src/relaxng/materials.rng +++ b/src/relaxng/materials.rng @@ -57,26 +57,22 @@ - - 7 - + - - 7 - + - 3 + 5 - 3 + 5 @@ -123,25 +119,21 @@ - - 7 - + - - 7 - + - 3 + 5 - 3 + 5 @@ -167,12 +159,12 @@ - 3 + 5 - 3 + 5 @@ -219,26 +211,22 @@ - - 7 - + - - 7 - + - 3 + 5 - 3 + 5 @@ -252,7 +240,7 @@ - 3 + 5 diff --git a/src/sab_header.F90 b/src/sab_header.F90 index 695f735c0..3e9e18fab 100644 --- a/src/sab_header.F90 +++ b/src/sab_header.F90 @@ -3,6 +3,12 @@ module sab_header use, intrinsic :: ISO_FORTRAN_ENV use constants + use distribution_univariate, only: Tabular + use hdf5, only: HID_T, HSIZE_T + use h5lt, only: h5ltpath_valid_f + use hdf5_interface, only: read_attribute, get_shape, open_group, close_group, & + open_dataset, read_dataset, close_dataset + use secondary_correlated, only: CorrelatedAngleEnergy use string, only: to_str implicit none @@ -27,10 +33,10 @@ module sab_header !=============================================================================== type SAlphaBeta - character(10) :: name ! name of table, e.g. lwtr.10t - real(8) :: awr ! weight of nucleus in neutron masses - real(8) :: kT ! temperature in MeV (k*T) - integer :: n_zaid ! Number of valid zaids + character(100) :: name ! name of table, e.g. lwtr.10t + real(8) :: awr ! weight of nucleus in neutron masses + real(8) :: kT ! temperature in MeV (k*T) + integer :: n_zaid ! Number of valid zaids integer, allocatable :: zaid(:) ! List of valid Z and A identifiers, e.g. 6012 ! threshold for S(a,b) treatment (usually ~4 eV) @@ -61,90 +67,247 @@ module sab_header real(8), allocatable :: elastic_P(:) real(8), allocatable :: elastic_mu(:,:) contains - procedure :: print => print_sab_table + procedure :: print => salphabeta_print + procedure :: from_hdf5 => salphabeta_from_hdf5 end type SAlphaBeta - contains +contains !=============================================================================== ! PRINT_SAB_TABLE displays information about a S(a,b) table containing data ! describing thermal scattering from bound materials such as hydrogen in water. !=============================================================================== - subroutine print_sab_table(this, unit) - class(SAlphaBeta), intent(in) :: this - integer, intent(in), optional :: unit + subroutine salphabeta_print(this, unit) + class(SAlphaBeta), intent(in) :: this + integer, intent(in), optional :: unit - integer :: size_sab ! memory used by S(a,b) table - integer :: unit_ ! unit to write to - integer :: i ! Loop counter for parsing through this % zaid - integer :: char_count ! Counter for the number of characters on a line + integer :: size_sab ! memory used by S(a,b) table + integer :: unit_ ! unit to write to + integer :: i ! Loop counter for parsing through this % zaid + integer :: char_count ! Counter for the number of characters on a line - ! set default unit for writing information - if (present(unit)) then - unit_ = unit + ! set default unit for writing information + if (present(unit)) then + unit_ = unit + else + unit_ = OUTPUT_UNIT + end if + + ! Basic S(a,b) table information + write(unit_,*) 'S(a,b) Table ' // trim(this % name) + write(unit_,'(A)',advance="no") ' zaids = ' + ! Initialize the counter based on the above string + char_count = 11 + do i = 1, this % n_zaid + ! Deal with a line thats too long + if (char_count >= 73) then ! 73 = 80 - (5 ZAID chars + 1 space + 1 comma) + ! End the line + write(unit_,*) "" + ! Add 11 leading blanks + write(unit_,'(A)', advance="no") " " + ! reset the counter to 11 + char_count = 11 + end if + if (i < this % n_zaid) then + ! Include a comma + write(unit_,'(A)',advance="no") trim(to_str(this % zaid(i))) // ", " + char_count = char_count + len(trim(to_str(this % zaid(i)))) + 2 else - unit_ = OUTPUT_UNIT + ! Don't include a comma, since we are all done + write(unit_,'(A)',advance="no") trim(to_str(this % zaid(i))) end if - ! Basic S(a,b) table information - write(unit_,*) 'S(a,b) Table ' // trim(this % name) - write(unit_,'(A)',advance="no") ' zaids = ' - ! Initialize the counter based on the above string - char_count = 11 - do i = 1, this % n_zaid - ! Deal with a line thats too long - if (char_count >= 73) then ! 73 = 80 - (5 ZAID chars + 1 space + 1 comma) - ! End the line - write(unit_,*) "" - ! Add 11 leading blanks - write(unit_,'(A)', advance="no") " " - ! reset the counter to 11 - char_count = 11 - end if - if (i < this % n_zaid) then - ! Include a comma - write(unit_,'(A)',advance="no") trim(to_str(this % zaid(i))) // ", " - char_count = char_count + len(trim(to_str(this % zaid(i)))) + 2 - else - ! Don't include a comma, since we are all done - write(unit_,'(A)',advance="no") trim(to_str(this % zaid(i))) - end if + end do + write(unit_,*) "" ! Move to next line + write(unit_,*) ' awr = ' // trim(to_str(this % awr)) + write(unit_,*) ' kT = ' // trim(to_str(this % kT)) - end do - write(unit_,*) "" ! Move to next line - write(unit_,*) ' awr = ' // trim(to_str(this % awr)) - write(unit_,*) ' kT = ' // trim(to_str(this % kT)) + ! Inelastic data + write(unit_,*) ' # of Incoming Energies (Inelastic) = ' // & + trim(to_str(this % n_inelastic_e_in)) + write(unit_,*) ' # of Outgoing Energies (Inelastic) = ' // & + trim(to_str(this % n_inelastic_e_out)) + write(unit_,*) ' # of Outgoing Angles (Inelastic) = ' // & + trim(to_str(this % n_inelastic_mu)) + write(unit_,*) ' Threshold for Inelastic = ' // & + trim(to_str(this % threshold_inelastic)) - ! Inelastic data - write(unit_,*) ' # of Incoming Energies (Inelastic) = ' // & - trim(to_str(this % n_inelastic_e_in)) - write(unit_,*) ' # of Outgoing Energies (Inelastic) = ' // & - trim(to_str(this % n_inelastic_e_out)) - write(unit_,*) ' # of Outgoing Angles (Inelastic) = ' // & - trim(to_str(this % n_inelastic_mu)) - write(unit_,*) ' Threshold for Inelastic = ' // & - trim(to_str(this % threshold_inelastic)) + ! Elastic data + if (this % n_elastic_e_in > 0) then + write(unit_,*) ' # of Incoming Energies (Elastic) = ' // & + trim(to_str(this % n_elastic_e_in)) + write(unit_,*) ' # of Outgoing Angles (Elastic) = ' // & + trim(to_str(this % n_elastic_mu)) + write(unit_,*) ' Threshold for Elastic = ' // & + trim(to_str(this % threshold_elastic)) + end if - ! Elastic data - if (this % n_elastic_e_in > 0) then - write(unit_,*) ' # of Incoming Energies (Elastic) = ' // & - trim(to_str(this % n_elastic_e_in)) - write(unit_,*) ' # of Outgoing Angles (Elastic) = ' // & - trim(to_str(this % n_elastic_mu)) - write(unit_,*) ' Threshold for Elastic = ' // & - trim(to_str(this % threshold_elastic)) + ! Determine memory used by S(a,b) table and write out + size_sab = 8 * (this % n_inelastic_e_in * (2 + this % n_inelastic_e_out * & + (1 + this % n_inelastic_mu)) + this % n_elastic_e_in * & + (2 + this % n_elastic_mu)) + write(unit_,*) ' Memory Used = ' // trim(to_str(size_sab)) // ' bytes' + + ! Blank line at end + write(unit_,*) + + end subroutine salphabeta_print + + subroutine salphabeta_from_hdf5(this, group_id) + class(SAlphaBeta), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + + integer :: i, j + integer :: n_energy, n_energy_out, n_mu + integer :: hdf5_err + integer(HID_T) :: elastic_group + integer(HID_T) :: inelastic_group + integer(HID_T) :: dset_id + integer(HSIZE_T) :: dims2(2) + integer(HSIZE_T) :: dims3(3) + real(8), allocatable :: temp(:,:) + character(20) :: type + logical :: exists + type(CorrelatedAngleEnergy) :: correlated_dist + + call read_attribute(this % awr, group_id, 'atomic_weight_ratio') + call read_attribute(this % kT, group_id, 'temperature') + call read_attribute(this % zaid, group_id, 'zaids') + this % n_zaid = size(this % zaid) + + ! Coherent elastic data + call h5ltpath_valid_f(group_id, 'elastic', .true., exists, hdf5_err) + if (exists) then + ! Read cross section data + elastic_group = open_group(group_id, 'elastic') + dset_id = open_dataset(elastic_group, 'xs') + call read_attribute(type, dset_id, 'type') + call get_shape(dset_id, dims2) + allocate(temp(dims2(1), dims2(2))) + call read_dataset(temp, dset_id) + call close_dataset(dset_id) + + ! Set cross section data and type + this % n_elastic_e_in = int(dims2(1), 4) + allocate(this % elastic_e_in(this % n_elastic_e_in)) + allocate(this % elastic_P(this % n_elastic_e_in)) + this % elastic_e_in(:) = temp(:, 1) + this % elastic_P(:) = temp(:, 2) + select case (type) + case ('tab1') + this % elastic_mode = SAB_ELASTIC_DISCRETE + case ('bragg') + this % elastic_mode = SAB_ELASTIC_EXACT + end select + deallocate(temp) + + ! Set elastic threshold + this % threshold_elastic = this % elastic_e_in(this % n_elastic_e_in) + + ! Read angle distribution + if (this % elastic_mode /= SAB_ELASTIC_EXACT) then + dset_id = open_dataset(elastic_group, 'mu_out') + call get_shape(dset_id, dims2) + this % n_elastic_mu = int(dims2(1), 4) + allocate(this % elastic_mu(dims2(1), dims2(2))) + call read_dataset(this % elastic_mu, dset_id) + call close_dataset(dset_id) end if - ! Determine memory used by S(a,b) table and write out - size_sab = 8 * (this % n_inelastic_e_in * (2 + this % n_inelastic_e_out * & - (1 + this % n_inelastic_mu)) + this % n_elastic_e_in * & - (2 + this % n_elastic_mu)) - write(unit_,*) ' Memory Used = ' // trim(to_str(size_sab)) // ' bytes' + call close_group(elastic_group) + end if - ! Blank line at end - write(unit_,*) + ! Inelastic data + call h5ltpath_valid_f(group_id, 'inelastic', .true., exists, hdf5_err) + if (exists) then + ! Read type of inelastic data + inelastic_group = open_group(group_id, 'inelastic') + call read_attribute(type, inelastic_group, 'secondary_mode') + select case (type) + case ('equal') + this % secondary_mode = SAB_SECONDARY_EQUAL + case ('skewed') + this % secondary_mode = SAB_SECONDARY_SKEWED + case ('continuous') + this % secondary_mode = SAB_SECONDARY_CONT + end select - end subroutine print_sab_table + ! Read cross section data + dset_id = open_dataset(inelastic_group, 'xs') + call get_shape(dset_id, dims2) + allocate(temp(dims2(1), dims2(2))) + call read_dataset(temp, dset_id) + call close_dataset(dset_id) + + ! Set cross section data + this % n_inelastic_e_in = int(dims2(1), 4) + allocate(this % inelastic_e_in(this % n_inelastic_e_in)) + allocate(this % inelastic_sigma(this % n_inelastic_e_in)) + this % inelastic_e_in(:) = temp(:, 1) + this % inelastic_sigma(:) = temp(:, 2) + deallocate(temp) + + ! Set inelastic threshold + this % threshold_inelastic = this % inelastic_e_in(this % n_inelastic_e_in) + + if (this % secondary_mode /= SAB_SECONDARY_CONT) then + ! Read energy distribution + dset_id = open_dataset(inelastic_group, 'energy_out') + call get_shape(dset_id, dims2) + this % n_inelastic_e_out = int(dims2(1), 4) + allocate(this % inelastic_e_out(dims2(1), dims2(2))) + call read_dataset(this % inelastic_e_out, dset_id) + call close_dataset(dset_id) + + ! Read angle distribution + dset_id = open_dataset(inelastic_group, 'mu_out') + call get_shape(dset_id, dims3) + this % n_inelastic_mu = int(dims3(1), 4) + allocate(this % inelastic_mu(dims3(1), dims3(2), dims3(3))) + call read_dataset(this % inelastic_mu, dset_id) + call close_dataset(dset_id) + else + ! Read correlated angle-energy distribution + call correlated_dist % from_hdf5(inelastic_group) + + ! Convert to S(a,b) native format + n_energy = size(correlated_dist % energy) + allocate(this % inelastic_data(n_energy)) + do i = 1, n_energy + associate (edist => correlated_dist % distribution(i)) + ! Get number of outgoing energies for incoming energy i + n_energy_out = size(edist % e_out) + this % inelastic_data(i) % n_e_out = n_energy_out + allocate(this % inelastic_data(i) % e_out(n_energy_out)) + allocate(this % inelastic_data(i) % e_out_pdf(n_energy_out)) + allocate(this % inelastic_data(i) % e_out_cdf(n_energy_out)) + + ! Copy outgoing energy distribution + this % inelastic_data(i) % e_out(:) = edist % e_out + this % inelastic_data(i) % e_out_pdf(:) = edist % p + this % inelastic_data(i) % e_out_cdf(:) = edist % c + + do j = 1, n_energy_out + select type (adist => edist % angle(j) % obj) + type is (Tabular) + ! On first pass, allocate space for angles + if (j == 1) then + n_mu = size(adist % x) + this % n_inelastic_mu = n_mu + allocate(this % inelastic_data(i) % mu(n_mu, n_energy_out)) + end if + + ! Copy outgoing angles + this % inelastic_data(i) % mu(:, j) = adist % x + end select + end do + end associate + end do + end if + + call close_group(inelastic_group) + end if + end subroutine salphabeta_from_hdf5 end module sab_header diff --git a/src/secondary_correlated.F90 b/src/secondary_correlated.F90 index b556d02dc..0576b28f6 100644 --- a/src/secondary_correlated.F90 +++ b/src/secondary_correlated.F90 @@ -1,8 +1,11 @@ module secondary_correlated use angleenergy_header, only: AngleEnergy - use constants, only: ZERO, ONE, TWO, HISTOGRAM, LINEAR_LINEAR - use distribution_univariate, only: DistributionContainer + use constants, only: ZERO, ONE, HALF, TWO, HISTOGRAM, LINEAR_LINEAR + use distribution_univariate, only: DistributionContainer, Tabular + use hdf5, only: HID_T, HSIZE_T + use hdf5_interface, only: get_shape, read_attribute, open_dataset, & + read_dataset, close_dataset use random_lcg, only: prn use search, only: binary_search @@ -28,6 +31,7 @@ module secondary_correlated type(AngleEnergyTable), allocatable :: distribution(:) ! outgoing E/mu distributions contains procedure :: sample => correlated_sample + procedure :: from_hdf5 => correlated_from_hdf5 end type CorrelatedAngleEnergy contains @@ -145,4 +149,156 @@ contains end if end subroutine correlated_sample + subroutine correlated_from_hdf5(this, group_id) + class(CorrelatedAngleEnergy), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + + integer :: i, j, k + integer :: n_energy + integer :: m, n + integer :: offset_mu + integer :: interp_mu + integer(HID_T) :: dset_id + integer(HSIZE_T) :: dims(1), dims2(2) + integer, allocatable :: temp(:,:) + integer, allocatable :: offsets(:) + integer, allocatable :: interp(:) + integer, allocatable :: n_discrete(:) + real(8), allocatable :: eout(:,:) + real(8), allocatable :: mu(:,:) + + ! Open incoming energy dataset + dset_id = open_dataset(group_id, 'energy') + + ! Get interpolation parameters + call read_attribute(temp, dset_id, 'interpolation') + allocate(this%breakpoints(size(temp, 1))) + allocate(this%interpolation(size(temp, 1))) + this%breakpoints(:) = temp(:, 1) + this%interpolation(:) = temp(:, 2) + this%n_region = size(this%breakpoints) + deallocate(temp) + + ! Get incoming energies + call get_shape(dset_id, dims) + n_energy = int(dims(1), 4) + allocate(this%energy(n_energy)) + allocate(this%distribution(n_energy)) + call read_dataset(this%energy, dset_id) + call close_dataset(dset_id) + + ! Get outgoing energy distribution data + dset_id = open_dataset(group_id, 'energy_out') + call read_attribute(offsets, dset_id, 'offsets') + call read_attribute(interp, dset_id, 'interpolation') + call read_attribute(n_discrete, dset_id, 'n_discrete_lines') + call get_shape(dset_id, dims2) + allocate(eout(dims2(1), dims2(2))) + call read_dataset(eout, dset_id) + call close_dataset(dset_id) + + ! Get outgoing angle data + dset_id = open_dataset(group_id, 'mu') + call get_shape(dset_id, dims2) + allocate(mu(dims2(1), dims2(2))) + call read_dataset(mu, dset_id) + call close_dataset(dset_id) + + do i = 1, n_energy + ! Determine number of outgoing energies + j = offsets(i) + if (i < n_energy) then + n = offsets(i+1) - j + else + n = size(eout, 1) - j + end if + + associate (d => this % distribution(i)) + ! Assign interpolation scheme and number of discrete lines + d % interpolation = interp(i) + d % n_discrete = n_discrete(i) + + ! Allocate arrays for energies and PDF/CDF + allocate(d % e_out(n)) + allocate(d % p(n)) + allocate(d % c(n)) + allocate(d % angle(n)) + + ! Copy data + d % e_out(:) = eout(j+1:j+n, 1) + d % p(:) = eout(j+1:j+n, 2) + d % c(:) = eout(j+1:j+n, 3) + + ! To get answers that match ACE data, for now we still use the tabulated + ! CDF values that were passed through to the HDF5 library. At a later + ! time, we can remove the CDF values from the HDF5 library and + ! reconstruct them using the PDF + if (.false.) then + ! Calculate cumulative distribution function -- discrete portion + do k = 1, d % n_discrete + if (k == 1) then + d % c(k) = d % p(k) + else + d % c(k) = d % c(k-1) + d % p(k) + end if + end do + + ! Continuous portion + do k = d % n_discrete + 1, n + if (k == d % n_discrete + 1) then + d % c(k) = sum(d % p(1:d % n_discrete)) + else + if (d % interpolation == HISTOGRAM) then + d % c(k) = d % c(k-1) + d % p(k-1) * & + (d % e_out(k) - d % e_out(k-1)) + elseif (d % interpolation == LINEAR_LINEAR) then + d % c(k) = d % c(k-1) + HALF*(d % p(k-1) + d % p(k)) * & + (d % e_out(k) - d % e_out(k-1)) + end if + end if + end do + + ! Normalize density and distribution functions + d % p(:) = d % p(:)/d % c(n) + d % c(:) = d % c(:)/d % c(n) + end if + end associate + + do j = 1, n + allocate(Tabular :: this%distribution(i)%angle(j)%obj) + select type(mudist => this%distribution(i)%angle(j)%obj) + type is (Tabular) + ! Get interpolation scheme + interp_mu = nint(eout(offsets(i)+j, 4)) + + ! Determine offset and size of distribution + offset_mu = nint(eout(offsets(i)+j, 5)) + if (offsets(i) + j < size(eout, 1)) then + m = nint(eout(offsets(i)+j+1, 5)) - offset_mu + else + m = size(mu, 1) - offset_mu + end if + + ! To get answers that match ACE data, for now we still use the tabulated + ! CDF values that were passed through to the HDF5 library. At a later + ! time, we can remove the CDF values from the HDF5 library and + ! reconstruct them using the PDF + if (.true.) then + mudist % interpolation = interp_mu + allocate(mudist % x(m)) + allocate(mudist % p(m)) + allocate(mudist % c(m)) + mudist % x(:) = mu(offset_mu+1:offset_mu+m, 1) + mudist % p(:) = mu(offset_mu+1:offset_mu+m, 2) + mudist % c(:) = mu(offset_mu+1:offset_mu+m, 3) + else + ! Initialize tabular distribution + call mudist % initialize(mu(offset_mu+1:offset_mu+m, 1), & + mu(offset_mu+1:offset_mu+m, 2), interp_mu) + end if + end select + end do + end do + end subroutine correlated_from_hdf5 + end module secondary_correlated diff --git a/src/secondary_kalbach.F90 b/src/secondary_kalbach.F90 index 668917d62..920d17869 100644 --- a/src/secondary_kalbach.F90 +++ b/src/secondary_kalbach.F90 @@ -1,7 +1,10 @@ module secondary_kalbach use angleenergy_header, only: AngleEnergy - use constants, only: ZERO, ONE, TWO, HISTOGRAM, LINEAR_LINEAR + use constants, only: ZERO, HALF, ONE, TWO, HISTOGRAM, LINEAR_LINEAR + use hdf5, only: HID_T, HSIZE_T + use hdf5_interface, only: read_attribute, read_dataset, open_dataset, & + close_dataset, get_shape use random_lcg, only: prn use search, only: binary_search @@ -29,6 +32,7 @@ module secondary_kalbach type(KalbachMannTable), allocatable :: distribution(:) ! outgoing E/mu parameters contains procedure :: sample => kalbachmann_sample + procedure :: from_hdf5 => kalbachmann_from_hdf5 end type KalbachMann contains @@ -161,4 +165,116 @@ contains end subroutine kalbachmann_sample + subroutine kalbachmann_from_hdf5(this, group_id) + class(KalbachMann), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + + integer :: i, j, k + integer :: n + integer :: n_energy + integer(HID_T) :: dset_id + integer(HSIZE_T) :: dims(1), dims2(2) + integer, allocatable :: temp(:,:) + integer, allocatable :: offsets(:) + integer, allocatable :: interp(:) + integer, allocatable :: n_discrete(:) + real(8), allocatable :: eout(:,:) + + ! Open incoming energy dataset + dset_id = open_dataset(group_id, 'energy') + + ! Get interpolation parameters + call read_attribute(temp, dset_id, 'interpolation') + allocate(this%breakpoints(size(temp, 1))) + allocate(this%interpolation(size(temp, 1))) + this%breakpoints(:) = temp(:, 1) + this%interpolation(:) = temp(:, 2) + this%n_region = size(this%breakpoints) + + ! Get incoming energies + call get_shape(dset_id, dims) + n_energy = int(dims(1), 4) + allocate(this%energy(n_energy)) + allocate(this%distribution(n_energy)) + call read_dataset(this%energy, dset_id) + call close_dataset(dset_id) + + ! Get outgoing energy distribution data + dset_id = open_dataset(group_id, 'distribution') + call read_attribute(offsets, dset_id, 'offsets') + call read_attribute(interp, dset_id, 'interpolation') + call read_attribute(n_discrete, dset_id, 'n_discrete_lines') + call get_shape(dset_id, dims2) + allocate(eout(dims2(1), dims2(2))) + call read_dataset(eout, dset_id) + call close_dataset(dset_id) + + do i = 1, n_energy + ! Determine number of outgoing energies + j = offsets(i) + if (i < n_energy) then + n = offsets(i+1) - j + else + n = size(eout, 1) - j + end if + + associate (d => this%distribution(i)) + ! Assign interpolation scheme and number of discrete lines + d % interpolation = interp(i) + d % n_discrete = n_discrete(i) + + ! Allocate arrays for energies and PDF/CDF + allocate(d % e_out(n)) + allocate(d % p(n)) + allocate(d % c(n)) + allocate(d % r(n)) + allocate(d % a(n)) + + ! Copy data + d % e_out(:) = eout(j+1:j+n, 1) + d % p(:) = eout(j+1:j+n, 2) + d % c(:) = eout(j+1:j+n, 3) + d % r(:) = eout(j+1:j+n, 4) + d % a(:) = eout(j+1:j+n, 5) + + + ! To get answers that match ACE data, for now we still use the tabulated + ! CDF values that were passed through to the HDF5 library. At a later + ! time, we can remove the CDF values from the HDF5 library and + ! reconstruct them using the PDF + if (.false.) then + ! Calculate cumulative distribution function -- discrete portion + do k = 1, d % n_discrete + if (k == 1) then + d % c(k) = d % p(k) + else + d % c(k) = d % c(k-1) + d % p(k) + end if + end do + + ! Continuous portion + do k = d % n_discrete + 1, n + if (k == d % n_discrete + 1) then + d % c(k) = sum(d % p(1:d % n_discrete)) + else + if (d % interpolation == HISTOGRAM) then + d % c(k) = d % c(k-1) + d % p(k-1) * & + (d % e_out(k) - d % e_out(k-1)) + elseif (d % interpolation == LINEAR_LINEAR) then + d % c(k) = d % c(k-1) + HALF*(d % p(k-1) + d % p(k)) * & + (d % e_out(k) - d % e_out(k-1)) + end if + end if + end do + + ! Normalize density and distribution functions + d % p(:) = d % p(:)/d % c(n) + d % c(:) = d % c(:)/d % c(n) + end if + end associate + + j = j + n + end do + end subroutine kalbachmann_from_hdf5 + end module secondary_kalbach diff --git a/src/secondary_nbody.F90 b/src/secondary_nbody.F90 index 71cae6fa2..d45c59879 100644 --- a/src/secondary_nbody.F90 +++ b/src/secondary_nbody.F90 @@ -2,6 +2,8 @@ module secondary_nbody use angleenergy_header, only: AngleEnergy use constants, only: ONE, TWO, PI + use hdf5, only: HID_T + use hdf5_interface, only: read_attribute use math, only: maxwell_spectrum use random_lcg, only: prn @@ -18,6 +20,7 @@ module secondary_nbody real(8) :: Q contains procedure :: sample => nbody_sample + procedure :: from_hdf5 => nbody_from_hdf5 end type NBodyPhaseSpace contains @@ -67,4 +70,14 @@ contains E_out = E_max * v end subroutine nbody_sample + subroutine nbody_from_hdf5(this, group_id) + class(NBodyPhaseSpace), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + + call read_attribute(this%mass_ratio, group_id, 'total_mass') + call read_attribute(this%n_bodies, group_id, 'n_particles') + call read_attribute(this%A, group_id, 'atomic_weight_ratio') + call read_attribute(this%Q, group_id, 'q_value') + end subroutine nbody_from_hdf5 + end module secondary_nbody diff --git a/src/secondary_uncorrelated.F90 b/src/secondary_uncorrelated.F90 index 7bc8fa13d..3158913cc 100644 --- a/src/secondary_uncorrelated.F90 +++ b/src/secondary_uncorrelated.F90 @@ -2,8 +2,13 @@ module secondary_uncorrelated use angle_distribution, only: AngleDistribution use angleenergy_header, only: AngleEnergy - use constants, only: ONE, TWO - use energy_distribution, only: EnergyDistribution + use constants, only: ONE, TWO, MAX_WORD_LEN + use energy_distribution, only: EnergyDistribution, LevelInelastic, & + ContinuousTabular, MaxwellEnergy, Evaporation, WattEnergy, DiscretePhoton + use error, only: warning + use h5lt, only: h5ltpath_valid_f + use hdf5, only: HID_T + use hdf5_interface, only: read_attribute, open_group, close_group use random_lcg, only: prn !=============================================================================== @@ -18,6 +23,7 @@ module secondary_uncorrelated class(EnergyDistribution), allocatable :: energy contains procedure :: sample => uncorrelated_sample + procedure :: from_hdf5 => uncorrelated_from_hdf5 end type UncorrelatedAngleEnergy contains @@ -45,4 +51,55 @@ contains E_out = this%energy%sample(E_in) end subroutine uncorrelated_sample + subroutine uncorrelated_from_hdf5(this, group_id) + class(UncorrelatedAngleEnergy), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + + logical :: exists + integer :: hdf5_err + integer(HID_T) :: energy_group + integer(HID_T) :: angle_group + character(MAX_WORD_LEN) :: type + + ! Check if energy group is present + call h5ltpath_valid_f(group_id, 'angle', .true., exists, hdf5_err) + + if (exists) then + angle_group = open_group(group_id, 'angle') + call this%angle%from_hdf5(angle_group) + call close_group(angle_group) + end if + + ! Check if energy group is present + call h5ltpath_valid_f(group_id, 'energy', .true., exists, hdf5_err) + + if (exists) then + energy_group = open_group(group_id, 'energy') + call read_attribute(type, energy_group, 'type') + select case (type) + case ('discrete_photon') + allocate(DiscretePhoton :: this%energy) + case ('level') + allocate(LevelInelastic :: this%energy) + case ('continuous') + allocate(ContinuousTabular :: this%energy) + case ('maxwell') + allocate(MaxwellEnergy :: this%energy) + case ('evaporation') + allocate(Evaporation :: this%energy) + case ('watt') + allocate(WattEnergy :: this%energy) + case default + call warning("Energy distribution type '" // trim(type) & + // "' not implemented.") + end select + + if (allocated(this % energy)) then + call this%energy%from_hdf5(energy_group) + end if + + call close_group(energy_group) + end if + end subroutine uncorrelated_from_hdf5 + end module secondary_uncorrelated diff --git a/src/state_point.F90 b/src/state_point.F90 index f37950134..87ef4ead7 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -43,7 +43,7 @@ contains subroutine write_state_point() integer :: i, j, k - integer :: i_list, i_xs + integer :: i_xs integer :: n_order ! loop index for moment orders integer :: nm_order ! loop index for Ynm moment orders integer, allocatable :: id_array(:) @@ -295,20 +295,11 @@ contains allocate(str_array(tally % n_nuclide_bins)) NUCLIDE_LOOP: do j = 1, tally % n_nuclide_bins if (tally % nuclide_bins(j) > 0) then - ! Get index in cross section listings for this nuclide - if (run_CE) then - i_list = nuclides(tally % nuclide_bins(j)) % listing - else - i_list = nuclides_MG(tally % nuclide_bins(j)) % obj % listing - end if - - ! Determine position of . in alias string (e.g. "U-235.71c"). If - ! no . is found, just use the entire string. - i_xs = index(xs_listings(i_list) % alias, '.') + i_xs = index(nuclides(tally % nuclide_bins(j)) % name, '.') if (i_xs > 0) then - str_array(j) = xs_listings(i_list) % alias(1:i_xs - 1) + str_array(j) = nuclides(tally % nuclide_bins(j)) % name(1 : i_xs-1) else - str_array(j) = xs_listings(i_list) % alias + str_array(j) = nuclides(tally % nuclide_bins(j)) % name end if else str_array(j) = 'total' diff --git a/src/stl_vector.F90 b/src/stl_vector.F90 index 8038628fb..c7f2246ff 100644 --- a/src/stl_vector.F90 +++ b/src/stl_vector.F90 @@ -38,6 +38,7 @@ module stl_vector implicit none private + integer, parameter :: VECTOR_CHAR_LEN = 255 real(8), parameter :: GROWTH_FACTOR = 1.5 type, public :: VectorInt @@ -76,6 +77,24 @@ module stl_vector procedure :: size => size_real end type VectorReal + type, public :: VectorChar + integer, private :: size_ = 0 + integer, private :: capacity_ = 0 + character(VECTOR_CHAR_LEN), allocatable :: data(:) + contains + procedure :: capacity => capacity_char + procedure :: clear => clear_char + generic :: initialize => & + initialize_fill_char + procedure, private :: initialize_fill_char + procedure :: pop_back => pop_back_char + procedure :: push_back => push_back_char + procedure :: reserve => reserve_char + procedure :: resize => resize_char + procedure :: shrink_to_fit => shrink_to_fit_char + procedure :: size => size_char + end type VectorChar + contains !=============================================================================== @@ -348,4 +367,143 @@ contains size = this%size_ end function size_real +!=============================================================================== +! Implementation of VectorChar +!=============================================================================== + + pure function capacity_char(this) result(capacity) + class(VectorChar), intent(in) :: this + integer :: capacity + + capacity = this%capacity_ + end function capacity_char + + subroutine clear_char(this) + class(VectorChar), intent(inout) :: this + + ! Since char is trivially destructible, we only need to set size to zero and + ! can leave capacity as is + this%size_ = 0 + end subroutine clear_char + + subroutine initialize_fill_char(this, n, val) + class(VectorChar), intent(inout) :: this + integer, intent(in) :: n + character(*), optional, intent(in) :: val + + integer :: i + character(VECTOR_CHAR_LEN) :: val_ + + ! If no value given, fill the vector with empty strings + if (present(val)) then + val_ = val + else + val_ = '' + end if + + if (allocated(this%data)) deallocate(this%data) + + allocate(this%data(n)) + do i = 1, n + this%data(i) = val_ + end do + this%size_ = n + this%capacity_ = n + end subroutine initialize_fill_char + + subroutine pop_back_char(this) + class(VectorChar), intent(inout) :: this + if (this%size_ > 0) this%size_ = this%size_ - 1 + end subroutine pop_back_char + + subroutine push_back_char(this, val) + class(VectorChar), intent(inout) :: this + character(*), intent(in) :: val + + integer :: capacity + character(VECTOR_CHAR_LEN), allocatable :: data(:) + + if (this%capacity_ == this%size_) then + ! Create new data array that is GROWTH_FACTOR larger. Note that + if (this%capacity_ == 0) then + capacity = 8 + else + capacity = int(GROWTH_FACTOR*this%capacity_) + end if + allocate(data(capacity)) + + ! Copy existing elements + if (this%size_ > 0) data(1:this%size_) = this%data + + ! Move allocation + call move_alloc(FROM=data, TO=this%data) + this%capacity_ = capacity + end if + + ! Increase size of vector by one and set new element + this%size_ = this%size_ + 1 + this%data(this%size_) = val + end subroutine push_back_char + + subroutine reserve_char(this, n) + class(VectorChar), intent(inout) :: this + integer, intent(in) :: n + + character(VECTOR_CHAR_LEN), allocatable :: data(:) + + if (n > this%capacity_) then + allocate(data(n)) + + ! Copy existing elements + if (this%size_ > 0) data(1:this%size_) = this%data(1:this%size_) + + ! Move allocation + call move_alloc(FROM=data, TO=this%data) + this%capacity_ = n + end if + end subroutine reserve_char + + subroutine resize_char(this, n, val) + class(VectorChar), intent(inout) :: this + integer, intent(in) :: n + character(*), intent(in), optional :: val + + if (n < this%size_) then + this%size_ = n + elseif (n > this%size_) then + ! If requested size is greater than capacity, first reserve that many + ! elements + if (n > this%capacity_) call this%reserve(n) + + ! Fill added elements with specified value and increase size + if (present(val)) this%data(this%size_ + 1 : n) = val + this%size_ = n + end if + + end subroutine resize_char + + subroutine shrink_to_fit_char(this) + class(VectorChar), intent(inout) :: this + + character(VECTOR_CHAR_LEN), allocatable :: data(:) + + if (this%capacity_ > this%size_) then + if (this%size_ > 0) then + allocate(data(this%size_)) + data(:) = this%data(1:this%size_) + call move_alloc(FROM=data, TO=this%data) + this%capacity_ = this%size_ + else + if (allocated(this%data)) deallocate(this%data) + end if + end if + end subroutine shrink_to_fit_char + + pure function size_char(this) result(size) + class(VectorChar), intent(in) :: this + integer :: size + + size = this%size_ + end function size_char + end module stl_vector diff --git a/src/summary.F90 b/src/summary.F90 index 2ec96042c..9ad9aa23c 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -128,11 +128,11 @@ contains allocate(zaids(n_nuclides_total)) do i = 1, n_nuclides_total if (run_CE) then - nucnames(i) = xs_listings(nuclides(i) % listing) % alias + nucnames(i) = nuclides(i) % name awrs(i) = nuclides(i) % awr zaids(i) = nuclides(i) % zaid else - nucnames(i) = xs_listings(nuclides_MG(i) % obj % listing) % alias + nucnames(i) = nuclides_MG(i) % obj % name awrs(i) = nuclides_MG(i) % obj % awr zaids(i) = nuclides_MG(i) % obj % zaid end if @@ -508,8 +508,7 @@ contains integer :: i integer :: j - integer :: i_list - character(12), allocatable :: nucnames(:) + character(20), allocatable :: nucnames(:) integer(HID_T) :: materials_group integer(HID_T) :: material_group type(Material), pointer :: m @@ -540,11 +539,10 @@ contains allocate(nucnames(m%n_nuclides)) do j = 1, m%n_nuclides if (run_CE) then - i_list = nuclides(m%nuclide(j))%listing + nucnames(j) = nuclides(m%nuclide(j))%name else - i_list = nuclides_MG(m%nuclide(j))%obj%listing + nucnames(j) = nuclides_MG(m%nuclide(j))%obj%name end if - nucnames(j) = xs_listings(i_list)%alias end do ! Write temporary array to 'nuclides' @@ -575,7 +573,7 @@ contains integer(HID_T), intent(in) :: file_id integer :: i, j, k - integer :: i_list, i_xs + integer :: i_xs integer :: n_order ! loop index for moment orders integer :: nm_order ! loop index for Ynm moment orders integer(HID_T) :: tallies_group @@ -705,16 +703,11 @@ contains allocate(str_array(t%n_nuclide_bins)) NUCLIDE_LOOP: do j = 1, t%n_nuclide_bins if (t%nuclide_bins(j) > 0) then - if (run_CE) then - i_list = nuclides(t % nuclide_bins(j)) % listing - else - i_list = nuclides_MG(t % nuclide_bins(j)) % obj % listing - end if - i_xs = index(xs_listings(i_list)%alias, '.') + i_xs = index(nuclides(t%nuclide_bins(j))%name, '.') if (i_xs > 0) then - str_array(j) = xs_listings(i_list)%alias(1:i_xs - 1) + str_array(j) = nuclides(t%nuclide_bins(j))%name(1 : i_xs-1) else - str_array(j) = xs_listings(i_list)%alias + str_array(j) = nuclides(t%nuclide_bins(j))%name end if else str_array(j) = 'total' diff --git a/src/urr_header.F90 b/src/urr_header.F90 index 96e182d2e..cc41d43cc 100644 --- a/src/urr_header.F90 +++ b/src/urr_header.F90 @@ -1,5 +1,9 @@ module urr_header + use hdf5, only: HID_T, HSIZE_T + use hdf5_interface, only: read_attribute, open_dataset, read_dataset, & + close_dataset, get_shape + implicit none !=============================================================================== @@ -15,6 +19,55 @@ module urr_header logical :: multiply_smooth ! multiply by smooth cross section? real(8), allocatable :: energy(:) ! incident energies real(8), allocatable :: prob(:,:,:) ! actual probabibility tables + contains + procedure :: from_hdf5 => urr_from_hdf5 end type UrrData +contains + + subroutine urr_from_hdf5(this, group_id) + class(UrrData), intent(inout) :: this + integer(HID_T), intent(in) :: group_id + + integer :: i, j, k + integer(HID_T) :: energy + integer(HID_T) :: table + integer(HSIZE_T) :: dims(1) + integer(HSIZE_T) :: dims3(3) + real(8), allocatable :: temp(:,:,:) + + ! Read interpolation and other flags + call read_attribute(this % interp, group_id, 'interpolation') + call read_attribute(this % inelastic_flag, group_id, 'inelastic') + call read_attribute(this % absorption_flag, group_id, 'absorption') + call read_attribute(i, group_id, 'multiply_smooth') + this % multiply_smooth = (i == 1) + + ! Read energies at which tables exist + energy = open_dataset(group_id, 'energy') + call get_shape(energy, dims) + this % n_energy = int(dims(1), 4) + allocate(this % energy(this % n_energy)) + call read_dataset(this % energy, energy) + call close_dataset(energy) + + ! Read URR tables + table = open_dataset(group_id, 'table') + call get_shape(table, dims3) + this % n_prob = int(dims3(1), 4) + allocate(temp(this % n_prob, 6, this % n_energy)) + call read_dataset(temp, table) + call close_dataset(table) + + ! Swap first and last indices + allocate(this % prob(this % n_energy, 6, this % n_prob)) + do i = 1, this % n_energy + do j = 1, 6 + do k = 1, this % n_prob + this % prob(i, j, k) = temp(k, j, i) + end do + end do + end do + end subroutine urr_from_hdf5 + end module urr_header diff --git a/tests/input_set.py b/tests/input_set.py index 13c0e99a7..827484022 100644 --- a/tests/input_set.py +++ b/tests/input_set.py @@ -24,250 +24,250 @@ class InputSet(object): # Define materials. fuel = openmc.Material(name='Fuel', material_id=1) fuel.set_density('g/cm3', 10.062) - fuel.add_nuclide("U-234", 4.9476e-6) - fuel.add_nuclide("U-235", 4.8218e-4) - fuel.add_nuclide("U-236", 9.0402e-5) - fuel.add_nuclide("U-238", 2.1504e-2) - fuel.add_nuclide("Np-237", 7.3733e-6) - fuel.add_nuclide("Pu-238", 1.5148e-6) - fuel.add_nuclide("Pu-239", 1.3955e-4) - fuel.add_nuclide("Pu-240", 3.4405e-5) - fuel.add_nuclide("Pu-241", 2.1439e-5) - fuel.add_nuclide("Pu-242", 3.7422e-6) - fuel.add_nuclide("Am-241", 4.5041e-7) - fuel.add_nuclide("Am-242m", 9.2301e-9) - fuel.add_nuclide("Am-243", 4.7878e-7) - fuel.add_nuclide("Cm-242", 1.0485e-7) - fuel.add_nuclide("Cm-243", 1.4268e-9) - fuel.add_nuclide("Cm-244", 8.8756e-8) - fuel.add_nuclide("Cm-245", 3.5285e-9) - fuel.add_nuclide("Mo-95", 2.6497e-5) - fuel.add_nuclide("Tc-99", 3.2772e-5) - fuel.add_nuclide("Ru-101", 3.0742e-5) - fuel.add_nuclide("Ru-103", 2.3505e-6) - fuel.add_nuclide("Ag-109", 2.0009e-6) - fuel.add_nuclide("Xe-135", 1.0801e-8) - fuel.add_nuclide("Cs-133", 3.4612e-5) - fuel.add_nuclide("Nd-143", 2.6078e-5) - fuel.add_nuclide("Nd-145", 1.9898e-5) - fuel.add_nuclide("Sm-147", 1.6128e-6) - fuel.add_nuclide("Sm-149", 1.1627e-7) - fuel.add_nuclide("Sm-150", 7.1727e-6) - fuel.add_nuclide("Sm-151", 5.4947e-7) - fuel.add_nuclide("Sm-152", 3.0221e-6) - fuel.add_nuclide("Eu-153", 2.6209e-6) - fuel.add_nuclide("Gd-155", 1.5369e-9) - fuel.add_nuclide("O-16", 4.5737e-2) + fuel.add_nuclide("U234", 4.9476e-6) + fuel.add_nuclide("U235", 4.8218e-4) + fuel.add_nuclide("U236", 9.0402e-5) + fuel.add_nuclide("U238", 2.1504e-2) + fuel.add_nuclide("Np237", 7.3733e-6) + fuel.add_nuclide("Pu238", 1.5148e-6) + fuel.add_nuclide("Pu239", 1.3955e-4) + fuel.add_nuclide("Pu240", 3.4405e-5) + fuel.add_nuclide("Pu241", 2.1439e-5) + fuel.add_nuclide("Pu242", 3.7422e-6) + fuel.add_nuclide("Am241", 4.5041e-7) + fuel.add_nuclide("Am242_m1", 9.2301e-9) + fuel.add_nuclide("Am243", 4.7878e-7) + fuel.add_nuclide("Cm242", 1.0485e-7) + fuel.add_nuclide("Cm243", 1.4268e-9) + fuel.add_nuclide("Cm244", 8.8756e-8) + fuel.add_nuclide("Cm245", 3.5285e-9) + fuel.add_nuclide("Mo95", 2.6497e-5) + fuel.add_nuclide("Tc99", 3.2772e-5) + fuel.add_nuclide("Ru101", 3.0742e-5) + fuel.add_nuclide("Ru103", 2.3505e-6) + fuel.add_nuclide("Ag109", 2.0009e-6) + fuel.add_nuclide("Xe135", 1.0801e-8) + fuel.add_nuclide("Cs133", 3.4612e-5) + fuel.add_nuclide("Nd143", 2.6078e-5) + fuel.add_nuclide("Nd145", 1.9898e-5) + fuel.add_nuclide("Sm147", 1.6128e-6) + fuel.add_nuclide("Sm149", 1.1627e-7) + fuel.add_nuclide("Sm150", 7.1727e-6) + fuel.add_nuclide("Sm151", 5.4947e-7) + fuel.add_nuclide("Sm152", 3.0221e-6) + fuel.add_nuclide("Eu153", 2.6209e-6) + fuel.add_nuclide("Gd155", 1.5369e-9) + fuel.add_nuclide("O16", 4.5737e-2) clad = openmc.Material(name='Cladding', material_id=2) clad.set_density('g/cm3', 5.77) - clad.add_nuclide("Zr-90", 0.5145) - clad.add_nuclide("Zr-91", 0.1122) - clad.add_nuclide("Zr-92", 0.1715) - clad.add_nuclide("Zr-94", 0.1738) - clad.add_nuclide("Zr-96", 0.0280) + clad.add_nuclide("Zr90", 0.5145) + clad.add_nuclide("Zr91", 0.1122) + clad.add_nuclide("Zr92", 0.1715) + clad.add_nuclide("Zr94", 0.1738) + clad.add_nuclide("Zr96", 0.0280) cold_water = openmc.Material(name='Cold borated water', material_id=3) cold_water.set_density('atom/b-cm', 0.07416) - cold_water.add_nuclide("H-1", 2.0) - cold_water.add_nuclide("O-16", 1.0) - cold_water.add_nuclide("B-10", 6.490e-4) - cold_water.add_nuclide("B-11", 2.689e-3) - cold_water.add_s_alpha_beta('HH2O', '71t') + cold_water.add_nuclide("H1", 2.0) + cold_water.add_nuclide("O16", 1.0) + cold_water.add_nuclide("B10", 6.490e-4) + cold_water.add_nuclide("B11", 2.689e-3) + cold_water.add_s_alpha_beta('c_H_in_H2O', '71t') hot_water = openmc.Material(name='Hot borated water', material_id=4) hot_water.set_density('atom/b-cm', 0.06614) - hot_water.add_nuclide("H-1", 2.0) - hot_water.add_nuclide("O-16", 1.0) - hot_water.add_nuclide("B-10", 6.490e-4) - hot_water.add_nuclide("B-11", 2.689e-3) - hot_water.add_s_alpha_beta('HH2O', '71t') + hot_water.add_nuclide("H1", 2.0) + hot_water.add_nuclide("O16", 1.0) + hot_water.add_nuclide("B10", 6.490e-4) + hot_water.add_nuclide("B11", 2.689e-3) + hot_water.add_s_alpha_beta('c_H_in_H2O', '71t') rpv_steel = openmc.Material(name='Reactor pressure vessel steel', material_id=5) rpv_steel.set_density('g/cm3', 7.9) - rpv_steel.add_nuclide("Fe-54", 0.05437098, 'wo') - rpv_steel.add_nuclide("Fe-56", 0.88500663, 'wo') - rpv_steel.add_nuclide("Fe-57", 0.0208008, 'wo') - rpv_steel.add_nuclide("Fe-58", 0.00282159, 'wo') - rpv_steel.add_nuclide("Ni-58", 0.0067198, 'wo') - rpv_steel.add_nuclide("Ni-60", 0.0026776, 'wo') - rpv_steel.add_nuclide("Ni-61", 0.0001183, 'wo') - rpv_steel.add_nuclide("Ni-62", 0.0003835, 'wo') - rpv_steel.add_nuclide("Ni-64", 0.0001008, 'wo') - rpv_steel.add_nuclide("Mn-55", 0.01, 'wo') - rpv_steel.add_nuclide("Mo-92", 0.000849, 'wo') - rpv_steel.add_nuclide("Mo-94", 0.0005418, 'wo') - rpv_steel.add_nuclide("Mo-95", 0.0009438, 'wo') - rpv_steel.add_nuclide("Mo-96", 0.0010002, 'wo') - rpv_steel.add_nuclide("Mo-97", 0.0005796, 'wo') - rpv_steel.add_nuclide("Mo-98", 0.0014814, 'wo') - rpv_steel.add_nuclide("Mo-100", 0.0006042, 'wo') - rpv_steel.add_nuclide("Si-28", 0.00367464, 'wo') - rpv_steel.add_nuclide("Si-29", 0.00019336, 'wo') - rpv_steel.add_nuclide("Si-30", 0.000132, 'wo') - rpv_steel.add_nuclide("Cr-50", 0.00010435, 'wo') - rpv_steel.add_nuclide("Cr-52", 0.002092475, 'wo') - rpv_steel.add_nuclide("Cr-53", 0.00024185, 'wo') - rpv_steel.add_nuclide("Cr-54", 6.1325e-05, 'wo') - rpv_steel.add_nuclide("C-Nat", 0.0025, 'wo') - rpv_steel.add_nuclide("Cu-63", 0.0013696, 'wo') - rpv_steel.add_nuclide("Cu-65", 0.0006304, 'wo') + rpv_steel.add_nuclide("Fe54", 0.05437098, 'wo') + rpv_steel.add_nuclide("Fe56", 0.88500663, 'wo') + rpv_steel.add_nuclide("Fe57", 0.0208008, 'wo') + rpv_steel.add_nuclide("Fe58", 0.00282159, 'wo') + rpv_steel.add_nuclide("Ni58", 0.0067198, 'wo') + rpv_steel.add_nuclide("Ni60", 0.0026776, 'wo') + rpv_steel.add_nuclide("Ni61", 0.0001183, 'wo') + rpv_steel.add_nuclide("Ni62", 0.0003835, 'wo') + rpv_steel.add_nuclide("Ni64", 0.0001008, 'wo') + rpv_steel.add_nuclide("Mn55", 0.01, 'wo') + rpv_steel.add_nuclide("Mo92", 0.000849, 'wo') + rpv_steel.add_nuclide("Mo94", 0.0005418, 'wo') + rpv_steel.add_nuclide("Mo95", 0.0009438, 'wo') + rpv_steel.add_nuclide("Mo96", 0.0010002, 'wo') + rpv_steel.add_nuclide("Mo97", 0.0005796, 'wo') + rpv_steel.add_nuclide("Mo98", 0.0014814, 'wo') + rpv_steel.add_nuclide("Mo100", 0.0006042, 'wo') + rpv_steel.add_nuclide("Si28", 0.00367464, 'wo') + rpv_steel.add_nuclide("Si29", 0.00019336, 'wo') + rpv_steel.add_nuclide("Si30", 0.000132, 'wo') + rpv_steel.add_nuclide("Cr50", 0.00010435, 'wo') + rpv_steel.add_nuclide("Cr52", 0.002092475, 'wo') + rpv_steel.add_nuclide("Cr53", 0.00024185, 'wo') + rpv_steel.add_nuclide("Cr54", 6.1325e-05, 'wo') + rpv_steel.add_nuclide("C0", 0.0025, 'wo') + rpv_steel.add_nuclide("Cu63", 0.0013696, 'wo') + rpv_steel.add_nuclide("Cu65", 0.0006304, 'wo') lower_rad_ref = openmc.Material(name='Lower radial reflector', material_id=6) lower_rad_ref.set_density('g/cm3', 4.32) - lower_rad_ref.add_nuclide("H-1", 0.0095661, 'wo') - lower_rad_ref.add_nuclide("O-16", 0.0759107, 'wo') - lower_rad_ref.add_nuclide("B-10", 3.08409e-5, 'wo') - lower_rad_ref.add_nuclide("B-11", 1.40499e-4, 'wo') - lower_rad_ref.add_nuclide("Fe-54", 0.035620772088, 'wo') - lower_rad_ref.add_nuclide("Fe-56", 0.579805982228, 'wo') - lower_rad_ref.add_nuclide("Fe-57", 0.01362750048, 'wo') - lower_rad_ref.add_nuclide("Fe-58", 0.001848545204, 'wo') - lower_rad_ref.add_nuclide("Ni-58", 0.055298376566, 'wo') - lower_rad_ref.add_nuclide("Ni-60", 0.022034425592, 'wo') - lower_rad_ref.add_nuclide("Ni-61", 0.000973510811, 'wo') - lower_rad_ref.add_nuclide("Ni-62", 0.003155886695, 'wo') - lower_rad_ref.add_nuclide("Ni-64", 0.000829500336, 'wo') - lower_rad_ref.add_nuclide("Mn-55", 0.0182870, 'wo') - lower_rad_ref.add_nuclide("Si-28", 0.00839976771, 'wo') - lower_rad_ref.add_nuclide("Si-29", 0.00044199679, 'wo') - lower_rad_ref.add_nuclide("Si-30", 0.0003017355, 'wo') - lower_rad_ref.add_nuclide("Cr-50", 0.007251360806, 'wo') - lower_rad_ref.add_nuclide("Cr-52", 0.145407678031, 'wo') - lower_rad_ref.add_nuclide("Cr-53", 0.016806340306, 'wo') - lower_rad_ref.add_nuclide("Cr-54", 0.004261520857, 'wo') - lower_rad_ref.add_s_alpha_beta('HH2O', '71t') + lower_rad_ref.add_nuclide("H1", 0.0095661, 'wo') + lower_rad_ref.add_nuclide("O16", 0.0759107, 'wo') + lower_rad_ref.add_nuclide("B10", 3.08409e-5, 'wo') + lower_rad_ref.add_nuclide("B11", 1.40499e-4, 'wo') + lower_rad_ref.add_nuclide("Fe54", 0.035620772088, 'wo') + lower_rad_ref.add_nuclide("Fe56", 0.579805982228, 'wo') + lower_rad_ref.add_nuclide("Fe57", 0.01362750048, 'wo') + lower_rad_ref.add_nuclide("Fe58", 0.001848545204, 'wo') + lower_rad_ref.add_nuclide("Ni58", 0.055298376566, 'wo') + lower_rad_ref.add_nuclide("Ni60", 0.022034425592, 'wo') + lower_rad_ref.add_nuclide("Ni61", 0.000973510811, 'wo') + lower_rad_ref.add_nuclide("Ni62", 0.003155886695, 'wo') + lower_rad_ref.add_nuclide("Ni64", 0.000829500336, 'wo') + lower_rad_ref.add_nuclide("Mn55", 0.0182870, 'wo') + lower_rad_ref.add_nuclide("Si28", 0.00839976771, 'wo') + lower_rad_ref.add_nuclide("Si29", 0.00044199679, 'wo') + lower_rad_ref.add_nuclide("Si30", 0.0003017355, 'wo') + lower_rad_ref.add_nuclide("Cr50", 0.007251360806, 'wo') + lower_rad_ref.add_nuclide("Cr52", 0.145407678031, 'wo') + lower_rad_ref.add_nuclide("Cr53", 0.016806340306, 'wo') + lower_rad_ref.add_nuclide("Cr54", 0.004261520857, 'wo') + lower_rad_ref.add_s_alpha_beta('c_H_in_H2O', '71t') upper_rad_ref = openmc.Material(name='Upper radial reflector /' 'Top plate region', material_id=7) upper_rad_ref.set_density('g/cm3', 4.28) - upper_rad_ref.add_nuclide("H-1", 0.0086117, 'wo') - upper_rad_ref.add_nuclide("O-16", 0.0683369, 'wo') - upper_rad_ref.add_nuclide("B-10", 2.77638e-5, 'wo') - upper_rad_ref.add_nuclide("B-11", 1.26481e-4, 'wo') - upper_rad_ref.add_nuclide("Fe-54", 0.035953677186, 'wo') - upper_rad_ref.add_nuclide("Fe-56", 0.585224740891, 'wo') - upper_rad_ref.add_nuclide("Fe-57", 0.01375486056, 'wo') - upper_rad_ref.add_nuclide("Fe-58", 0.001865821363, 'wo') - upper_rad_ref.add_nuclide("Ni-58", 0.055815129186, 'wo') - upper_rad_ref.add_nuclide("Ni-60", 0.022240333032, 'wo') - upper_rad_ref.add_nuclide("Ni-61", 0.000982608081, 'wo') - upper_rad_ref.add_nuclide("Ni-62", 0.003185377845, 'wo') - upper_rad_ref.add_nuclide("Ni-64", 0.000837251856, 'wo') - upper_rad_ref.add_nuclide("Mn-55", 0.0184579, 'wo') - upper_rad_ref.add_nuclide("Si-28", 0.00847831314, 'wo') - upper_rad_ref.add_nuclide("Si-29", 0.00044612986, 'wo') - upper_rad_ref.add_nuclide("Si-30", 0.000304557, 'wo') - upper_rad_ref.add_nuclide("Cr-50", 0.00731912987, 'wo') - upper_rad_ref.add_nuclide("Cr-52", 0.146766614995, 'wo') - upper_rad_ref.add_nuclide("Cr-53", 0.01696340737, 'wo') - upper_rad_ref.add_nuclide("Cr-54", 0.004301347765, 'wo') - upper_rad_ref.add_s_alpha_beta('HH2O', '71t') + upper_rad_ref.add_nuclide("H1", 0.0086117, 'wo') + upper_rad_ref.add_nuclide("O16", 0.0683369, 'wo') + upper_rad_ref.add_nuclide("B10", 2.77638e-5, 'wo') + upper_rad_ref.add_nuclide("B11", 1.26481e-4, 'wo') + upper_rad_ref.add_nuclide("Fe54", 0.035953677186, 'wo') + upper_rad_ref.add_nuclide("Fe56", 0.585224740891, 'wo') + upper_rad_ref.add_nuclide("Fe57", 0.01375486056, 'wo') + upper_rad_ref.add_nuclide("Fe58", 0.001865821363, 'wo') + upper_rad_ref.add_nuclide("Ni58", 0.055815129186, 'wo') + upper_rad_ref.add_nuclide("Ni60", 0.022240333032, 'wo') + upper_rad_ref.add_nuclide("Ni61", 0.000982608081, 'wo') + upper_rad_ref.add_nuclide("Ni62", 0.003185377845, 'wo') + upper_rad_ref.add_nuclide("Ni64", 0.000837251856, 'wo') + upper_rad_ref.add_nuclide("Mn55", 0.0184579, 'wo') + upper_rad_ref.add_nuclide("Si28", 0.00847831314, 'wo') + upper_rad_ref.add_nuclide("Si29", 0.00044612986, 'wo') + upper_rad_ref.add_nuclide("Si30", 0.000304557, 'wo') + upper_rad_ref.add_nuclide("Cr50", 0.00731912987, 'wo') + upper_rad_ref.add_nuclide("Cr52", 0.146766614995, 'wo') + upper_rad_ref.add_nuclide("Cr53", 0.01696340737, 'wo') + upper_rad_ref.add_nuclide("Cr54", 0.004301347765, 'wo') + upper_rad_ref.add_s_alpha_beta('c_H_in_H2O', '71t') bot_plate = openmc.Material(name='Bottom plate region', material_id=8) bot_plate.set_density('g/cm3', 7.184) - bot_plate.add_nuclide("H-1", 0.0011505, 'wo') - bot_plate.add_nuclide("O-16", 0.0091296, 'wo') - bot_plate.add_nuclide("B-10", 3.70915e-6, 'wo') - bot_plate.add_nuclide("B-11", 1.68974e-5, 'wo') - bot_plate.add_nuclide("Fe-54", 0.03855611055, 'wo') - bot_plate.add_nuclide("Fe-56", 0.627585036425, 'wo') - bot_plate.add_nuclide("Fe-57", 0.014750478, 'wo') - bot_plate.add_nuclide("Fe-58", 0.002000875025, 'wo') - bot_plate.add_nuclide("Ni-58", 0.059855207342, 'wo') - bot_plate.add_nuclide("Ni-60", 0.023850159704, 'wo') - bot_plate.add_nuclide("Ni-61", 0.001053732407, 'wo') - bot_plate.add_nuclide("Ni-62", 0.003415945715, 'wo') - bot_plate.add_nuclide("Ni-64", 0.000897854832, 'wo') - bot_plate.add_nuclide("Mn-55", 0.0197940, 'wo') - bot_plate.add_nuclide("Si-28", 0.00909197802, 'wo') - bot_plate.add_nuclide("Si-29", 0.00047842098, 'wo') - bot_plate.add_nuclide("Si-30", 0.000326601, 'wo') - bot_plate.add_nuclide("Cr-50", 0.007848910646, 'wo') - bot_plate.add_nuclide("Cr-52", 0.157390026871, 'wo') - bot_plate.add_nuclide("Cr-53", 0.018191270146, 'wo') - bot_plate.add_nuclide("Cr-54", 0.004612692337, 'wo') - bot_plate.add_s_alpha_beta('HH2O', '71t') + bot_plate.add_nuclide("H1", 0.0011505, 'wo') + bot_plate.add_nuclide("O16", 0.0091296, 'wo') + bot_plate.add_nuclide("B10", 3.70915e-6, 'wo') + bot_plate.add_nuclide("B11", 1.68974e-5, 'wo') + bot_plate.add_nuclide("Fe54", 0.03855611055, 'wo') + bot_plate.add_nuclide("Fe56", 0.627585036425, 'wo') + bot_plate.add_nuclide("Fe57", 0.014750478, 'wo') + bot_plate.add_nuclide("Fe58", 0.002000875025, 'wo') + bot_plate.add_nuclide("Ni58", 0.059855207342, 'wo') + bot_plate.add_nuclide("Ni60", 0.023850159704, 'wo') + bot_plate.add_nuclide("Ni61", 0.001053732407, 'wo') + bot_plate.add_nuclide("Ni62", 0.003415945715, 'wo') + bot_plate.add_nuclide("Ni64", 0.000897854832, 'wo') + bot_plate.add_nuclide("Mn55", 0.0197940, 'wo') + bot_plate.add_nuclide("Si28", 0.00909197802, 'wo') + bot_plate.add_nuclide("Si29", 0.00047842098, 'wo') + bot_plate.add_nuclide("Si30", 0.000326601, 'wo') + bot_plate.add_nuclide("Cr50", 0.007848910646, 'wo') + bot_plate.add_nuclide("Cr52", 0.157390026871, 'wo') + bot_plate.add_nuclide("Cr53", 0.018191270146, 'wo') + bot_plate.add_nuclide("Cr54", 0.004612692337, 'wo') + bot_plate.add_s_alpha_beta('c_H_in_H2O', '71t') bot_nozzle = openmc.Material(name='Bottom nozzle region', material_id=9) bot_nozzle.set_density('g/cm3', 2.53) - bot_nozzle.add_nuclide("H-1", 0.0245014, 'wo') - bot_nozzle.add_nuclide("O-16", 0.1944274, 'wo') - bot_nozzle.add_nuclide("B-10", 7.89917e-5, 'wo') - bot_nozzle.add_nuclide("B-11", 3.59854e-4, 'wo') - bot_nozzle.add_nuclide("Fe-54", 0.030411411144, 'wo') - bot_nozzle.add_nuclide("Fe-56", 0.495012237964, 'wo') - bot_nozzle.add_nuclide("Fe-57", 0.01163454624, 'wo') - bot_nozzle.add_nuclide("Fe-58", 0.001578204652, 'wo') - bot_nozzle.add_nuclide("Ni-58", 0.047211231662, 'wo') - bot_nozzle.add_nuclide("Ni-60", 0.018811987544, 'wo') - bot_nozzle.add_nuclide("Ni-61", 0.000831139127, 'wo') - bot_nozzle.add_nuclide("Ni-62", 0.002694352115, 'wo') - bot_nozzle.add_nuclide("Ni-64", 0.000708189552, 'wo') - bot_nozzle.add_nuclide("Mn-55", 0.0156126, 'wo') - bot_nozzle.add_nuclide("Si-28", 0.007171335558, 'wo') - bot_nozzle.add_nuclide("Si-29", 0.000377356542, 'wo') - bot_nozzle.add_nuclide("Si-30", 0.0002576079, 'wo') - bot_nozzle.add_nuclide("Cr-50", 0.006190885148, 'wo') - bot_nozzle.add_nuclide("Cr-52", 0.124142524198, 'wo') - bot_nozzle.add_nuclide("Cr-53", 0.014348496148, 'wo') - bot_nozzle.add_nuclide("Cr-54", 0.003638294506, 'wo') - bot_nozzle.add_s_alpha_beta('HH2O', '71t') + bot_nozzle.add_nuclide("H1", 0.0245014, 'wo') + bot_nozzle.add_nuclide("O16", 0.1944274, 'wo') + bot_nozzle.add_nuclide("B10", 7.89917e-5, 'wo') + bot_nozzle.add_nuclide("B11", 3.59854e-4, 'wo') + bot_nozzle.add_nuclide("Fe54", 0.030411411144, 'wo') + bot_nozzle.add_nuclide("Fe56", 0.495012237964, 'wo') + bot_nozzle.add_nuclide("Fe57", 0.01163454624, 'wo') + bot_nozzle.add_nuclide("Fe58", 0.001578204652, 'wo') + bot_nozzle.add_nuclide("Ni58", 0.047211231662, 'wo') + bot_nozzle.add_nuclide("Ni60", 0.018811987544, 'wo') + bot_nozzle.add_nuclide("Ni61", 0.000831139127, 'wo') + bot_nozzle.add_nuclide("Ni62", 0.002694352115, 'wo') + bot_nozzle.add_nuclide("Ni64", 0.000708189552, 'wo') + bot_nozzle.add_nuclide("Mn55", 0.0156126, 'wo') + bot_nozzle.add_nuclide("Si28", 0.007171335558, 'wo') + bot_nozzle.add_nuclide("Si29", 0.000377356542, 'wo') + bot_nozzle.add_nuclide("Si30", 0.0002576079, 'wo') + bot_nozzle.add_nuclide("Cr50", 0.006190885148, 'wo') + bot_nozzle.add_nuclide("Cr52", 0.124142524198, 'wo') + bot_nozzle.add_nuclide("Cr53", 0.014348496148, 'wo') + bot_nozzle.add_nuclide("Cr54", 0.003638294506, 'wo') + bot_nozzle.add_s_alpha_beta('c_H_in_H2O', '71t') top_nozzle = openmc.Material(name='Top nozzle region', material_id=10) top_nozzle.set_density('g/cm3', 1.746) - top_nozzle.add_nuclide("H-1", 0.0358870, 'wo') - top_nozzle.add_nuclide("O-16", 0.2847761, 'wo') - top_nozzle.add_nuclide("B-10", 1.15699e-4, 'wo') - top_nozzle.add_nuclide("B-11", 5.27075e-4, 'wo') - top_nozzle.add_nuclide("Fe-54", 0.02644016154, 'wo') - top_nozzle.add_nuclide("Fe-56", 0.43037146399, 'wo') - top_nozzle.add_nuclide("Fe-57", 0.0101152584, 'wo') - top_nozzle.add_nuclide("Fe-58", 0.00137211607, 'wo') - top_nozzle.add_nuclide("Ni-58", 0.04104621835, 'wo') - top_nozzle.add_nuclide("Ni-60", 0.0163554502, 'wo') - top_nozzle.add_nuclide("Ni-61", 0.000722605975, 'wo') - top_nozzle.add_nuclide("Ni-62", 0.002342513875, 'wo') - top_nozzle.add_nuclide("Ni-64", 0.0006157116, 'wo') - top_nozzle.add_nuclide("Mn-55", 0.0135739, 'wo') - top_nozzle.add_nuclide("Si-28", 0.006234853554, 'wo') - top_nozzle.add_nuclide("Si-29", 0.000328078746, 'wo') - top_nozzle.add_nuclide("Si-30", 0.0002239677, 'wo') - top_nozzle.add_nuclide("Cr-50", 0.005382452306, 'wo') - top_nozzle.add_nuclide("Cr-52", 0.107931450781, 'wo') - top_nozzle.add_nuclide("Cr-53", 0.012474806806, 'wo') - top_nozzle.add_nuclide("Cr-54", 0.003163190107, 'wo') - top_nozzle.add_s_alpha_beta('HH2O', '71t') + top_nozzle.add_nuclide("H1", 0.0358870, 'wo') + top_nozzle.add_nuclide("O16", 0.2847761, 'wo') + top_nozzle.add_nuclide("B10", 1.15699e-4, 'wo') + top_nozzle.add_nuclide("B11", 5.27075e-4, 'wo') + top_nozzle.add_nuclide("Fe54", 0.02644016154, 'wo') + top_nozzle.add_nuclide("Fe56", 0.43037146399, 'wo') + top_nozzle.add_nuclide("Fe57", 0.0101152584, 'wo') + top_nozzle.add_nuclide("Fe58", 0.00137211607, 'wo') + top_nozzle.add_nuclide("Ni58", 0.04104621835, 'wo') + top_nozzle.add_nuclide("Ni60", 0.0163554502, 'wo') + top_nozzle.add_nuclide("Ni61", 0.000722605975, 'wo') + top_nozzle.add_nuclide("Ni62", 0.002342513875, 'wo') + top_nozzle.add_nuclide("Ni64", 0.0006157116, 'wo') + top_nozzle.add_nuclide("Mn55", 0.0135739, 'wo') + top_nozzle.add_nuclide("Si28", 0.006234853554, 'wo') + top_nozzle.add_nuclide("Si29", 0.000328078746, 'wo') + top_nozzle.add_nuclide("Si30", 0.0002239677, 'wo') + top_nozzle.add_nuclide("Cr50", 0.005382452306, 'wo') + top_nozzle.add_nuclide("Cr52", 0.107931450781, 'wo') + top_nozzle.add_nuclide("Cr53", 0.012474806806, 'wo') + top_nozzle.add_nuclide("Cr54", 0.003163190107, 'wo') + top_nozzle.add_s_alpha_beta('c_H_in_H2O', '71t') top_fa = openmc.Material(name='Top of fuel assemblies', material_id=11) top_fa.set_density('g/cm3', 3.044) - top_fa.add_nuclide("H-1", 0.0162913, 'wo') - top_fa.add_nuclide("O-16", 0.1292776, 'wo') - top_fa.add_nuclide("B-10", 5.25228e-5, 'wo') - top_fa.add_nuclide("B-11", 2.39272e-4, 'wo') - top_fa.add_nuclide("Zr-90", 0.43313403903, 'wo') - top_fa.add_nuclide("Zr-91", 0.09549277374, 'wo') - top_fa.add_nuclide("Zr-92", 0.14759527104, 'wo') - top_fa.add_nuclide("Zr-94", 0.15280552077, 'wo') - top_fa.add_nuclide("Zr-96", 0.02511169542, 'wo') - top_fa.add_s_alpha_beta('HH2O', '71t') + top_fa.add_nuclide("H1", 0.0162913, 'wo') + top_fa.add_nuclide("O16", 0.1292776, 'wo') + top_fa.add_nuclide("B10", 5.25228e-5, 'wo') + top_fa.add_nuclide("B11", 2.39272e-4, 'wo') + top_fa.add_nuclide("Zr90", 0.43313403903, 'wo') + top_fa.add_nuclide("Zr91", 0.09549277374, 'wo') + top_fa.add_nuclide("Zr92", 0.14759527104, 'wo') + top_fa.add_nuclide("Zr94", 0.15280552077, 'wo') + top_fa.add_nuclide("Zr96", 0.02511169542, 'wo') + top_fa.add_s_alpha_beta('c_H_in_H2O', '71t') bot_fa = openmc.Material(name='Bottom of fuel assemblies', material_id=12) bot_fa.set_density('g/cm3', 1.762) - bot_fa.add_nuclide("H-1", 0.0292856, 'wo') - bot_fa.add_nuclide("O-16", 0.2323919, 'wo') - bot_fa.add_nuclide("B-10", 9.44159e-5, 'wo') - bot_fa.add_nuclide("B-11", 4.30120e-4, 'wo') - bot_fa.add_nuclide("Zr-90", 0.3741373658, 'wo') - bot_fa.add_nuclide("Zr-91", 0.0824858164, 'wo') - bot_fa.add_nuclide("Zr-92", 0.1274914944, 'wo') - bot_fa.add_nuclide("Zr-94", 0.1319920622, 'wo') - bot_fa.add_nuclide("Zr-96", 0.0216912612, 'wo') - bot_fa.add_s_alpha_beta('HH2O', '71t') + bot_fa.add_nuclide("H1", 0.0292856, 'wo') + bot_fa.add_nuclide("O16", 0.2323919, 'wo') + bot_fa.add_nuclide("B10", 9.44159e-5, 'wo') + bot_fa.add_nuclide("B11", 4.30120e-4, 'wo') + bot_fa.add_nuclide("Zr90", 0.3741373658, 'wo') + bot_fa.add_nuclide("Zr91", 0.0824858164, 'wo') + bot_fa.add_nuclide("Zr92", 0.1274914944, 'wo') + bot_fa.add_nuclide("Zr94", 0.1319920622, 'wo') + bot_fa.add_nuclide("Zr96", 0.0216912612, 'wo') + bot_fa.add_s_alpha_beta('c_H_in_H2O', '71t') # Define the materials file. self.materials.default_xs = '71c' @@ -592,26 +592,26 @@ class PinCellInputSet(object): # Define materials. fuel = openmc.Material(name='Fuel') fuel.set_density('g/cm3', 10.29769) - fuel.add_nuclide("U-234", 4.4843e-6) - fuel.add_nuclide("U-235", 5.5815e-4) - fuel.add_nuclide("U-238", 2.2408e-2) - fuel.add_nuclide("O-16", 4.5829e-2) + fuel.add_nuclide("U234", 4.4843e-6) + fuel.add_nuclide("U235", 5.5815e-4) + fuel.add_nuclide("U238", 2.2408e-2) + fuel.add_nuclide("O16", 4.5829e-2) clad = openmc.Material(name='Cladding') clad.set_density('g/cm3', 6.55) - clad.add_nuclide("Zr-90", 2.1827e-2) - clad.add_nuclide("Zr-91", 4.7600e-3) - clad.add_nuclide("Zr-92", 7.2758e-3) - clad.add_nuclide("Zr-94", 7.3734e-3) - clad.add_nuclide("Zr-96", 1.1879e-3) + clad.add_nuclide("Zr90", 2.1827e-2) + clad.add_nuclide("Zr91", 4.7600e-3) + clad.add_nuclide("Zr92", 7.2758e-3) + clad.add_nuclide("Zr94", 7.3734e-3) + clad.add_nuclide("Zr96", 1.1879e-3) hot_water = openmc.Material(name='Hot borated water') hot_water.set_density('g/cm3', 0.740582) - hot_water.add_nuclide("H-1", 4.9457e-2) - hot_water.add_nuclide("O-16", 2.4672e-2) - hot_water.add_nuclide("B-10", 8.0042e-6) - hot_water.add_nuclide("B-11", 3.2218e-5) - hot_water.add_s_alpha_beta('HH2O', '71t') + hot_water.add_nuclide("H1", 4.9457e-2) + hot_water.add_nuclide("O16", 2.4672e-2) + hot_water.add_nuclide("B10", 8.0042e-6) + hot_water.add_nuclide("B11", 3.2218e-5) + hot_water.add_s_alpha_beta('c_H_in_H2O', '71t') # Define the materials file. self.materials.default_xs = '71c' diff --git a/tests/test_asymmetric_lattice/inputs_true.dat b/tests/test_asymmetric_lattice/inputs_true.dat index f40e661b3..d503a3a0b 100644 --- a/tests/test_asymmetric_lattice/inputs_true.dat +++ b/tests/test_asymmetric_lattice/inputs_true.dat @@ -1 +1 @@ -9b859eb5501c05b6a652d299bd0cadc0a924ffae31117babbdc9f7f8ca87689322c275818eb0dde0ff5fa78317d8d8f1585b18dcc772e3ff4ed499de8a491dc3 \ No newline at end of file +a55899cd2ed0a8ec5d44003139da639f87f5f03ee76b2d6577db6a8c2014849e4277f8e68fa874ac6795e4cbc4eb6e4031d726cafe6e663e84787d1ecd8e7f86 \ No newline at end of file diff --git a/tests/test_cmfd_feed/materials.xml b/tests/test_cmfd_feed/materials.xml index 7b4baf9e1..8f32169d9 100644 --- a/tests/test_cmfd_feed/materials.xml +++ b/tests/test_cmfd_feed/materials.xml @@ -4,9 +4,9 @@ - - - + + + diff --git a/tests/test_cmfd_nofeed/materials.xml b/tests/test_cmfd_nofeed/materials.xml index 7b4baf9e1..8f32169d9 100644 --- a/tests/test_cmfd_nofeed/materials.xml +++ b/tests/test_cmfd_nofeed/materials.xml @@ -4,9 +4,9 @@ - - - + + + diff --git a/tests/test_complex_cell/materials.xml b/tests/test_complex_cell/materials.xml index 60ec99673..a9e69b8bc 100644 --- a/tests/test_complex_cell/materials.xml +++ b/tests/test_complex_cell/materials.xml @@ -5,11 +5,11 @@ - + - + diff --git a/tests/test_confidence_intervals/materials.xml b/tests/test_confidence_intervals/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_confidence_intervals/materials.xml +++ b/tests/test_confidence_intervals/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_density/materials.xml b/tests/test_density/materials.xml index 36947d066..c474c5c65 100644 --- a/tests/test_density/materials.xml +++ b/tests/test_density/materials.xml @@ -3,24 +3,24 @@ - + - + - + - - - + + + diff --git a/tests/test_distribmat/inputs_true.dat b/tests/test_distribmat/inputs_true.dat index 9c8a86bfa..1212a28e5 100644 --- a/tests/test_distribmat/inputs_true.dat +++ b/tests/test_distribmat/inputs_true.dat @@ -1 +1 @@ -96c54eb4f1da175445bf2187449ee32c9ff435d8c60e9421a4a16497aae9f233e3e494f531892dd55f6ac1a06e0240799503ff19e14e2436a0b0f0d83ba56cb8 \ No newline at end of file +46df57157980545d90b482acfb01f525b84c0e623fa93a5d9c08a65723d677ef1c092360219a3c7fcf5110c6ba32f1eacbd5c5eaed40be4bfe154f302400c0a4 \ No newline at end of file diff --git a/tests/test_distribmat/test_distribmat.py b/tests/test_distribmat/test_distribmat.py index 96d41c3fb..a8d013996 100644 --- a/tests/test_distribmat/test_distribmat.py +++ b/tests/test_distribmat/test_distribmat.py @@ -17,16 +17,16 @@ class DistribmatTestHarness(PyAPITestHarness): moderator = openmc.Material(material_id=1) moderator.set_density('g/cc', 1.0) - moderator.add_nuclide('H-1', 2.0) - moderator.add_nuclide('O-16', 1.0) + moderator.add_nuclide('H1', 2.0) + moderator.add_nuclide('O16', 1.0) dense_fuel = openmc.Material(material_id=2) dense_fuel.set_density('g/cc', 4.5) - dense_fuel.add_nuclide('U-235', 1.0) + dense_fuel.add_nuclide('U235', 1.0) light_fuel = openmc.Material(material_id=3) light_fuel.set_density('g/cc', 2.0) - light_fuel.add_nuclide('U-235', 1.0) + light_fuel.add_nuclide('U235', 1.0) mats_file = openmc.Materials([moderator, dense_fuel, light_fuel]) mats_file.default_xs = '71c' diff --git a/tests/test_eigenvalue_genperbatch/materials.xml b/tests/test_eigenvalue_genperbatch/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_eigenvalue_genperbatch/materials.xml +++ b/tests/test_eigenvalue_genperbatch/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_eigenvalue_no_inactive/materials.xml b/tests/test_eigenvalue_no_inactive/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_eigenvalue_no_inactive/materials.xml +++ b/tests/test_eigenvalue_no_inactive/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_energy_grid/materials.xml b/tests/test_energy_grid/materials.xml index 45ccc6554..ed9b38d90 100644 --- a/tests/test_energy_grid/materials.xml +++ b/tests/test_energy_grid/materials.xml @@ -3,9 +3,9 @@ - - - + + + diff --git a/tests/test_energy_laws/materials.xml b/tests/test_energy_laws/materials.xml index 97f9b84be..c70e071cf 100644 --- a/tests/test_energy_laws/materials.xml +++ b/tests/test_energy_laws/materials.xml @@ -3,9 +3,9 @@ 71c - - - - + + + + diff --git a/tests/test_entropy/materials.xml b/tests/test_entropy/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_entropy/materials.xml +++ b/tests/test_entropy/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_filter_distribcell/case-1/materials.xml b/tests/test_filter_distribcell/case-1/materials.xml index 0bb9a35a3..891cc9fd0 100644 --- a/tests/test_filter_distribcell/case-1/materials.xml +++ b/tests/test_filter_distribcell/case-1/materials.xml @@ -6,13 +6,13 @@ - + - - + + diff --git a/tests/test_filter_distribcell/case-2/materials.xml b/tests/test_filter_distribcell/case-2/materials.xml index 0bb9a35a3..891cc9fd0 100644 --- a/tests/test_filter_distribcell/case-2/materials.xml +++ b/tests/test_filter_distribcell/case-2/materials.xml @@ -6,13 +6,13 @@ - + - - + + diff --git a/tests/test_filter_distribcell/case-3/materials.xml b/tests/test_filter_distribcell/case-3/materials.xml index 8f50a6a60..6a5916a83 100644 --- a/tests/test_filter_distribcell/case-3/materials.xml +++ b/tests/test_filter_distribcell/case-3/materials.xml @@ -6,120 +6,120 @@ - - - + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + diff --git a/tests/test_filter_distribcell/case-4/materials.xml b/tests/test_filter_distribcell/case-4/materials.xml index 19678c2a1..ab9f8688e 100644 --- a/tests/test_filter_distribcell/case-4/materials.xml +++ b/tests/test_filter_distribcell/case-4/materials.xml @@ -3,16 +3,16 @@ 71c - + - - - + + + - + diff --git a/tests/test_filter_mesh_2d/materials.xml b/tests/test_filter_mesh_2d/materials.xml index 9c0b74f3f..f5a9e61be 100644 --- a/tests/test_filter_mesh_2d/materials.xml +++ b/tests/test_filter_mesh_2d/materials.xml @@ -6,267 +6,267 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + diff --git a/tests/test_filter_mesh_3d/materials.xml b/tests/test_filter_mesh_3d/materials.xml index 9c0b74f3f..f5a9e61be 100644 --- a/tests/test_filter_mesh_3d/materials.xml +++ b/tests/test_filter_mesh_3d/materials.xml @@ -6,267 +6,267 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + diff --git a/tests/test_fixed_source/materials.xml b/tests/test_fixed_source/materials.xml index ab7a76bc0..6c52b2501 100644 --- a/tests/test_fixed_source/materials.xml +++ b/tests/test_fixed_source/materials.xml @@ -3,8 +3,8 @@ - - + + diff --git a/tests/test_infinite_cell/materials.xml b/tests/test_infinite_cell/materials.xml index 2e5b48381..1c2d64942 100644 --- a/tests/test_infinite_cell/materials.xml +++ b/tests/test_infinite_cell/materials.xml @@ -3,12 +3,12 @@ - + - + diff --git a/tests/test_iso_in_lab/inputs_true.dat b/tests/test_iso_in_lab/inputs_true.dat index bd722c9f6..34f522872 100644 --- a/tests/test_iso_in_lab/inputs_true.dat +++ b/tests/test_iso_in_lab/inputs_true.dat @@ -1 +1 @@ -85faac9b8c725ec9242ebc3793b70dcd1c8e58aeb4296345aefd8031304263bd66eaad0c6f1c61a1c644b73f397699856ab3d76d2b397295176650b4069acc9e \ No newline at end of file +c05fdb7815ccc1dcd2f260429b9139ad96ad4a7d1643e2bb938e3cd61268451363538ef4e41c5eaf73a64dbace43b2bd4489d5ff012a33104c2c1d6fa61146eb \ No newline at end of file diff --git a/tests/test_lattice/materials.xml b/tests/test_lattice/materials.xml index 4edf926d5..67240c4c9 100644 --- a/tests/test_lattice/materials.xml +++ b/tests/test_lattice/materials.xml @@ -15,127 +15,127 @@ - - - - - - - - - - - + + + + + + + + + + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - + + - - + + - - - + + + - - - + + + - - - - + + + + - - + + - + - - - + + + - - - - + + + + - + - - - - - + + + + + - + - - - - + + + + diff --git a/tests/test_lattice_hex/materials.xml b/tests/test_lattice_hex/materials.xml index 987a25ab1..92d10fa81 100644 --- a/tests/test_lattice_hex/materials.xml +++ b/tests/test_lattice_hex/materials.xml @@ -4,39 +4,39 @@ - - - + + + - - - + + + - - - - - + + + + + - - - - - - - + + + + + + + diff --git a/tests/test_lattice_mixed/materials.xml b/tests/test_lattice_mixed/materials.xml index 987a25ab1..92d10fa81 100644 --- a/tests/test_lattice_mixed/materials.xml +++ b/tests/test_lattice_mixed/materials.xml @@ -4,39 +4,39 @@ - - - + + + - - - + + + - - - - - + + + + + - - - - - - - + + + + + + + diff --git a/tests/test_lattice_multiple/materials.xml b/tests/test_lattice_multiple/materials.xml index 9c0b74f3f..f5a9e61be 100644 --- a/tests/test_lattice_multiple/materials.xml +++ b/tests/test_lattice_multiple/materials.xml @@ -6,267 +6,267 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + diff --git a/tests/test_mgxs_library_ce_to_mg/inputs_true.dat b/tests/test_mgxs_library_ce_to_mg/inputs_true.dat index 9633a46a8..332c2df5f 100644 --- a/tests/test_mgxs_library_ce_to_mg/inputs_true.dat +++ b/tests/test_mgxs_library_ce_to_mg/inputs_true.dat @@ -1 +1 @@ -34d5891f6f17c2d4b686b814ba61ba0045bc4289e278b1c3c47dbba59b83837fcfe15f2b8d58e7a2b07627b73d51e40348d70e9ed36dbb7cc94468d61c068c4c \ No newline at end of file +fb9c9180f692198548ca14543b29a8b623b382a19932999b2140ca4dd440f2102ba2fdbcb500ede3b8829aa85e1b4498151d280f1422d2d6b1bb5789aff34d71 \ No newline at end of file diff --git a/tests/test_mgxs_library_condense/inputs_true.dat b/tests/test_mgxs_library_condense/inputs_true.dat index 79ca0ec66..d92cc888a 100644 --- a/tests/test_mgxs_library_condense/inputs_true.dat +++ b/tests/test_mgxs_library_condense/inputs_true.dat @@ -1 +1 @@ -317a63a9dd3bfd84e969667b00f46018e56c04c356461a75103f63569e6b70c84d0da7f5e611faaf1b2631330b05ab4346223d3d843018ce0ce8876671a450c0 \ No newline at end of file +855919f7a333acff6423527b82656d6a472ea8416002fb475c2d21c676e2f75f5c040d209a3e9a3a6118cf944f2c1bfb8cf2318273b890861d790b1927791a35 \ No newline at end of file diff --git a/tests/test_mgxs_library_distribcell/inputs_true.dat b/tests/test_mgxs_library_distribcell/inputs_true.dat index dc67b7c56..9c33eeab4 100644 --- a/tests/test_mgxs_library_distribcell/inputs_true.dat +++ b/tests/test_mgxs_library_distribcell/inputs_true.dat @@ -1 +1 @@ -88849ac150f9c389e67de96356dfceb0bde08643f68ca25699e67d263995b95893d7340a2b08b2f0f5075fc5020f73553c5287ec6c56ace2f35ce0214961e123 \ No newline at end of file +3a3b7f75b326c94a8e5c7efe3046b2fdb887e9f75ecf6eb27587f9450c77cf8fd6acc4198c15bffb4e7ceead6d7b4327c19536bbf9cc35dfaae3f4ce4c26cc1a \ No newline at end of file diff --git a/tests/test_mgxs_library_hdf5/inputs_true.dat b/tests/test_mgxs_library_hdf5/inputs_true.dat index 79ca0ec66..d92cc888a 100644 --- a/tests/test_mgxs_library_hdf5/inputs_true.dat +++ b/tests/test_mgxs_library_hdf5/inputs_true.dat @@ -1 +1 @@ -317a63a9dd3bfd84e969667b00f46018e56c04c356461a75103f63569e6b70c84d0da7f5e611faaf1b2631330b05ab4346223d3d843018ce0ce8876671a450c0 \ No newline at end of file +855919f7a333acff6423527b82656d6a472ea8416002fb475c2d21c676e2f75f5c040d209a3e9a3a6118cf944f2c1bfb8cf2318273b890861d790b1927791a35 \ No newline at end of file diff --git a/tests/test_mgxs_library_no_nuclides/inputs_true.dat b/tests/test_mgxs_library_no_nuclides/inputs_true.dat index 79ca0ec66..d92cc888a 100644 --- a/tests/test_mgxs_library_no_nuclides/inputs_true.dat +++ b/tests/test_mgxs_library_no_nuclides/inputs_true.dat @@ -1 +1 @@ -317a63a9dd3bfd84e969667b00f46018e56c04c356461a75103f63569e6b70c84d0da7f5e611faaf1b2631330b05ab4346223d3d843018ce0ce8876671a450c0 \ No newline at end of file +855919f7a333acff6423527b82656d6a472ea8416002fb475c2d21c676e2f75f5c040d209a3e9a3a6118cf944f2c1bfb8cf2318273b890861d790b1927791a35 \ No newline at end of file diff --git a/tests/test_mgxs_library_nuclides/inputs_true.dat b/tests/test_mgxs_library_nuclides/inputs_true.dat index 8dbb564c6..ea7655c91 100644 --- a/tests/test_mgxs_library_nuclides/inputs_true.dat +++ b/tests/test_mgxs_library_nuclides/inputs_true.dat @@ -1 +1 @@ -eebb1469278f470b5859ed83e9b6526e7c4e3fed503bd22e414c6dc13b19b8e4cb6a44e3c14269e6e173f43056eda78268f455662ae119280bc18ea6a071dac7 \ No newline at end of file +739796983940a1bad601998cf9ea2f90453a994477c7f675c2fd404d1864fe04a7fbfb5a15c5fe7cf9bad016b78432ba0910baba6f9cc026143761e9f526b62a \ No newline at end of file diff --git a/tests/test_mgxs_library_nuclides/results_true.dat b/tests/test_mgxs_library_nuclides/results_true.dat index 4f47bd417..d8ebe19a1 100644 --- a/tests/test_mgxs_library_nuclides/results_true.dat +++ b/tests/test_mgxs_library_nuclides/results_true.dat @@ -1 +1 @@ -a631b8a347f344d822e6300ed2576caa7c05a74daedeb4aaaabfb89570942cff1bbd47ad7f81306e668e12266404f7abdcf680fdfeb5a4835579892e32bf57e8 \ No newline at end of file +d56c6bae6bf3cd8950d3f50f089458c1c6c807be780fc97570532c6af6eb7e3057968d3345bd3c363f01315271129ef7b2ca028b05767353e601dc37b035f8a7 \ No newline at end of file diff --git a/tests/test_multipole/inputs_true.dat b/tests/test_multipole/inputs_true.dat index 5890332ed..801536d07 100644 --- a/tests/test_multipole/inputs_true.dat +++ b/tests/test_multipole/inputs_true.dat @@ -1 +1 @@ -28df9e4c4729798d35741e6e9a4f910f3386c831acc54325d0e583bd989065a487cea6981068d8669bc80c3388b166c002c2127446936733f8f3f0da3e154bae \ No newline at end of file +c727431ebef7a5987dade28f4cd940c566142f97b5ce01fbf9343d680caf9056623f0fac550db64f8f1043fa2cd8230155cfcbbcaffd1ae92cede723974596d7 \ No newline at end of file diff --git a/tests/test_multipole/test_multipole.py b/tests/test_multipole/test_multipole.py index 7c3615ad7..bd6822bdf 100644 --- a/tests/test_multipole/test_multipole.py +++ b/tests/test_multipole/test_multipole.py @@ -16,12 +16,12 @@ class MultipoleTestHarness(PyAPITestHarness): moderator = openmc.Material(material_id=1) moderator.set_density('g/cc', 1.0) - moderator.add_nuclide('H-1', 2.0) - moderator.add_nuclide('O-16', 1.0) + moderator.add_nuclide('H1', 2.0) + moderator.add_nuclide('O16', 1.0) dense_fuel = openmc.Material(material_id=2) dense_fuel.set_density('g/cc', 4.5) - dense_fuel.add_nuclide('U-235', 1.0) + dense_fuel.add_nuclide('U235', 1.0) mats_file = openmc.Materials([moderator, dense_fuel]) mats_file.default_xs = '71c' diff --git a/tests/test_natural_element/materials.xml b/tests/test_natural_element/materials.xml index 5a3b3bbc6..60d60b81f 100644 --- a/tests/test_natural_element/materials.xml +++ b/tests/test_natural_element/materials.xml @@ -8,9 +8,9 @@ - - - + + + @@ -18,7 +18,7 @@ - + diff --git a/tests/test_output/materials.xml b/tests/test_output/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_output/materials.xml +++ b/tests/test_output/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_particle_restart_eigval/materials.xml b/tests/test_particle_restart_eigval/materials.xml index 1fd2fb1c7..5ff4b736f 100644 --- a/tests/test_particle_restart_eigval/materials.xml +++ b/tests/test_particle_restart_eigval/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_particle_restart_fixed/materials.xml b/tests/test_particle_restart_fixed/materials.xml index deddaef97..f132f9763 100644 --- a/tests/test_particle_restart_fixed/materials.xml +++ b/tests/test_particle_restart_fixed/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_periodic/inputs_true.dat b/tests/test_periodic/inputs_true.dat index d50d0b859..56d3e01bf 100644 --- a/tests/test_periodic/inputs_true.dat +++ b/tests/test_periodic/inputs_true.dat @@ -1 +1 @@ -af589996f2930337afe34ba9894098ff5efe3b29b6e927117220b718bf29b630ffdbc931754d465a8e8100125a8aa997dbe10aab322b43f69d59710573996a6d \ No newline at end of file +0766f3e0ac9b3d26bf5529eb3c92e0337698994d663b6a68dd8c1340807d6941c7589d777430782bc7b78590adced55f0f55b1de71a7c70d453f78d4ca469d8d \ No newline at end of file diff --git a/tests/test_periodic/test_periodic.py b/tests/test_periodic/test_periodic.py index 558514575..026071104 100644 --- a/tests/test_periodic/test_periodic.py +++ b/tests/test_periodic/test_periodic.py @@ -11,13 +11,13 @@ class PeriodicTest(PyAPITestHarness): def _build_inputs(self): # Define materials water = openmc.Material(1) - water.add_nuclide('H-1', 2.0) - water.add_nuclide('O-16', 1.0) - water.add_s_alpha_beta('HH2O', '71t') + water.add_nuclide('H1', 2.0) + water.add_nuclide('O16', 1.0) + water.add_s_alpha_beta('c_H_in_H2O', '71t') water.set_density('g/cc', 1.0) fuel = openmc.Material(2) - fuel.add_nuclide('U-235', 1.0) + fuel.add_nuclide('U235', 1.0) fuel.set_density('g/cc', 4.5) materials = openmc.Materials((water, fuel)) diff --git a/tests/test_plot/materials.xml b/tests/test_plot/materials.xml index f90a5e7f8..826f670a4 100644 --- a/tests/test_plot/materials.xml +++ b/tests/test_plot/materials.xml @@ -3,17 +3,17 @@ - + - + - + diff --git a/tests/test_ptables_off/materials.xml b/tests/test_ptables_off/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_ptables_off/materials.xml +++ b/tests/test_ptables_off/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_quadric_surfaces/materials.xml b/tests/test_quadric_surfaces/materials.xml index 0150332b3..606253bec 100644 --- a/tests/test_quadric_surfaces/materials.xml +++ b/tests/test_quadric_surfaces/materials.xml @@ -3,8 +3,8 @@ - - + + diff --git a/tests/test_reflective_plane/materials.xml b/tests/test_reflective_plane/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_reflective_plane/materials.xml +++ b/tests/test_reflective_plane/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_resonance_scattering/inputs_true.dat b/tests/test_resonance_scattering/inputs_true.dat index f2a875c7e..fdfb2b84e 100644 --- a/tests/test_resonance_scattering/inputs_true.dat +++ b/tests/test_resonance_scattering/inputs_true.dat @@ -1 +1 @@ -ece83bb075ed8144af89ce7cebf1577dcb2489d2e9ce4afbe61a3e4398837e7a9aaa2ae0cea0a6542f51ca5e0d119b570c675ed1dca0d74237cd5fdce0b606a3 \ No newline at end of file +a97844ec7ab45b9e8c1d5849c99414dfa1408956fd951fa783ffa99f78770cd4644a3fb5abe038b0f835ef233ccea2b014e8bd7448f06769944383035ef38ac6 \ No newline at end of file diff --git a/tests/test_resonance_scattering/test_resonance_scattering.py b/tests/test_resonance_scattering/test_resonance_scattering.py index b752cf7f3..0d70c0d8d 100644 --- a/tests/test_resonance_scattering/test_resonance_scattering.py +++ b/tests/test_resonance_scattering/test_resonance_scattering.py @@ -12,10 +12,10 @@ class ResonanceScatteringTestHarness(PyAPITestHarness): # Materials mat = openmc.Material(material_id=1) mat.set_density('g/cc', 1.0) - mat.add_nuclide('U-238', 1.0) - mat.add_nuclide('U-235', 0.02) - mat.add_nuclide('Pu-239', 0.02) - mat.add_nuclide('H-1', 20.0) + mat.add_nuclide('U238', 1.0) + mat.add_nuclide('U235', 0.02) + mat.add_nuclide('Pu239', 0.02) + mat.add_nuclide('H1', 20.0) mats_file = openmc.Materials([mat]) mats_file.default_xs = '71c' @@ -37,8 +37,7 @@ class ResonanceScatteringTestHarness(PyAPITestHarness): geometry.export_to_xml() # Settings - nuclide = openmc.Nuclide('U-238', '71c') - nuclide.zaid = 92238 + nuclide = openmc.Nuclide('U238', '71c') res_scatt_dbrc = openmc.ResonanceScattering() res_scatt_dbrc.nuclide = nuclide res_scatt_dbrc.nuclide_0K = nuclide # This is a bad idea! Just for tests @@ -46,8 +45,7 @@ class ResonanceScatteringTestHarness(PyAPITestHarness): res_scatt_dbrc.E_min = 1e-6 res_scatt_dbrc.E_max = 210e-6 - nuclide = openmc.Nuclide('U-235', '71c') - nuclide.zaid = 92235 + nuclide = openmc.Nuclide('U235', '71c') res_scatt_wcm = openmc.ResonanceScattering() res_scatt_wcm.nuclide = nuclide res_scatt_wcm.nuclide_0K = nuclide @@ -55,8 +53,7 @@ class ResonanceScatteringTestHarness(PyAPITestHarness): res_scatt_wcm.E_min = 1e-6 res_scatt_wcm.E_max = 210e-6 - nuclide = openmc.Nuclide('Pu-239', '71c') - nuclide.zaid = 94239 + nuclide = openmc.Nuclide('Pu239', '71c') res_scatt_ares = openmc.ResonanceScattering() res_scatt_ares.nuclide = nuclide res_scatt_ares.nuclide_0K = nuclide diff --git a/tests/test_rotation/materials.xml b/tests/test_rotation/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_rotation/materials.xml +++ b/tests/test_rotation/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_salphabeta/materials.xml b/tests/test_salphabeta/materials.xml index b51395f18..2bc401e49 100644 --- a/tests/test_salphabeta/materials.xml +++ b/tests/test_salphabeta/materials.xml @@ -5,39 +5,39 @@ - - - + + + - - - + + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + diff --git a/tests/test_score_current/materials.xml b/tests/test_score_current/materials.xml index 9c0b74f3f..f5a9e61be 100644 --- a/tests/test_score_current/materials.xml +++ b/tests/test_score_current/materials.xml @@ -6,267 +6,267 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + diff --git a/tests/test_seed/materials.xml b/tests/test_seed/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_seed/materials.xml +++ b/tests/test_seed/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_source/inputs_true.dat b/tests/test_source/inputs_true.dat index 69a1e2ea8..f11998e6c 100644 --- a/tests/test_source/inputs_true.dat +++ b/tests/test_source/inputs_true.dat @@ -1 +1 @@ -526c91551d9a80dc01216e5cb04162253f12ec684cc2b4912ca18cfc510f1ea2e5303029f1c1607882082b0c2c8a47f25dd5be14678f449a1579e3601d1bdec5 \ No newline at end of file +27ceb546499a4134eac08ffb22d02ce21d67f12617d43a02991b443e9aca7b7eca818d03e146676c0b352abaef6505423e48edef24cfd7a8fdb148cb3dbcdb1f \ No newline at end of file diff --git a/tests/test_source/test_source.py b/tests/test_source/test_source.py index 0abae4344..09a13efaa 100644 --- a/tests/test_source/test_source.py +++ b/tests/test_source/test_source.py @@ -15,7 +15,7 @@ class SourceTestHarness(PyAPITestHarness): def _build_inputs(self): mat1 = openmc.Material(material_id=1) mat1.set_density('g/cm3', 4.5) - mat1.add_nuclide(openmc.Nuclide('U-235', '71c'), 1.0) + mat1.add_nuclide(openmc.Nuclide('U235', '71c'), 1.0) materials = openmc.Materials([mat1]) materials.export_to_xml() diff --git a/tests/test_source_file/materials.xml b/tests/test_source_file/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_source_file/materials.xml +++ b/tests/test_source_file/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_sourcepoint_batch/materials.xml b/tests/test_sourcepoint_batch/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_sourcepoint_batch/materials.xml +++ b/tests/test_sourcepoint_batch/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_sourcepoint_interval/materials.xml b/tests/test_sourcepoint_interval/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_sourcepoint_interval/materials.xml +++ b/tests/test_sourcepoint_interval/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_sourcepoint_latest/materials.xml b/tests/test_sourcepoint_latest/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_sourcepoint_latest/materials.xml +++ b/tests/test_sourcepoint_latest/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_sourcepoint_restart/materials.xml b/tests/test_sourcepoint_restart/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_sourcepoint_restart/materials.xml +++ b/tests/test_sourcepoint_restart/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_statepoint_batch/materials.xml b/tests/test_statepoint_batch/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_statepoint_batch/materials.xml +++ b/tests/test_statepoint_batch/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_statepoint_interval/materials.xml b/tests/test_statepoint_interval/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_statepoint_interval/materials.xml +++ b/tests/test_statepoint_interval/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_statepoint_restart/materials.xml b/tests/test_statepoint_restart/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_statepoint_restart/materials.xml +++ b/tests/test_statepoint_restart/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_statepoint_sourcesep/materials.xml b/tests/test_statepoint_sourcesep/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_statepoint_sourcesep/materials.xml +++ b/tests/test_statepoint_sourcesep/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_survival_biasing/materials.xml b/tests/test_survival_biasing/materials.xml index 1748e09fd..facad016b 100644 --- a/tests/test_survival_biasing/materials.xml +++ b/tests/test_survival_biasing/materials.xml @@ -3,8 +3,8 @@ - - + + diff --git a/tests/test_tallies/inputs_true.dat b/tests/test_tallies/inputs_true.dat index e3d37be30..ddab630f2 100644 --- a/tests/test_tallies/inputs_true.dat +++ b/tests/test_tallies/inputs_true.dat @@ -1 +1 @@ -ea09926d8f5c6c96529bf5529f4deb3be78eda2da80adbbf3440147c337587358c2b1823bc72df9463676135573eb481dcd361b735f18365216645ee81092f1e \ No newline at end of file +a392a7a8f27fd2f959b06a6809df29b3482e215da175b19846c248af44dc2ee7e2b05269b802dd9d6ed434506b05092f349436a0048411adab6b296dba0bd683 \ No newline at end of file diff --git a/tests/test_tallies/test_tallies.py b/tests/test_tallies/test_tallies.py index 9e40d4185..e51e4616f 100644 --- a/tests/test_tallies/test_tallies.py +++ b/tests/test_tallies/test_tallies.py @@ -155,7 +155,7 @@ class TalliesTestHarness(PyAPITestHarness): total_tallies[0].scores = ['total'] for t in total_tallies[1:]: t.scores = ['total-y4'] - t.nuclides = ['U-235', 'total'] + t.nuclides = ['U235', 'total'] total_tallies[1].estimator = 'tracklength' total_tallies[2].estimator = 'analog' total_tallies[3].estimator = 'collision' diff --git a/tests/test_tally_aggregation/inputs_true.dat b/tests/test_tally_aggregation/inputs_true.dat index 055ac76fd..9c7978755 100644 --- a/tests/test_tally_aggregation/inputs_true.dat +++ b/tests/test_tally_aggregation/inputs_true.dat @@ -1 +1 @@ -f819f1b3564ca1df1e235f120f4bd65003cd80935fa8261f0a5982b7e7ec5b2e7497716673c142fab99f3fb26c174ac7a12e145b9a6f2caf707d2a07702f6eb2 \ No newline at end of file +67daf0d74cddb40ecbbc7e3793a3302866adcb1617fe2dc454dd161c06105e65027523f5e5954d80b961ba6c6abf14114b8be5c4d5b7682eaddffc1288d3e7c8 \ No newline at end of file diff --git a/tests/test_tally_aggregation/test_tally_aggregation.py b/tests/test_tally_aggregation/test_tally_aggregation.py index fdc086e68..76284ef9d 100644 --- a/tests/test_tally_aggregation/test_tally_aggregation.py +++ b/tests/test_tally_aggregation/test_tally_aggregation.py @@ -16,9 +16,9 @@ class TallyAggregationTestHarness(PyAPITestHarness): self._input_set.settings.output = {'summary': True} # Initialize the nuclides - u235 = openmc.Nuclide('U-235') - u238 = openmc.Nuclide('U-238') - pu239 = openmc.Nuclide('Pu-239') + u235 = openmc.Nuclide('U235') + u238 = openmc.Nuclide('U238') + pu239 = openmc.Nuclide('Pu239') # Initialize the filters energy_filter = openmc.Filter(type='energy', bins=[0.0, 0.253e-6, @@ -60,7 +60,7 @@ class TallyAggregationTestHarness(PyAPITestHarness): outstr += ', '.join(map(str, tally_sum.std_dev)) # Sum across all nuclides - tally_sum = tally.summation(nuclides=['U-235', 'U-238', 'Pu-239']) + tally_sum = tally.summation(nuclides=['U235', 'U238', 'Pu239']) outstr += ', '.join(map(str, tally_sum.mean)) outstr += ', '.join(map(str, tally_sum.std_dev)) diff --git a/tests/test_tally_arithmetic/inputs_true.dat b/tests/test_tally_arithmetic/inputs_true.dat index d7b854a51..b56c17b6b 100644 --- a/tests/test_tally_arithmetic/inputs_true.dat +++ b/tests/test_tally_arithmetic/inputs_true.dat @@ -1 +1 @@ -bb7e730630f7bb4694a27fd77c3c0171f70c78df2681acc26b0ef88bcff367523b11335f487b46269325adbcee7faeb756484af64055c3c91b0103f7ed962053 \ No newline at end of file +c8772a174e2162030f0315a318991085f1a5c0b054883a5f071d520e34f9ecf7d309f25700067bea8685e2b6324a19a003bd6f6ab38161ee87c95257b5a6ae69 \ No newline at end of file diff --git a/tests/test_tally_arithmetic/test_tally_arithmetic.py b/tests/test_tally_arithmetic/test_tally_arithmetic.py index a5919909f..20f86d97b 100644 --- a/tests/test_tally_arithmetic/test_tally_arithmetic.py +++ b/tests/test_tally_arithmetic/test_tally_arithmetic.py @@ -19,9 +19,9 @@ class TallyArithmeticTestHarness(PyAPITestHarness): tallies_file = openmc.Tallies() # Initialize the nuclides - u235 = openmc.Nuclide('U-235') - u238 = openmc.Nuclide('U-238') - pu239 = openmc.Nuclide('Pu-239') + u235 = openmc.Nuclide('U235') + u238 = openmc.Nuclide('U238') + pu239 = openmc.Nuclide('Pu239') # Initialize Mesh mesh = openmc.Mesh(mesh_id=1) diff --git a/tests/test_tally_assumesep/materials.xml b/tests/test_tally_assumesep/materials.xml index 9c0b74f3f..f5a9e61be 100644 --- a/tests/test_tally_assumesep/materials.xml +++ b/tests/test_tally_assumesep/materials.xml @@ -6,267 +6,267 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + diff --git a/tests/test_tally_nuclides/materials.xml b/tests/test_tally_nuclides/materials.xml index 2761be30c..e9667b41f 100644 --- a/tests/test_tally_nuclides/materials.xml +++ b/tests/test_tally_nuclides/materials.xml @@ -6,7 +6,7 @@ - + diff --git a/tests/test_tally_nuclides/tallies.xml b/tests/test_tally_nuclides/tallies.xml index cf20668c8..3440dbf21 100644 --- a/tests/test_tally_nuclides/tallies.xml +++ b/tests/test_tally_nuclides/tallies.xml @@ -7,7 +7,7 @@ - Pu-239 + Pu239 total absorption fission scatter diff --git a/tests/test_tally_slice_merge/inputs_true.dat b/tests/test_tally_slice_merge/inputs_true.dat index 771a1de8e..16d11b6d2 100644 --- a/tests/test_tally_slice_merge/inputs_true.dat +++ b/tests/test_tally_slice_merge/inputs_true.dat @@ -1 +1 @@ -8e54df241233bf8d5424afa0a22cc23c614a3541e5d7cc64036b5284edd28fe2353905ffdfebb446a4dd0202dda6a7da6d0110af00b4ca79117ec1dbe0584ba7 \ No newline at end of file +a17354ce54bcb5ce93e861ae95fd932c45fe60c733d999aa180d5dee636f43130fac5b003c615e23c65d44fe55f376ef4c86596b78603ab217d7e37fd694074d \ No newline at end of file diff --git a/tests/test_tally_slice_merge/results_true.dat b/tests/test_tally_slice_merge/results_true.dat index f986a91de..89d415b0d 100644 --- a/tests/test_tally_slice_merge/results_true.dat +++ b/tests/test_tally_slice_merge/results_true.dat @@ -1,67 +1,67 @@ cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 21 0.00e+00 6.25e-07 U-235 fission 1.08e-01 7.94e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 21 0.00e+00 6.25e-07 U-235 nu-fission 2.64e-01 1.94e-02 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 21 0.00e+00 6.25e-07 U-238 fission 1.51e-07 1.00e-08 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 21 0.00e+00 6.25e-07 U-238 nu-fission 3.76e-07 2.50e-08 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 21 6.25e-07 2.00e+01 U-235 fission 3.12e-02 2.56e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 21 6.25e-07 2.00e+01 U-235 nu-fission 7.65e-02 6.24e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 21 6.25e-07 2.00e+01 U-238 fission 2.00e-02 1.30e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 21 6.25e-07 2.00e+01 U-238 nu-fission 5.56e-02 3.78e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 27 0.00e+00 6.25e-07 U-235 fission 4.43e-02 7.21e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 27 0.00e+00 6.25e-07 U-235 nu-fission 1.08e-01 1.76e-02 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 27 0.00e+00 6.25e-07 U-238 fission 6.14e-08 9.64e-09 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 27 0.00e+00 6.25e-07 U-238 nu-fission 1.53e-07 2.40e-08 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 27 6.25e-07 2.00e+01 U-235 fission 1.39e-02 1.06e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 27 6.25e-07 2.00e+01 U-235 nu-fission 3.40e-02 2.61e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 27 6.25e-07 2.00e+01 U-238 fission 9.72e-03 1.21e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 27 6.25e-07 2.00e+01 U-238 nu-fission 2.71e-02 3.80e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 21 0.00e+00 6.25e-07 U-235 fission 1.08e-01 7.94e-03 -1 21 0.00e+00 6.25e-07 U-235 nu-fission 2.64e-01 1.94e-02 -2 21 0.00e+00 6.25e-07 U-238 fission 1.51e-07 1.00e-08 -3 21 0.00e+00 6.25e-07 U-238 nu-fission 3.76e-07 2.50e-08 -4 21 6.25e-07 2.00e+01 U-235 fission 3.12e-02 2.56e-03 -5 21 6.25e-07 2.00e+01 U-235 nu-fission 7.65e-02 6.24e-03 -6 21 6.25e-07 2.00e+01 U-238 fission 2.00e-02 1.30e-03 -7 21 6.25e-07 2.00e+01 U-238 nu-fission 5.56e-02 3.78e-03 -8 27 0.00e+00 6.25e-07 U-235 fission 4.43e-02 7.21e-03 -9 27 0.00e+00 6.25e-07 U-235 nu-fission 1.08e-01 1.76e-02 -10 27 0.00e+00 6.25e-07 U-238 fission 6.14e-08 9.64e-09 -11 27 0.00e+00 6.25e-07 U-238 nu-fission 1.53e-07 2.40e-08 -12 27 6.25e-07 2.00e+01 U-235 fission 1.39e-02 1.06e-03 -13 27 6.25e-07 2.00e+01 U-235 nu-fission 3.40e-02 2.61e-03 -14 27 6.25e-07 2.00e+01 U-238 fission 9.72e-03 1.21e-03 -15 27 6.25e-07 2.00e+01 U-238 nu-fission 2.71e-02 3.80e-03 +0 21 0.00e+00 6.25e-07 U235 fission 1.08e-01 7.94e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. +0 21 0.00e+00 6.25e-07 U235 nu-fission 2.64e-01 1.94e-02 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. +0 21 0.00e+00 6.25e-07 U238 fission 1.51e-07 1.00e-08 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. +0 21 0.00e+00 6.25e-07 U238 nu-fission 3.76e-07 2.50e-08 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. +0 21 6.25e-07 2.00e+01 U235 fission 3.12e-02 2.56e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. +0 21 6.25e-07 2.00e+01 U235 nu-fission 7.65e-02 6.24e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. +0 21 6.25e-07 2.00e+01 U238 fission 2.00e-02 1.30e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. +0 21 6.25e-07 2.00e+01 U238 nu-fission 5.56e-02 3.78e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. +0 27 0.00e+00 6.25e-07 U235 fission 4.43e-02 7.21e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. +0 27 0.00e+00 6.25e-07 U235 nu-fission 1.08e-01 1.76e-02 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. +0 27 0.00e+00 6.25e-07 U238 fission 6.14e-08 9.64e-09 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. +0 27 0.00e+00 6.25e-07 U238 nu-fission 1.53e-07 2.40e-08 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. +0 27 6.25e-07 2.00e+01 U235 fission 1.39e-02 1.06e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. +0 27 6.25e-07 2.00e+01 U235 nu-fission 3.40e-02 2.61e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. +0 27 6.25e-07 2.00e+01 U238 fission 9.72e-03 1.21e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. +0 27 6.25e-07 2.00e+01 U238 nu-fission 2.71e-02 3.80e-03 cell energy low [MeV] energy high [MeV] nuclide score mean std. dev. +0 21 0.00e+00 6.25e-07 U235 fission 1.08e-01 7.94e-03 +1 21 0.00e+00 6.25e-07 U235 nu-fission 2.64e-01 1.94e-02 +2 21 0.00e+00 6.25e-07 U238 fission 1.51e-07 1.00e-08 +3 21 0.00e+00 6.25e-07 U238 nu-fission 3.76e-07 2.50e-08 +4 21 6.25e-07 2.00e+01 U235 fission 3.12e-02 2.56e-03 +5 21 6.25e-07 2.00e+01 U235 nu-fission 7.65e-02 6.24e-03 +6 21 6.25e-07 2.00e+01 U238 fission 2.00e-02 1.30e-03 +7 21 6.25e-07 2.00e+01 U238 nu-fission 5.56e-02 3.78e-03 +8 27 0.00e+00 6.25e-07 U235 fission 4.43e-02 7.21e-03 +9 27 0.00e+00 6.25e-07 U235 nu-fission 1.08e-01 1.76e-02 +10 27 0.00e+00 6.25e-07 U238 fission 6.14e-08 9.64e-09 +11 27 0.00e+00 6.25e-07 U238 nu-fission 1.53e-07 2.40e-08 +12 27 6.25e-07 2.00e+01 U235 fission 1.39e-02 1.06e-03 +13 27 6.25e-07 2.00e+01 U235 nu-fission 3.40e-02 2.61e-03 +14 27 6.25e-07 2.00e+01 U238 fission 9.72e-03 1.21e-03 +15 27 6.25e-07 2.00e+01 U238 nu-fission 2.71e-02 3.80e-03 sum(distribcell) energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 (0, 100, 2000, 30000) 0.00e+00 6.25e-07 U-235 fission 0.00e+00 0.00e+00 -1 (0, 100, 2000, 30000) 0.00e+00 6.25e-07 U-235 nu-fission 0.00e+00 0.00e+00 -2 (0, 100, 2000, 30000) 0.00e+00 6.25e-07 U-238 fission 0.00e+00 0.00e+00 -3 (0, 100, 2000, 30000) 0.00e+00 6.25e-07 U-238 nu-fission 0.00e+00 0.00e+00 -4 (0, 100, 2000, 30000) 6.25e-07 2.00e+01 U-235 fission 0.00e+00 0.00e+00 -5 (0, 100, 2000, 30000) 6.25e-07 2.00e+01 U-235 nu-fission 0.00e+00 0.00e+00 -6 (0, 100, 2000, 30000) 6.25e-07 2.00e+01 U-238 fission 0.00e+00 0.00e+00 -7 (0, 100, 2000, 30000) 6.25e-07 2.00e+01 U-238 nu-fission 0.00e+00 0.00e+00 -8 (500, 5000, 50000) 0.00e+00 6.25e-07 U-235 fission 0.00e+00 0.00e+00 -9 (500, 5000, 50000) 0.00e+00 6.25e-07 U-235 nu-fission 0.00e+00 0.00e+00 -10 (500, 5000, 50000) 0.00e+00 6.25e-07 U-238 fission 0.00e+00 0.00e+00 -11 (500, 5000, 50000) 0.00e+00 6.25e-07 U-238 nu-fission 0.00e+00 0.00e+00 -12 (500, 5000, 50000) 6.25e-07 2.00e+01 U-235 fission 0.00e+00 0.00e+00 -13 (500, 5000, 50000) 6.25e-07 2.00e+01 U-235 nu-fission 0.00e+00 0.00e+00 -14 (500, 5000, 50000) 6.25e-07 2.00e+01 U-238 fission 0.00e+00 0.00e+00 -15 (500, 5000, 50000) 6.25e-07 2.00e+01 U-238 nu-fission 0.00e+00 0.00e+00 +0 (0, 100, 2000, 30000) 0.00e+00 6.25e-07 U235 fission 0.00e+00 0.00e+00 +1 (0, 100, 2000, 30000) 0.00e+00 6.25e-07 U235 nu-fission 0.00e+00 0.00e+00 +2 (0, 100, 2000, 30000) 0.00e+00 6.25e-07 U238 fission 0.00e+00 0.00e+00 +3 (0, 100, 2000, 30000) 0.00e+00 6.25e-07 U238 nu-fission 0.00e+00 0.00e+00 +4 (0, 100, 2000, 30000) 6.25e-07 2.00e+01 U235 fission 0.00e+00 0.00e+00 +5 (0, 100, 2000, 30000) 6.25e-07 2.00e+01 U235 nu-fission 0.00e+00 0.00e+00 +6 (0, 100, 2000, 30000) 6.25e-07 2.00e+01 U238 fission 0.00e+00 0.00e+00 +7 (0, 100, 2000, 30000) 6.25e-07 2.00e+01 U238 nu-fission 0.00e+00 0.00e+00 +8 (500, 5000, 50000) 0.00e+00 6.25e-07 U235 fission 0.00e+00 0.00e+00 +9 (500, 5000, 50000) 0.00e+00 6.25e-07 U235 nu-fission 0.00e+00 0.00e+00 +10 (500, 5000, 50000) 0.00e+00 6.25e-07 U238 fission 0.00e+00 0.00e+00 +11 (500, 5000, 50000) 0.00e+00 6.25e-07 U238 nu-fission 0.00e+00 0.00e+00 +12 (500, 5000, 50000) 6.25e-07 2.00e+01 U235 fission 0.00e+00 0.00e+00 +13 (500, 5000, 50000) 6.25e-07 2.00e+01 U235 nu-fission 0.00e+00 0.00e+00 +14 (500, 5000, 50000) 6.25e-07 2.00e+01 U238 fission 0.00e+00 0.00e+00 +15 (500, 5000, 50000) 6.25e-07 2.00e+01 U238 nu-fission 0.00e+00 0.00e+00 sum(mesh) energy low [MeV] energy high [MeV] nuclide score mean std. dev. -0 ((1, 1, 1), (1, 2, 1)) 0.00e+00 6.25e-07 U-235 fission 9.18e-03 1.62e-03 -1 ((1, 1, 1), (1, 2, 1)) 0.00e+00 6.25e-07 U-235 nu-fission 2.24e-02 3.94e-03 -2 ((1, 1, 1), (1, 2, 1)) 0.00e+00 6.25e-07 U-238 fission 1.31e-08 2.08e-09 -3 ((1, 1, 1), (1, 2, 1)) 0.00e+00 6.25e-07 U-238 nu-fission 3.26e-08 5.19e-09 -4 ((1, 1, 1), (1, 2, 1)) 6.25e-07 2.00e+01 U-235 fission 8.40e-04 2.13e-04 -5 ((1, 1, 1), (1, 2, 1)) 6.25e-07 2.00e+01 U-235 nu-fission 2.06e-03 5.17e-04 -6 ((1, 1, 1), (1, 2, 1)) 6.25e-07 2.00e+01 U-238 fission 7.05e-04 3.42e-04 -7 ((1, 1, 1), (1, 2, 1)) 6.25e-07 2.00e+01 U-238 nu-fission 1.99e-03 1.01e-03 -8 ((2, 1, 1), (2, 2, 1)) 0.00e+00 6.25e-07 U-235 fission 8.77e-03 1.30e-03 -9 ((2, 1, 1), (2, 2, 1)) 0.00e+00 6.25e-07 U-235 nu-fission 2.14e-02 3.18e-03 -10 ((2, 1, 1), (2, 2, 1)) 0.00e+00 6.25e-07 U-238 fission 1.24e-08 1.74e-09 -11 ((2, 1, 1), (2, 2, 1)) 0.00e+00 6.25e-07 U-238 nu-fission 3.08e-08 4.33e-09 -12 ((2, 1, 1), (2, 2, 1)) 6.25e-07 2.00e+01 U-235 fission 2.30e-03 6.20e-04 -13 ((2, 1, 1), (2, 2, 1)) 6.25e-07 2.00e+01 U-235 nu-fission 5.63e-03 1.52e-03 -14 ((2, 1, 1), (2, 2, 1)) 6.25e-07 2.00e+01 U-238 fission 1.45e-03 7.19e-04 -15 ((2, 1, 1), (2, 2, 1)) 6.25e-07 2.00e+01 U-238 nu-fission 3.97e-03 1.98e-03 +0 ((1, 1, 1), (1, 2, 1)) 0.00e+00 6.25e-07 U235 fission 9.18e-03 1.62e-03 +1 ((1, 1, 1), (1, 2, 1)) 0.00e+00 6.25e-07 U235 nu-fission 2.24e-02 3.94e-03 +2 ((1, 1, 1), (1, 2, 1)) 0.00e+00 6.25e-07 U238 fission 1.31e-08 2.08e-09 +3 ((1, 1, 1), (1, 2, 1)) 0.00e+00 6.25e-07 U238 nu-fission 3.26e-08 5.19e-09 +4 ((1, 1, 1), (1, 2, 1)) 6.25e-07 2.00e+01 U235 fission 8.40e-04 2.13e-04 +5 ((1, 1, 1), (1, 2, 1)) 6.25e-07 2.00e+01 U235 nu-fission 2.06e-03 5.17e-04 +6 ((1, 1, 1), (1, 2, 1)) 6.25e-07 2.00e+01 U238 fission 7.05e-04 3.42e-04 +7 ((1, 1, 1), (1, 2, 1)) 6.25e-07 2.00e+01 U238 nu-fission 1.99e-03 1.01e-03 +8 ((2, 1, 1), (2, 2, 1)) 0.00e+00 6.25e-07 U235 fission 8.77e-03 1.30e-03 +9 ((2, 1, 1), (2, 2, 1)) 0.00e+00 6.25e-07 U235 nu-fission 2.14e-02 3.18e-03 +10 ((2, 1, 1), (2, 2, 1)) 0.00e+00 6.25e-07 U238 fission 1.24e-08 1.74e-09 +11 ((2, 1, 1), (2, 2, 1)) 0.00e+00 6.25e-07 U238 nu-fission 3.08e-08 4.33e-09 +12 ((2, 1, 1), (2, 2, 1)) 6.25e-07 2.00e+01 U235 fission 2.30e-03 6.20e-04 +13 ((2, 1, 1), (2, 2, 1)) 6.25e-07 2.00e+01 U235 nu-fission 5.63e-03 1.52e-03 +14 ((2, 1, 1), (2, 2, 1)) 6.25e-07 2.00e+01 U238 fission 1.45e-03 7.19e-04 +15 ((2, 1, 1), (2, 2, 1)) 6.25e-07 2.00e+01 U238 nu-fission 3.97e-03 1.98e-03 diff --git a/tests/test_tally_slice_merge/test_tally_slice_merge.py b/tests/test_tally_slice_merge/test_tally_slice_merge.py index 78ea4fe9d..2794539d2 100644 --- a/tests/test_tally_slice_merge/test_tally_slice_merge.py +++ b/tests/test_tally_slice_merge/test_tally_slice_merge.py @@ -1,5 +1,7 @@ #!/usr/bin/env python +from __future__ import division + import os import sys import glob @@ -20,7 +22,7 @@ class TallySliceMergeTestHarness(PyAPITestHarness): tallies_file = openmc.Tallies() # Define nuclides and scores to add to both tallies - self.nuclides = ['U-235', 'U-238'] + self.nuclides = ['U235', 'U238'] self.scores = ['fission', 'nu-fission'] # Define filters for energy and spatial domain @@ -60,7 +62,7 @@ class TallySliceMergeTestHarness(PyAPITestHarness): # Merge all cell tallies together while len(tallies) != 1: - halfway = int(len(tallies) / 2) + halfway = len(tallies) // 2 zip_split = zip(tallies[:halfway], tallies[halfway:]) tallies = list(map(lambda xy: xy[0].merge(xy[1]), zip_split)) diff --git a/tests/test_trace/materials.xml b/tests/test_trace/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_trace/materials.xml +++ b/tests/test_trace/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_track_output/materials.xml b/tests/test_track_output/materials.xml index e2b96cb53..017797aa1 100644 --- a/tests/test_track_output/materials.xml +++ b/tests/test_track_output/materials.xml @@ -4,92 +4,92 @@ - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + diff --git a/tests/test_translation/materials.xml b/tests/test_translation/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_translation/materials.xml +++ b/tests/test_translation/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_trigger_batch_interval/materials.xml b/tests/test_trigger_batch_interval/materials.xml index 2761be30c..e9667b41f 100644 --- a/tests/test_trigger_batch_interval/materials.xml +++ b/tests/test_trigger_batch_interval/materials.xml @@ -6,7 +6,7 @@ - + diff --git a/tests/test_trigger_batch_interval/tallies.xml b/tests/test_trigger_batch_interval/tallies.xml index cf20668c8..3440dbf21 100644 --- a/tests/test_trigger_batch_interval/tallies.xml +++ b/tests/test_trigger_batch_interval/tallies.xml @@ -7,7 +7,7 @@ - Pu-239 + Pu239 total absorption fission scatter diff --git a/tests/test_trigger_no_batch_interval/materials.xml b/tests/test_trigger_no_batch_interval/materials.xml index 2761be30c..e9667b41f 100644 --- a/tests/test_trigger_no_batch_interval/materials.xml +++ b/tests/test_trigger_no_batch_interval/materials.xml @@ -6,7 +6,7 @@ - + diff --git a/tests/test_trigger_no_batch_interval/tallies.xml b/tests/test_trigger_no_batch_interval/tallies.xml index cf20668c8..3440dbf21 100644 --- a/tests/test_trigger_no_batch_interval/tallies.xml +++ b/tests/test_trigger_no_batch_interval/tallies.xml @@ -7,7 +7,7 @@ - Pu-239 + Pu239 total absorption fission scatter diff --git a/tests/test_trigger_no_status/materials.xml b/tests/test_trigger_no_status/materials.xml index 2761be30c..e9667b41f 100644 --- a/tests/test_trigger_no_status/materials.xml +++ b/tests/test_trigger_no_status/materials.xml @@ -6,7 +6,7 @@ - + diff --git a/tests/test_trigger_no_status/tallies.xml b/tests/test_trigger_no_status/tallies.xml index cf20668c8..3440dbf21 100644 --- a/tests/test_trigger_no_status/tallies.xml +++ b/tests/test_trigger_no_status/tallies.xml @@ -7,7 +7,7 @@ - Pu-239 + Pu239 total absorption fission scatter diff --git a/tests/test_trigger_tallies/materials.xml b/tests/test_trigger_tallies/materials.xml index 2761be30c..e9667b41f 100644 --- a/tests/test_trigger_tallies/materials.xml +++ b/tests/test_trigger_tallies/materials.xml @@ -6,7 +6,7 @@ - + diff --git a/tests/test_trigger_tallies/tallies.xml b/tests/test_trigger_tallies/tallies.xml index 2dbc836ee..0d66e1538 100644 --- a/tests/test_trigger_tallies/tallies.xml +++ b/tests/test_trigger_tallies/tallies.xml @@ -7,7 +7,7 @@ - Pu-239 + Pu239 total absorption fission scatter diff --git a/tests/test_triso/inputs_true.dat b/tests/test_triso/inputs_true.dat index 3e54e5e6f..04119a2dc 100644 --- a/tests/test_triso/inputs_true.dat +++ b/tests/test_triso/inputs_true.dat @@ -1 +1 @@ -2dcfd1a17cba671874e60192a7355deb57e2e51467a474fd168c8b51e454a977edb34df07ae11625c0a43906112152c75113e442a9a8f240a4c9d1a11ee4771d \ No newline at end of file +f33e6653b883200457df2ff2ba9cf715d5ddaa1296dd71d277c6f1d9d5b7831cc92aaf1e97509d26e5a93235cd9f775c0cfaa5ebc3dfe8fc71469bac166d362b \ No newline at end of file diff --git a/tests/test_triso/test_triso.py b/tests/test_triso/test_triso.py index d1ac4e5cd..9a8fb0c3b 100644 --- a/tests/test_triso/test_triso.py +++ b/tests/test_triso/test_triso.py @@ -19,35 +19,35 @@ class TRISOTestHarness(PyAPITestHarness): # Define TRISO matrials fuel = openmc.Material() fuel.set_density('g/cm3', 10.5) - fuel.add_nuclide('U-235', 0.14154) - fuel.add_nuclide('U-238', 0.85846) - fuel.add_nuclide('C-Nat', 0.5) - fuel.add_nuclide('O-16', 1.5) + fuel.add_nuclide('U235', 0.14154) + fuel.add_nuclide('U238', 0.85846) + fuel.add_nuclide('C0', 0.5) + fuel.add_nuclide('O16', 1.5) porous_carbon = openmc.Material() porous_carbon.set_density('g/cm3', 1.0) - porous_carbon.add_nuclide('C-Nat', 1.0) - porous_carbon.add_s_alpha_beta('Graph', '71t') + porous_carbon.add_nuclide('C0', 1.0) + porous_carbon.add_s_alpha_beta('c_Graphite', '71t') ipyc = openmc.Material() ipyc.set_density('g/cm3', 1.90) - ipyc.add_nuclide('C-Nat', 1.0) - ipyc.add_s_alpha_beta('Graph', '71t') + ipyc.add_nuclide('C0', 1.0) + ipyc.add_s_alpha_beta('c_Graphite', '71t') sic = openmc.Material() sic.set_density('g/cm3', 3.20) sic.add_element('Si', 1.0) - sic.add_nuclide('C-Nat', 1.0) + sic.add_nuclide('C0', 1.0) opyc = openmc.Material() opyc.set_density('g/cm3', 1.87) - opyc.add_nuclide('C-Nat', 1.0) - opyc.add_s_alpha_beta('Graph', '71t') + opyc.add_nuclide('C0', 1.0) + opyc.add_s_alpha_beta('c_Graphite', '71t') graphite = openmc.Material() graphite.set_density('g/cm3', 1.1995) - graphite.add_nuclide('C-Nat', 1.0) - graphite.add_s_alpha_beta('Graph', '71t') + graphite.add_nuclide('C0', 1.0) + graphite.add_s_alpha_beta('c_Graphite', '71t') # Create TRISO particles spheres = [openmc.Sphere(R=r*1e-4) diff --git a/tests/test_uniform_fs/materials.xml b/tests/test_uniform_fs/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_uniform_fs/materials.xml +++ b/tests/test_uniform_fs/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_union_energy_grids/materials.xml b/tests/test_union_energy_grids/materials.xml index 45ccc6554..ed9b38d90 100644 --- a/tests/test_union_energy_grids/materials.xml +++ b/tests/test_union_energy_grids/materials.xml @@ -3,9 +3,9 @@ - - - + + + diff --git a/tests/test_universe/materials.xml b/tests/test_universe/materials.xml index 315c0fa84..23d7f969d 100644 --- a/tests/test_universe/materials.xml +++ b/tests/test_universe/materials.xml @@ -3,7 +3,7 @@ - + diff --git a/tests/test_void/materials.xml b/tests/test_void/materials.xml index b8f993824..4768bad0b 100644 --- a/tests/test_void/materials.xml +++ b/tests/test_void/materials.xml @@ -8,56 +8,56 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - + + + + - - + +