Merge remote-tracking branch 'upstream/develop' into pyapi_filters

This commit is contained in:
Sterling Harper 2016-09-29 16:22:10 -04:00
commit e464e22690
23 changed files with 687 additions and 696 deletions

88
data/convert_mcnp_70.py Executable file
View file

@ -0,0 +1,88 @@
#!/usr/bin/env python
from __future__ import print_function
from argparse import ArgumentParser
from collections import defaultdict
import glob
import os
import openmc.data
# Get path to MCNP data
parser = ArgumentParser()
parser.add_argument('-d', '--destination', default='mcnp_endfb70',
help='Directory to create new library in')
parser.add_argument('mcnpdata', help='Directory containing endf70[a-k] and endf70sab')
args = parser.parse_args()
assert os.path.isdir(args.mcnpdata)
# Get a list of all neutron ACE files
endf70 = glob.glob(os.path.join(args.mcnpdata, 'endf70[a-k]'))
# Create output directory if it doesn't exist
if not os.path.isdir(args.destination):
os.mkdir(args.destination)
library = openmc.data.DataLibrary()
for path in sorted(endf70):
print('Loading data from {}...'.format(path))
lib = openmc.data.ace.Library(path)
# Group together tables for the same nuclide
tables = defaultdict(list)
for table in lib.tables:
zaid, xs = table.name.split('.')
tables[zaid].append(table)
for zaid, tables in sorted(tables.items()):
# Convert first temperature for the table
print('Converting: ' + tables[0].name)
data = openmc.data.IncidentNeutron.from_ace(tables[0], 'mcnp')
# For each higher temperature, add cross sections to the existing table
for table in tables[1:]:
print('Adding: ' + table.name)
data.add_temperature_from_ace(table, 'mcnp')
# Export HDF5 file
h5_file = os.path.join(args.destination, data.name + '.h5')
print('Writing {}...'.format(h5_file))
data.export_to_hdf5(h5_file, 'w')
# Register with library
library.register_file(h5_file)
# Handle S(a,b) tables
endf70sab = os.path.join(args.mcnpdata, 'endf70sab')
if os.path.exists(endf70sab):
lib = openmc.data.ace.Library(endf70sab)
# Group together tables for the same nuclide
tables = defaultdict(list)
for table in lib.tables:
name, xs = table.name.split('.')
tables[name].append(table)
for zaid, tables in sorted(tables.items()):
# Convert first temperature for the table
print('Converting: ' + tables[0].name)
data = openmc.data.ThermalScattering.from_ace(tables[0])
# For each higher temperature, add cross sections to the existing table
for table in tables[1:]:
print('Adding: ' + table.name)
data.add_temperature_from_ace(table)
# Export HDF5 file
h5_file = os.path.join(args.destination, data.name + '.h5')
print('Writing {}...'.format(h5_file))
data.export_to_hdf5(h5_file, 'w')
# Register with library
library.register_file(h5_file)
# Write cross_sections.xml
libpath = os.path.join(args.destination, 'cross_sections.xml')
library.export_to_xml(libpath)

77
data/convert_mcnp_71.py Executable file
View file

@ -0,0 +1,77 @@
#!/usr/bin/env python
from __future__ import print_function
from argparse import ArgumentParser
from collections import defaultdict
import glob
import os
import openmc.data
# Get path to MCNP data
parser = ArgumentParser()
parser.add_argument('-d', '--destination', default='mcnp_endfb71',
help='Directory to create new library in')
parser.add_argument('-f', '--fission_energy_release',
help='HDF5 file containing fission energy release data')
parser.add_argument('mcnpdata', help='Directory containing endf71x and ENDF71SaB')
args = parser.parse_args()
assert os.path.isdir(args.mcnpdata)
# Get a list of all ACE files
endf71x = glob.glob(os.path.join(args.mcnpdata, 'endf71x', '*', '*.71?nc'))
endf71sab = glob.glob(os.path.join(args.mcnpdata, 'ENDF71SaB' , '*.2?t'))
# There's a bug in H-Zr at 1200 K
endf71sab.remove(os.path.join(args.mcnpdata, 'ENDF71SaB' , 'h-zr.27t'))
# Group together tables for the same nuclide
suffixes = defaultdict(list)
for filename in sorted(endf71x + endf71sab):
dirname, basename = os.path.split(filename)
zaid, xs = basename.split('.')
suffixes[os.path.join(dirname, zaid)].append(xs)
# Create output directory if it doesn't exist
if not os.path.isdir(args.destination):
os.mkdir(args.destination)
library = openmc.data.DataLibrary()
for basename, xs_list in sorted(suffixes.items()):
# Convert first temperature for the table
filename = '.'.join((basename, xs_list[0]))
print('Converting: ' + filename)
if filename.endswith('t'):
data = openmc.data.ThermalScattering.from_ace(filename)
else:
data = openmc.data.IncidentNeutron.from_ace(filename, 'mcnp')
# Add fission energy release data, if available
if args.fission_energy_release is not None:
fer = openmc.data.FissionEnergyRelease.from_compact_hdf5(
args.fission_energy_release, data)
if fer is not None:
data.fission_energy = fer
# For each higher temperature, add cross sections to the existing table
for xs in xs_list[1:]:
filename = '.'.join((basename, xs))
print('Adding: ' + filename)
if filename.endswith('t'):
data.add_temperature_from_ace(filename)
else:
data.add_temperature_from_ace(filename, 'mcnp')
# Export HDF5 file
h5_file = os.path.join(args.destination, data.name + '.h5')
print('Writing {}...'.format(h5_file))
data.export_to_hdf5(h5_file, 'w')
# Register with library
library.register_file(h5_file)
# Write cross_sections.xml
libpath = os.path.join(args.destination, 'cross_sections.xml')
library.export_to_xml(libpath)

View file

@ -2,14 +2,13 @@
from __future__ import print_function
import os
import shutil
import subprocess
from collections import defaultdict
import sys
import tarfile
import zipfile
import glob
import hashlib
import argparse
from string import digits
import openmc.data
@ -26,23 +25,18 @@ else:
download_warning = """
WARNING: This script will download approximately 9 GB of data. Extracting and
processing the data may require as much as 30 GB of additional free disk
processing the data may require as much as 40 GB of additional free disk
space. Note that if you don't need all 11 temperatures, you can modify the
'files' list in the script to download only the data you want.
Are you sure you want to continue? ([y]/n)
"""
thermal_suffix = {20: '01t', 100: '02t', 293: '03t', 296: '03t', 323: '04t',
350: '05t', 373: '06t', 400: '07t', 423: '08t', 473: '09t',
500: '10t', 523: '11t', 573: '12t', 600: '13t', 623: '14t',
643: '15t', 647: '15t', 700: '16t', 773: '17t', 800: '18t',
1000: '19t', 1200: '20t', 1600: '21t', 2000: '22t',
3000: '23t'}
parser = argparse.ArgumentParser()
parser.add_argument('-b', '--batch', action='store_true',
help='supresses standard in')
parser.add_argument('-d', '--destination', default='jeff-3.2-hdf5',
help='Directory to create new library in')
args = parser.parse_args()
response = askuser(download_warning) if not args.batch else 'y'
@ -130,21 +124,6 @@ for f in files:
for path in glob.glob(os.path.join('jeff-3.2', 'ACEs_293K', '*-293.ACE')):
os.remove(path)
# ==============================================================================
# FIX ERRORS
# A few nuclides at 400K has 03c instead of 04c
print('Assigning new cross section identifiers...')
wrong_nuclides = ['Mn55', 'Mo95', 'Nb93', 'Pd105', 'Pu239', 'Pu240', 'U235',
'U238', 'Y89']
for nuc in wrong_nuclides:
path = os.path.join('jeff-3.2', 'ACEs_400K', nuc + '.ACE')
print(' Fixing {} (03c --> 04c)...'.format(path))
if os.path.isfile(path):
text = open(path, 'r').read()
text = text[:7] + '04c' + text[10:]
open(path, 'w').write(text)
# ==============================================================================
# CHANGE ZAID FOR METASTABLES
@ -158,50 +137,90 @@ for path in metastables:
open(path, 'w').write(text)
# ==============================================================================
# CHANGE IDENTIFIER FOR S(A,B) TABLES
# GENERATE HDF5 LIBRARY -- NEUTRON FILES
thermals = glob.glob(os.path.join('jeff-3.2', 'ANNEX_6_3_STLs', '**', '*.ace'))
for path in thermals:
print(' Fixing {} (unique suffix)...'.format(path))
basename = os.path.basename(path)
temperature = int(basename.split('-')[1][:-4])
text = open(path, 'r').read()
text = text[:7] + thermal_suffix[temperature] + text[10:]
open(path, 'w').write(text)
# Get a list of all ACE files
neutron_files = glob.glob(os.path.join('jeff-3.2', '*', '*.ACE'))
# Group together tables for same nuclide
tables = defaultdict(list)
for filename in sorted(neutron_files):
dirname, basename = os.path.split(filename)
name = basename.split('.')[0]
tables[name].append(filename)
# Sort temperatures from lowest to highest
for name, filenames in sorted(tables.items()):
filenames.sort(key=lambda x: int(
x.split(os.path.sep)[1].split('_')[1][:-1]))
# Create output directory if it doesn't exist
if not os.path.isdir(args.destination):
os.mkdir(args.destination)
library = openmc.data.DataLibrary()
for name, filenames in sorted(tables.items()):
# Convert first temperature for the table
print('Converting: ' + filenames[0])
data = openmc.data.IncidentNeutron.from_ace(filenames[0])
# For each higher temperature, add cross sections to the existing table
for filename in filenames[1:]:
print('Adding: ' + filename)
data.add_temperature_from_ace(filename)
# Export HDF5 file
h5_file = os.path.join(args.destination, data.name + '.h5')
print('Writing {}...'.format(h5_file))
data.export_to_hdf5(h5_file, 'w')
# Register with library
library.register_file(h5_file)
# ==============================================================================
# CONVERT TO BINARY TO SAVE DISK SPACE
# GENERATE HDF5 LIBRARY -- S(A,B) FILES
# get a list of all ACE files
ace_files = (glob.glob(os.path.join('jeff-3.2', '**', '*.ACE')) +
glob.glob(os.path.join('jeff-3.2', 'ANNEX_6_3_STLs', '**', '*.ace')))
sab_files = glob.glob(os.path.join('jeff-3.2', 'ANNEX_6_3_STLs', '*', '*.ace'))
# Ask user to convert
if not args.batch:
response = askuser('Convert ACE files to binary? ([y]/n) ')
else:
response = 'y'
# Group together tables for same nuclide
tables = defaultdict(list)
for filename in sorted(sab_files):
dirname, basename = os.path.split(filename)
name = basename.split('-')[0]
tables[name].append(filename)
# Convert files if requested
if not response or response.lower().startswith('y'):
for f in ace_files:
print(' Converting {}...'.format(f))
openmc.data.ace.ascii_to_binary(f, f)
# Sort temperatures from lowest to highest
for name, filenames in sorted(tables.items()):
filenames.sort(key=lambda x: int(
os.path.split(x)[1].split('-')[1].split('.')[0]))
# ==============================================================================
# PROMPT USER TO GENERATE HDF5 LIBRARY
for name, filenames in sorted(tables.items()):
# Convert first temperature for the table
print('Converting: ' + filenames[0])
# Ask user to convert
if not args.batch:
response = askuser('Generate HDF5 library? ([y]/n) ')
else:
response = 'y'
# Take numbers out of table name, e.g. lw10.32t -> lw.32t
table = openmc.data.ace.get_table(filenames[0])
name, xs = table.name.split('.')
table.name = '.'.join((name.strip(digits), xs))
data = openmc.data.ThermalScattering.from_ace(table)
# Convert files if requested
if not response or response.lower().startswith('y'):
# Ensure 'import openmc.data' works in the openmc-ace-to-xml script
env = os.environ.copy()
env['PYTHONPATH'] = os.path.join(os.getcwd(), os.pardir)
# For each higher temperature, add cross sections to the existing table
for filename in filenames[1:]:
print('Adding: ' + filename)
table = openmc.data.ace.get_table(filename)
name, xs = table.name.split('.')
table.name = '.'.join((name.strip(digits), xs))
data.add_temperature_from_ace(table)
subprocess.call(['../scripts/openmc-ace-to-hdf5', '-d', 'jeff-3.2-hdf5']
+ sorted(ace_files), env=env)
# Export HDF5 file
h5_file = os.path.join(args.destination, data.name + '.h5')
print('Writing {}...'.format(h5_file))
data.export_to_hdf5(h5_file, 'w')
# Register with library
library.register_file(h5_file)
# Write cross_sections.xml
libpath = os.path.join(args.destination, 'cross_sections.xml')
library.export_to_xml(libpath)

View file

@ -4,7 +4,7 @@
Summary File Format
===================
The current revision of the summary file format is 1.
The current revision of the summary file format is 4.
**/filetype** (*char[]*)
@ -129,7 +129,16 @@ The current revision of the summary file format is 1.
**/geometry/cells/cell <uid>/distribcell_index** (*int*)
Index of this cell in distribcell filter arrays.
Index of this cell in distribcell arrays. Only present if this cell is
listed in a distribcell filter or if it uses distributed materials.
**/geometry/cells/cell <uid>/paths** (*char[][]*)
The paths traversed through the CSG tree to reach each distribcell
instance. This consists of the integer IDs for each universe, cell and
lattice delimited by '->'. Each lattice cell is specified by its (x,y) or
(x,y,z) indices. Only present if this cell is listed in a distribcell filter
or if it uses distributed materials.
**/geometry/surfaces/surface <uid>/index** (*int*)
@ -244,90 +253,6 @@ The current revision of the summary file format is 1.
Names of S(:math:`\alpha`,:math:`\beta`) tables assigned to the material.
**/tallies/n_tallies** (*int*)
Number of tallies in the problem.
**/tallies/n_meshes** (*int*)
Number of meshes in the problem.
**/tallies/mesh <uid>/index** (*int*)
Index in the meshes array used internally in OpenMC.
**/tallies/mesh <uid>/type** (*char[]*)
Type of the mesh. The only valid option is currently 'regular'.
**/tallies/mesh <uid>/dimension** (*int[]*)
Number of mesh cells in each direction.
**/tallies/mesh <uid>/lower_left** (*double[]*)
Coordinates of the lower-left corner of the mesh.
**/tallies/mesh <uid>/upper_right** (*double[]*)
Coordinates of the upper-right corner of the mesh.
**/tallies/mesh <uid>/width** (*double[]*)
Width of a single mesh cell in each direction.
**/tallies/tally <uid>/index** (*int*)
Index in tallies array used internally in OpenMC.
**/tallies/tally <uid>/name** (*char[]*)
Name of the tally.
**/tallies/tally <uid>/n_filters** (*int*)
Number of filters applied to the tally.
**/tallies/tally <uid>/filter <j>/type** (*char[]*)
Type of the j-th filter. Can be 'universe', 'material', 'cell', 'cellborn',
'surface', 'mesh', 'energy', 'energyout', or 'distribcell'.
**/tallies/tally <uid>/filter <j>/offset** (*int*)
Filter offset (used for distribcell filter).
**/tallies/tally <uid>/filter <j>/paths** (*char[][]*)
The paths traversed through the CSG tree to reach each distribcell
instance (for 'distribcell' filters only). This consists of the integer
IDs for each universe, cell and lattice delimited by '->'. Each lattice
cell is specified by its (x,y) or (x,y,z) indices.
**/tallies/tally <uid>/filter <j>/n_bins** (*int*)
Number of bins for the j-th filter.
**/tallies/tally <uid>/filter <j>/bins** (*int[]* or *double[]*)
Value for each filter bin of this type.
**/tallies/tally <uid>/nuclides** (*char[][]*)
Array of nuclides to tally. Note that if no nuclide is specified in the user
input, a single 'total' nuclide appears here.
**/tallies/tally <uid>/n_score_bins** (*int*)
Number of scoring bins for a single nuclide. In general, this can be greater
than the number of user-specified scores since each score might have
multiple scoring bins, e.g., scatter-PN.
**/tallies/tally <uid>/moment_orders** (*char[][]*)
Tallying moment orders for Legendre and spherical harmonic tally expansions
(*e.g.*, 'P2', 'Y1,2', etc.).
**/tallies/tally <uid>/score_bins** (*char[][]*)
Scoring bins for the tally.

File diff suppressed because one or more lines are too long

View file

@ -366,6 +366,7 @@ Core Classes
openmc.data.ThermalScattering
openmc.data.CoherentElastic
openmc.data.FissionEnergyRelease
openmc.data.DataLibrary
Angle-Energy Distributions
--------------------------

View file

@ -383,14 +383,16 @@ Cross Section Configuration
---------------------------
In order to run a simulation with OpenMC, you will need cross section data for
each nuclide or material in your problem. OpenMC can be run in
continuous-energy or multi-group mode.
each nuclide or material in your problem. OpenMC can be run in continuous-energy
or multi-group mode.
In continuous-energy mode OpenMC uses ACE format cross sections; in this case
you can use nuclear data that was processed with NJOY_, such as that
distributed with MCNP_ or Serpent_. Several sources provide free processed
ACE data as described below. The TALYS-based evaluated nuclear data library,
TENDL_, is also openly available in ACE format.
In continuous-energy mode, OpenMC uses a native HDF5 format to store all nuclear
data. If you have ACE format data that was produced with NJOY_, such as that
distributed with MCNP_ or Serpent_, it can be converted to the HDF5 format using
the :ref:`openmc-ace-to-hdf5 <other_cross_sections>` script distributed with
OpenMC. Several sources provide openly available ACE data as described
below. The TALYS-based evaluated nuclear data library, TENDL_, is also available
in ACE format.
In multi-group mode, OpenMC utilizes an XML-based library format which can be
used to describe nuclide- or material-specific quantities.
@ -400,8 +402,8 @@ Using ENDF/B-VII.1 Cross Sections from NNDC
The NNDC_ provides ACE data from the ENDF/B-VII.1 neutron and thermal scattering
sublibraries at four temperatures processed using NJOY_. To use this data with
OpenMC, a script is provided with OpenMC that will automatically download,
extract, and set up a confiuration file:
OpenMC, a script is provided with OpenMC that will automatically download and
extract the ACE data, fix any deficiencies, and create an HDF5 library:
.. code-block:: sh
@ -410,56 +412,99 @@ extract, and set up a confiuration file:
At this point, you should set the :envvar:`OPENMC_CROSS_SECTIONS` environment
variable to the absolute path of the file
``openmc/data/nndc/cross_sections.xml``. This cross section set is used by the
test suite.
``openmc/data/nndc_hdf5/cross_sections.xml``. This cross section set is used by
the test suite.
Using JEFF Cross Sections from OECD/NEA
---------------------------------------
The NEA_ provides processed ACE data from the JEFF_ nuclear library upon
request. A DVD of the data can be requested here_. To use this data with OpenMC,
the following steps must be taken:
The NEA_ provides processed ACE data from the JEFF_ library. To use this data
with OpenMC, a script is provided with OpenMC that will automatically download
and extract the ACE data, fix any deficiencies, and create an HDF5 library.
1. Copy and unzip the data on the DVD to a directory on your computer.
2. In the root directory, a file named ``xsdir``, or some variant thereof,
should be present. This file contains a listing of all the cross sections and
is used by MCNP. This file should be converted to a ``cross_sections.xml``
file for use with OpenMC. A utility is provided in the OpenMC distribution
for this purpose:
.. code-block:: sh
.. code-block:: sh
cd openmc/data
python get_jeff_data.py
openmc/scripts/openmc-xsdir-to-xml xsdir31 cross_sections.xml
3. In the converted ``cross_sections.xml`` file, change the contents of the
<directory> element to the absolute path of the directory containing the
actual ACE files.
4. Additionally, you may need to change any occurrences of upper-case "ACE"
within the ``cross_sections.xml`` file to lower-case.
5. Either set the :ref:`cross_sections` in a settings.xml file or the
:envvar:`OPENMC_CROSS_SECTIONS` environment variable to the absolute path of
the ``cross_sections.xml`` file.
At this point, you should set the :envvar:`OPENMC_CROSS_SECTIONS` environment
variable to the absolute path of the file
``openmc/data/jeff-3.2-hdf5/cross_sections.xml``.
Using Cross Sections from MCNP
------------------------------
To use cross sections distributed with MCNP, change the <directory> element in
the ``cross_sections.xml`` file in the root directory of the OpenMC distribution
to the location of the MCNP cross sections. Then, either set the
:ref:`cross_sections` in a settings.xml file or the
:envvar:`OPENMC_CROSS_SECTIONS` environment variable to the absolute path of
the ``cross_sections.xml`` file.
OpenMC is provided with a script that will automatically convert ENDF/B-VII.0
and ENDF/B-VII.1 ACE data that is provided with MCNP5 or MCNP6. To convert the
ENDF/B-VII.0 ACE files (``endf70[a-k]`` and ``endf70sab``) into the native HDF5
format, run the following:
Using Cross Sections from Serpent
---------------------------------
.. code-block:: sh
cd openmc/data
python convert_mcnp_endf70.py /path/to/mcnpdata/
where ``/path/to/mcnpdata`` is the directory containing the ``endf70[a-k]``
files.
To convert the ENDF/B-VII.1 ACE files (the endf71x and ENDF71SaB libraries), use
the following script:
.. code-block:: sh
cd openmc/data
python convert_mcnp_endf71.py /path/to/mcnpdata
where ``/path/to/mcnpdata`` is the directory containing the ``endf71x`` and
``ENDF71SaB`` directories.
.. _other_cross_sections:
Using Other Cross Sections
--------------------------
If you have a library of ACE format cross sections other than those listed above
that you need to convert to OpenMC's HDF5 format, the ``openmc-ace-to-hdf5``
script can be used. There are four different ways you can specify ACE libraries
that are to be converted:
1. List each ACE library as a positional argument. This is very useful in
conjunction with the usual shell utilities (ls, find, etc.).
2. Use the --xml option to specify a pre-v0.9 cross_sections.xml file.
3. Use the --xsdir option to specify a MCNP xsdir file.
4. Use the --xsdata option to specify a Serpent xsdata file.
The script does not use any extra information from cross_sections.xml/ xsdir/
xsdata files to determine whether the nuclide is metastable. Instead, the
--metastable argument can be used to specify whether the ZAID naming convention
follows the NNDC data convention (1000*Z + A + 300 + 100*m), or the MCNP data
convention (essentially the same as NNDC, except that the first metastable state
of Am242 is 95242 and the ground state is 95642).
The ``openmc-ace-to-hdf5`` script has the following command-line flags:
-h, --help show this help message and exit
-d DESTINATION, --destination DESTINATION
Directory to create new library in (default: .)
-m META, --metastable META
How to interpret ZAIDs for metastable nuclides. META
can be either 'nndc' or 'mcnp'. (default: nndc)
--xml XML Old-style cross_sections.xml that lists ACE libraries
(default: None)
--xsdir XSDIR MCNP xsdir file that lists ACE libraries (default:
None)
--xsdata XSDATA Serpent xsdata file that lists ACE libraries (default:
None)
--fission_energy_release FISSION_ENERGY_RELEASE
HDF5 file containing fission energy release data
(default: None)
To use cross sections distributed with Serpent, change the <directory> element
in the ``cross_sections_serpent.xml`` file in the root directory of the OpenMC
distribution to the location of the Serpent cross sections. Then, either set the
:ref:`cross_sections` in a settings.xml file or the
:envvar:`OPENMC_CROSS_SECTIONS` environment variable to the absolute path of
the ``cross_sections_serpent.xml``
file.
Using Multi-Group Cross Sections
--------------------------------
@ -471,14 +516,13 @@ However, if the user has obtained or generated their own library, the user
should set the :envvar:`OPENMC_MG_CROSS_SECTIONS` environment variable
to the absolute path of the file library expected to used most frequently.
.. _NJOY: http://t2.lanl.gov/nis/codes.shtml
.. _NJOY: http://t2.lanl.gov/nis/codes/NJOY12/
.. _NNDC: http://www.nndc.bnl.gov/endf/b7.1/acefiles.html
.. _NEA: http://www.oecd-nea.org
.. _JEFF: http://www.oecd-nea.org/dbdata/jeff/
.. _here: http://www.oecd-nea.org/dbdata/pubs/jeff312-cd.html
.. _JEFF: https://www.oecd-nea.org/dbforms/data/eva/evatapes/jeff_32/
.. _MCNP: http://mcnp.lanl.gov
.. _Serpent: http://montecarlo.vtt.fi
.. _TENDL: ftp://ftp.nrg.eu/pub/www/talys/tendl2012/tendl2012.html
.. _TENDL: https://tendl.web.psi.ch/tendl_2015/tendl2015.html
--------------
Running OpenMC

View file

@ -85,6 +85,9 @@ class Cell(object):
Array of offsets used for distributed cell searches
distribcell_index : int
Index of this cell in distribcell arrays
distribcell_paths : list of str
The paths traversed through the CSG tree to reach each distribcell
instance
volume_information : dict
Estimate of the volume and total number of atoms of each nuclide from a
stochastic volume calculation. This information is set with the
@ -104,6 +107,7 @@ class Cell(object):
self._translation = None
self._offsets = None
self._distribcell_index = None
self._distribcell_paths = None
self._volume_information = None
def __contains__(self, point):
@ -217,6 +221,10 @@ class Cell(object):
def distribcell_index(self):
return self._distribcell_index
@property
def distribcell_paths(self):
return self._distribcell_paths
@property
def volume_information(self):
return self._volume_information
@ -291,6 +299,7 @@ class Cell(object):
@temperature.setter
def temperature(self, temperature):
# Make sure temperatures are positive
cv.check_type('cell temperature', temperature, (Iterable, Real))
if isinstance(temperature, Iterable):
cv.check_type('cell temperature', temperature, Iterable, Real)
@ -298,7 +307,15 @@ class Cell(object):
cv.check_greater_than('cell temperature', T, 0.0, True)
else:
cv.check_greater_than('cell temperature', temperature, 0.0, True)
self._temperature = temperature
# If this cell is filled with a universe or lattice, propagate
# temperatures to all cells contained. Otherwise, simply assign it.
if self.fill_type in ('universe', 'lattice'):
for c in self.get_all_cells().values():
if c.fill_type == 'material':
c._temperature = temperature
else:
self._temperature = temperature
@offsets.setter
def offsets(self, offsets):
@ -316,6 +333,12 @@ class Cell(object):
cv.check_type('distribcell index', ind, Integral)
self._distribcell_index = ind
@distribcell_paths.setter
def distribcell_paths(self, distribcell_paths):
cv.check_iterable_type('distribcell_paths', distribcell_paths,
basestring)
self._distribcell_paths = distribcell_paths
def add_surface(self, surface, halfspace):
"""Add a half-space to the list of half-spaces whose intersection defines the
cell.
@ -427,9 +450,8 @@ class Cell(object):
else:
if self.volume_information is not None:
volume = self.volume_information['volume'][0]
for full_name, atoms in self.volume_information['atoms']:
name, xs = full_name.split('.')
nuclide = openmc.Nuclide(name, xs)
for name, atoms in self.volume_information['atoms']:
nuclide = openmc.Nuclide(name)
density = 1.0e-24 * atoms[0]/volume # density in atoms/b-cm
nuclides[name] = (nuclide, density)
else:

View file

@ -8,20 +8,50 @@ from openmc.clean_xml import clean_xml_indentation
class DataLibrary(EqualityMixin):
"""Collection of cross section data libraries.
Attributes
----------
libraries : list of dict
List in which each item is a dictionary summarizing cross section data
from a single file. The dictionary has keys 'path', 'type', and
'materials'.
"""
def __init__(self):
self.libraries = []
def register_file(self, filename, filetype='neutron'):
def register_file(self, filename):
"""Register a file with the data library.
Parameters
----------
filename : str
Path to the file to be registered.
"""
h5file = h5py.File(filename, 'r')
materials = []
filetype = 'neutron'
for name in h5file:
if name.startswith('c_'):
filetype = 'thermal'
materials.append(name)
library = {'path': filename, 'type': filetype, 'materials': materials}
self.libraries.append(library)
def export_to_xml(self, path='cross_sections.xml'):
"""Export cross section data library to an XML file.
Parameters
----------
path : str
Path to file to write. Defaults to 'cross_sections.xml'.
"""
root = ET.Element('cross_sections')
# Determine common directory for library paths

View file

@ -320,11 +320,11 @@ class IncidentNeutron(EqualityMixin):
# Add normal and summed reactions
for mt in chain(data.reactions, data.summed_reactions):
if mt not in self:
raise ValueError("Tried to add cross sections for MT={} at T={}"
" but this reaction doesn't exist.".format(
mt, strT))
self[mt].xs[strT] = data[mt].xs[strT]
if mt in self:
self[mt].xs[strT] = data[mt].xs[strT]
else:
warn("Tried to add cross sections for MT={} at T={} but this "
"reaction doesn't exist.".format(mt, strT))
# Add probability tables
if strT in data.urr:

View file

@ -22,13 +22,13 @@ _THERMAL_NAMES = {'al': 'c_Al27', 'al27': 'c_Al27',
'bebeo': 'c_Be_in_BeO', 'be-o': 'c_Be_in_BeO', 'be/o': 'c_Be_in_BeO',
'benz': 'c_Benzine',
'cah': 'c_Ca_in_CaH2',
'dd2o': 'c_D_in_D2O', 'hwtr': 'c_D_in_D2O',
'dd2o': 'c_D_in_D2O', 'hwtr': 'c_D_in_D2O', 'hw': 'c_D_in_D2O',
'fe': 'c_Fe56', 'fe56': 'c_Fe56',
'graph': 'c_Graphite', 'grph': 'c_Graphite',
'graph': 'c_Graphite', 'grph': 'c_Graphite', 'gr': 'c_Graphite',
'hca': 'c_H_in_CaH2',
'hch2': 'c_H_in_CH2', 'poly': 'c_H_in_CH2',
'hh2o': 'c_H_in_H2O', 'lwtr': 'c_H_in_H2O',
'hzrh': 'c_H_in_ZrH', 'h-zr': 'c_H_in_ZrH', 'h/zr': 'c_H_in_ZrH',
'hch2': 'c_H_in_CH2', 'poly': 'c_H_in_CH2', 'pol': 'c_H_in_CH2',
'hh2o': 'c_H_in_H2O', 'lwtr': 'c_H_in_H2O', 'lw': 'c_H_in_H2O',
'hzrh': 'c_H_in_ZrH', 'h-zr': 'c_H_in_ZrH', 'h/zr': 'c_H_in_ZrH', 'hzr': 'c_H_in_ZrH',
'lch4': 'c_liquid_CH4', 'lmeth': 'c_liquid_CH4',
'mg': 'c_Mg24',
'obeo': 'c_O_in_BeO', 'o-be': 'c_O_in_BeO', 'o/be': 'c_O_in_BeO',
@ -191,8 +191,6 @@ class ThermalScattering(EqualityMixin):
self.name = name
self.atomic_weight_ratio = atomic_weight_ratio
self.kTs = kTs
self.temperatures = [str(int(round(kT / K_BOLTZMANN))) + "K"
for kT in kTs]
self.elastic_xs = {}
self.elastic_mu_out = {}
self.inelastic_xs = {}
@ -208,6 +206,10 @@ class ThermalScattering(EqualityMixin):
else:
return "<Thermal Scattering Data>"
@property
def temperatures(self):
return ["{}K".format(int(round(kT / K_BOLTZMANN))) for kT in self.kTs]
def export_to_hdf5(self, path, mode='a'):
"""Export table to an HDF5 file.
@ -277,142 +279,36 @@ class ThermalScattering(EqualityMixin):
Thermal scattering data
"""
if isinstance(ace_or_filename, Table):
ace = ace_or_filename
else:
ace = get_table(ace_or_filename)
data = ThermalScattering.from_ace(ace_or_filename, name)
# Get new name that is GND-consistent
ace_name, xs = ace.name.split('.')
if name is None:
if ace_name.lower() in _THERMAL_NAMES:
name = _THERMAL_NAMES[ace_name.lower()]
else:
# Make an educated guess? This actually works well for JEFF-3.2
# which stupidly uses names like lw00.32t, lw01.32t, etc. for
# different temperatures
matches = get_close_matches(
ace_name.lower(), _THERMAL_NAMES.keys(), cutoff=0.5)
if len(matches) > 0:
name = _THERMAL_NAMES[matches[0]]
else:
# OK, we give up. Just use the ACE name.
name = 'c_' + ace.name
warn('Thermal scattering material "{}" is not recognized. '
'Assigning a name of {}.'.format(ace.name, name))
# Check if temprature already exists
strT = data.temperatures[0]
if strT in self.temperatures:
warn('S(a,b) data at T={} already exists.'.format(strT))
return
# If this ACE data matches the data within self then get the data
if ace.temperature not in self.kTs:
if name == self.name:
# Add temperature and kTs
strT = str(int(round(ace.temperature / K_BOLTZMANN))) + "K"
self.temperatures.append(strT)
self.kTs.append(ace.temperature)
# Check that name matches
if data.name != self.name:
raise ValueError('Data provided for an incorrect material.')
# Incoherent inelastic scattering cross section
idx = ace.jxs[1]
n_energy = int(ace.xss[idx])
energy = ace.xss[idx + 1: idx + 1 + n_energy]
xs = ace.xss[idx + 1 + n_energy: idx + 1 + 2 * n_energy]
self.inelastic_xs[strT] = Tabulated1D(energy, xs)
# Add temperature
self.kTs += data.kTs
# Make sure secondary_mode is always equal. This should always
# be the case, but to reduce future debugging should something
# change, this will alert the developers to the issue.
if ace.nxs[7] == 0:
secondary_mode = 'equal'
elif ace.nxs[7] == 1:
secondary_mode = 'skewed'
elif ace.nxs[7] == 2:
secondary_mode = 'continuous'
# Add inelastic cross section and distributions
if strT in data.inelastic_xs:
self.inelastic_xs[strT] = data.inelastic_xs[strT]
if strT in data.inelastic_e_out:
self.inelastic_e_out[strT] = data.inelastic_e_out[strT]
if strT in data.inelastic_mu_out:
self.inelastic_mu_out[strT] = data.inelastic_mu_out[strT]
if strT in data.inelastic_dist:
self.inelastic_dist[strT] = data.inelastic_dist[strT]
if secondary_mode != self.secondary_mode:
raise ValueError('Secondary Modes are inconsistent.')
n_energy_out = ace.nxs[4]
if self.secondary_mode in ('equal', 'skewed'):
n_mu = ace.nxs[3]
idx = ace.jxs[3]
self.inelastic_e_out[strT] = \
ace.xss[idx:idx + n_energy * n_energy_out * (n_mu + 2):
n_mu + 2]
self.inelastic_e_out[strT].shape = \
(n_energy, n_energy_out)
self.inelastic_mu_out[strT] = \
ace.xss[idx:idx + n_energy * n_energy_out * (n_mu + 2)]
self.inelastic_mu_out[strT].shape = \
(n_energy, n_energy_out, n_mu + 2)
self.inelastic_mu_out[strT] = \
self.inelastic_mu_out[strT][:, :, 1:]
else:
n_mu = ace.nxs[3] - 1
idx = ace.jxs[3]
locc = ace.xss[idx:idx + n_energy].astype(int)
n_energy_out = \
ace.xss[idx + n_energy:idx + 2 * n_energy].astype(int)
energy_out = []
mu_out = []
for i in range(n_energy):
idx = locc[i]
# Outgoing energy distribution for incoming energy i
e = ace.xss[idx + 1:idx + 1 + n_energy_out[i]*(n_mu + 3):
n_mu + 3]
p = ace.xss[idx + 2:idx + 2 + n_energy_out[i]*(n_mu + 3):
n_mu + 3]
c = ace.xss[idx + 3:idx + 3 + n_energy_out[i]*(n_mu + 3):
n_mu + 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(n_energy_out[i]):
mu = ace.xss[idx + 4:idx + 4 + n_mu]
p_mu = 1. / n_mu * np.ones(n_mu)
mu_ij = Discrete(mu, p_mu)
mu_ij.c = np.cumsum(p_mu)
mu_i.append(mu_ij)
idx += 3 + n_mu
energy_out.append(eout_i)
mu_out.append(mu_i)
# Create correlated angle-energy distribution
breakpoints = [n_energy]
interpolation = [2]
energy = self.inelastic_xs[strT].x
self.inelastic_dist[strT] = CorrelatedAngleEnergy(
breakpoints, interpolation, energy, energy_out, mu_out)
# Incoherent/coherent elastic scattering cross section
idx = ace.jxs[4]
if idx != 0:
n_energy = int(ace.xss[idx])
energy = ace.xss[idx + 1: idx + 1 + n_energy]
P = ace.xss[idx + 1 + n_energy: idx + 1 + 2 * n_energy]
if ace.nxs[5] == 4:
self.elastic_xs[strT] = CoherentElastic(energy, P)
else:
self.elastic_xs[strT] = Tabulated1D(energy, P)
# Angular distribution
n_mu = ace.nxs[6]
if n_mu != -1:
idx = ace.jxs[6]
self.elastic_mu_out[strT] = \
ace.xss[idx:idx + n_energy * n_mu]
self.elastic_mu_out[strT].shape = \
(n_energy, n_mu)
else:
raise ValueError('Data provided for an incorrect library')
else:
raise Warning('Temperature data set already within '
'IncidentNeutron object')
# Add elastic cross sectoin and angular distribution
if strT in data.elastic_xs:
self.elastic_xs[strT] = data.elastic_xs[strT]
if strT in data.elastic_mu_out:
self.elastic_mu_out[strT] = data.elastic_mu_out[strT]
@classmethod
def from_hdf5(cls, group_or_filename):

View file

@ -65,10 +65,14 @@ class Geometry(object):
cell.add_volume_information(volume_calc)
def export_to_xml(self, path='geometry.xml'):
"""Create a geometry.xml file that can be used for a simulation.
"""Export geometry to an XML file.
Parameters
----------
path : str
Path to file to write. Defaults to 'geometry.xml'.
"""
# Clear OpenMC written IDs used to optimize XML generation
openmc.universe.WRITTEN_IDS = {}

View file

@ -812,7 +812,12 @@ class Materials(cv.CheckedList):
self._materials_file.append(xml_element)
def export_to_xml(self, path='materials.xml'):
"""Create a materials.xml file that can be used for a simulation.
"""Export material collection to an XML file.
Parameters
----------
path : str
Path to file to write. Defaults to 'materials.xml'.
"""

View file

@ -617,10 +617,14 @@ class Plots(cv.CheckedList):
self._plots_file.append(xml_element)
def export_to_xml(self, path='plots.xml'):
"""Create a plots.xml file that can be used by OpenMC.
"""Export plot specifications to an XML file.
Parameters
----------
path : str
Path to file to write. Defaults to 'plots.xml'.
"""
# Reset xml element tree
self._plots_file.clear()

View file

@ -15,8 +15,7 @@ if sys.version_info[0] >= 3:
class Settings(object):
"""Settings file used for an OpenMC simulation. Corresponds directly to the
settings.xml input file.
"""Settings used for an OpenMC simulation.
Attributes
----------
@ -1119,7 +1118,12 @@ class Settings(object):
elem.append(r.to_xml_element())
def export_to_xml(self, path='settings.xml'):
"""Create a settings.xml file that can be used for a simulation.
"""Export simulation settings to an XML file.
Parameters
----------
path : str
Path to file to write. Defaults to 'settings.xml'.
"""

View file

@ -676,19 +676,10 @@ class StatePoint(object):
raise ValueError(msg)
for tally_id, tally in self.tallies.items():
summary_tally = summary.tallies[tally_id]
tally.name = summary_tally.name
tally.name = summary.tally_names[tally_id]
tally.with_summary = True
for tally_filter in tally.filters:
summary_filter = summary_tally.find_filter(type(tally_filter))
if isinstance(tally_filter, openmc.SurfaceFilter):
surface_ids = []
for bin in tally_filter.bins:
surface_ids.append(bin)
tally_filter.bins = surface_ids
if isinstance(tally_filter, (openmc.CellFilter,
openmc.DistribcellFilter)):
distribcell_ids = []
@ -697,8 +688,9 @@ class StatePoint(object):
tally_filter.bins = distribcell_ids
if isinstance(tally_filter, (openmc.DistribcellFilter)):
tally_filter.distribcell_paths = \
summary_filter.distribcell_paths
cell_id = tally_filter.bins[0]
cell = summary.get_cell_by_id(cell_id)
tally_filter.distribcell_paths = cell.distribcell_paths
if isinstance(tally_filter, openmc.UniverseFilter):
universe_ids = []

View file

@ -297,10 +297,13 @@ class Summary(object):
cell.region = Region.from_expression(
region, {s.id: s for s in self.surfaces.values()})
# Get the distribcell index
ind = self._f['geometry/cells'][key]['distribcell_index'].value
if ind != 0:
# Get the distribcell data
if 'distribcell_index' in self._f['geometry/cells'][key]:
ind = self._f['geometry/cells'][key]['distribcell_index'].value
cell.distribcell_index = ind
paths = self._f['geometry/cells'][key]['paths'][...]
paths = [str(path.decode()) for path in paths]
cell.distribcell_paths = paths
# Add the Cell to the global dictionary of all Cells
self.cells[index] = cell
@ -520,18 +523,15 @@ class Summary(object):
self.openmc_geometry.root_universe = root_universe
def _read_tallies(self):
# Initialize dictionaries for the Tallies
# Initialize a dictionary for the tally names
# Keys - Tally IDs
# Values - Tally objects
self.tallies = {}
# Values - Tally names
self.tally_names = {}
# Read the number of tallies
if 'tallies' not in self._f:
self.n_tallies = 0
return
self.n_tallies = self._f['tallies/n_tallies'].value
# OpenMC Tally keys
all_keys = self._f['tallies/'].keys()
tally_keys = [key for key in all_keys if 'tally' in key]
@ -545,34 +545,7 @@ class Summary(object):
# Read Tally name metadata
tally_name = self._f['{0}/name'.format(subbase)].value.decode()
# Create Tally object and assign basic properties
tally = openmc.Tally(tally_id, tally_name)
# Read scattering moment order strings (e.g., P3, Y1,2, etc.)
moments = self._f['{0}/moment_orders'.format(subbase)].value
# Read score metadata
scores = self._f['{0}/score_bins'.format(subbase)].value
for j, score in enumerate(scores):
score = score.decode()
# If this is a moment, use generic moment order
pattern = r'-n$|-pn$|-yn$'
score = re.sub(pattern, '-' + moments[j].decode(), score)
tally.scores.append(score)
# Read filter metadata
num_filters = self._f['{0}/n_filters'.format(subbase)].value
# Read all filters
for j in range(1, num_filters+1):
subsubbase = '{0}/filter {1}'.format(subbase, j)
new_filter = openmc.Filter.from_hdf5(self._f[subsubbase])
tally.filters.append(new_filter)
# Add Tally to the global dictionary of all Tallies
self.tallies[tally_id] = tally
self.tally_names[tally_id] = tally_name
def add_volume_information(self, volume_calc):
"""Add volume information to the geometry within the summary file

View file

@ -190,7 +190,7 @@ for filename in ace_libraries:
thermal.export_to_hdf5(outfile, 'w')
# Register with library
library.register_file(outfile, 'thermal')
library.register_file(outfile)
# Add data to list
nuclides[name] = outfile

View file

@ -14,7 +14,7 @@ module constants
integer, parameter :: REVISION_STATEPOINT = 15
integer, parameter :: REVISION_PARTICLE_RESTART = 1
integer, parameter :: REVISION_TRACK = 1
integer, parameter :: REVISION_SUMMARY = 3
integer, parameter :: REVISION_SUMMARY = 4
character(10), parameter :: MULTIPOLE_VERSION = "v0.2"
! ============================================================================

View file

@ -239,10 +239,10 @@ contains
xyz_ll_plot = pl % origin
xyz_ur_plot = pl % origin
xyz_ll_plot(outer) = pl % origin(1) - pl % width(1) / TWO
xyz_ll_plot(inner) = pl % origin(2) - pl % width(2) / TWO
xyz_ur_plot(outer) = pl % origin(1) + pl % width(1) / TWO
xyz_ur_plot(inner) = pl % origin(2) + pl % width(2) / TWO
xyz_ll_plot(outer) = pl % origin(outer) - pl % width(1) / TWO
xyz_ll_plot(inner) = pl % origin(inner) - pl % width(2) / TWO
xyz_ur_plot(outer) = pl % origin(outer) + pl % width(1) / TWO
xyz_ur_plot(inner) = pl % origin(inner) + pl % width(2) / TWO
width = xyz_ur_plot - xyz_ll_plot

View file

@ -2,8 +2,8 @@ module summary
use constants
use endf, only: reaction_name
use geometry_header, only: Cell, Universe, Lattice, RectLattice, &
&HexLattice
use geometry_header, only: BASE_UNIVERSE, Cell, Universe, Lattice, &
RectLattice, HexLattice
use global
use hdf5_interface
use material_header, only: Material
@ -13,6 +13,7 @@ module summary
use surface_header
use string, only: to_str
use tally_header, only: TallyObject
use tally_filter, only: find_offset
use hdf5
@ -150,7 +151,7 @@ contains
subroutine write_geometry(file_id)
integer(HID_T), intent(in) :: file_id
integer :: i, j, k, m
integer :: i, j, k, m, offset
integer, allocatable :: lattice_universes(:,:,:)
integer, allocatable :: cell_materials(:)
real(8), allocatable :: cell_temperatures(:)
@ -161,6 +162,8 @@ contains
integer(HID_T) :: lattices_group, lattice_group
real(8), allocatable :: coeffs(:)
character(REGION_SPEC_LEN) :: region_spec
character(MAX_LINE_LEN), allocatable :: paths(:)
character(MAX_LINE_LEN) :: path
type(Cell), pointer :: c
class(Surface), pointer :: s
type(Universe), pointer :: u
@ -266,7 +269,21 @@ contains
end do
call write_dataset(cell_group, "region", adjustl(region_spec))
call write_dataset(cell_group, "distribcell_index", c % distribcell_index)
! Write distribcell data
if (c % distribcell_index /= NONE) then
call write_dataset(cell_group, "distribcell_index", &
c % distribcell_index)
allocate(paths(c % instances))
do k = 1, c % instances
path = ''
offset = 1
call find_offset(i, universes(BASE_UNIVERSE), k, offset, path)
paths(k) = path
end do
call write_dataset(cell_group, "paths", paths)
deallocate(paths)
end if
call close_group(cell_group)
end do CELL_LOOP
@ -566,119 +583,21 @@ contains
subroutine write_tallies(file_id)
integer(HID_T), intent(in) :: file_id
integer :: i, j, k
integer :: n_order ! loop index for moment orders
integer :: nm_order ! loop index for Ynm moment orders
integer :: i
integer(HID_T) :: tallies_group
integer(HID_T) :: mesh_group
integer(HID_T) :: tally_group
integer(HID_T) :: filter_group
character(20), allocatable :: str_array(:)
type(RegularMesh), pointer :: m
type(TallyObject), pointer :: t
tallies_group = create_group(file_id, "tallies")
! Write total number of meshes
call write_dataset(tallies_group, "n_meshes", n_meshes)
! Write information for meshes
MESH_LOOP: do i = 1, n_meshes
m => meshes(i)
mesh_group = create_group(tallies_group, "mesh " // trim(to_str(m%id)))
! Write internal OpenMC index for this mesh
call write_dataset(mesh_group, "index", i)
! Write type and number of dimensions
call write_dataset(mesh_group, "type", "regular")
! Write mesh information
call write_dataset(mesh_group, "dimension", m%dimension)
call write_dataset(mesh_group, "lower_left", m%lower_left)
call write_dataset(mesh_group, "upper_right", m%upper_right)
call write_dataset(mesh_group, "width", m%width)
call close_group(mesh_group)
end do MESH_LOOP
! Write number of tallies
call write_dataset(tallies_group, "n_tallies", n_tallies)
TALLY_METADATA: do i = 1, n_tallies
! Get pointer to tally
t => tallies(i)
tally_group = create_group(tallies_group, "tally " // trim(to_str(t%id)))
! Write internal OpenMC index for this tally
call write_dataset(tally_group, "index", i)
tally_group = create_group(tallies_group, "tally " &
// trim(to_str(t % id)))
! Write the name for this tally
call write_dataset(tally_group, "name", t%name)
! Write number of filters
call write_dataset(tally_group, "n_filters", size(t % filters))
FILTER_LOOP: do j = 1, size(t % filters)
filter_group = create_group(tally_group, "filter " // trim(to_str(j)))
call t % filters(j) % obj % to_summary(filter_group)
call close_group(filter_group)
end do FILTER_LOOP
! Create temporary array for nuclide bins
allocate(str_array(t%n_nuclide_bins))
NUCLIDE_LOOP: do j = 1, t%n_nuclide_bins
if (t%nuclide_bins(j) > 0) then
str_array(j) = nuclides(t % nuclide_bins(j)) % name
else
str_array(j) = 'total'
end if
end do NUCLIDE_LOOP
! Write and deallocate nuclide bins
call write_dataset(tally_group, "nuclides", str_array)
deallocate(str_array)
! Write number of score bins
call write_dataset(tally_group, "n_score_bins", t%n_score_bins)
allocate(str_array(size(t%score_bins)))
do j = 1, size(t%score_bins)
str_array(j) = reaction_name(t%score_bins(j))
end do
call write_dataset(tally_group, "score_bins", str_array)
deallocate(str_array)
! Write explicit moment order strings for each score bin
k = 1
allocate(str_array(t%n_score_bins))
MOMENT_LOOP: do j = 1, t%n_user_score_bins
select case(t%score_bins(k))
case (SCORE_SCATTER_N, SCORE_NU_SCATTER_N)
str_array(k) = 'P' // trim(to_str(t%moment_order(k)))
k = k + 1
case (SCORE_SCATTER_PN, SCORE_NU_SCATTER_PN)
do n_order = 0, t%moment_order(k)
str_array(k) = 'P' // trim(to_str(n_order))
k = k + 1
end do
case (SCORE_SCATTER_YN, SCORE_NU_SCATTER_YN, SCORE_FLUX_YN, &
SCORE_TOTAL_YN)
do n_order = 0, t%moment_order(k)
do nm_order = -n_order, n_order
str_array(k) = 'Y' // trim(to_str(n_order)) // ',' // &
trim(to_str(nm_order))
k = k + 1
end do
end do
case default
str_array(k) = ''
k = k + 1
end select
end do MOMENT_LOOP
call write_dataset(tally_group, "moment_orders", str_array)
deallocate(str_array)
call write_dataset(tally_group, "name", t % name)
call close_group(tally_group)
end do TALLY_METADATA

View file

@ -80,7 +80,6 @@ module tally_filter
contains
procedure :: get_next_bin => get_next_bin_distribcell
procedure :: to_statepoint => to_statepoint_distribcell
procedure :: to_summary => to_summary_distribcell
procedure :: text_label => text_label_distribcell
procedure :: initialize => initialize_distribcell
end type DistribcellFilter
@ -714,36 +713,6 @@ contains
call write_dataset(filter_group, "bins", this % cell )
end subroutine to_statepoint_distribcell
subroutine to_summary_distribcell(this, filter_group)
class(DistribcellFilter), intent(in) :: this
integer(HID_T), intent(in) :: filter_group
integer :: offset, k
character(MAX_LINE_LEN), allocatable :: paths(:)
character(MAX_LINE_LEN) :: path
call write_dataset(filter_group, "type", "distribcell")
call write_dataset(filter_group, "n_bins", this % n_bins)
call write_dataset(filter_group, "bins", this % cell )
! Write paths to reach each distribcell instance
! Allocate array of strings for each distribcell path
allocate(paths(this % n_bins))
! Store path for each distribcell instance
do k = 1, this % n_bins
path = ''
offset = 1
call find_offset(this % cell, universes(BASE_UNIVERSE), k, offset, path)
paths(k) = path
end do
! Write array of distribcell paths to summary file
call write_dataset(filter_group, "paths", paths)
deallocate(paths)
end subroutine to_summary_distribcell
subroutine initialize_distribcell(this)
class(DistribcellFilter), intent(inout) :: this

View file

@ -18,7 +18,6 @@ module tally_filter_header
contains
procedure(get_next_bin_), deferred :: get_next_bin
procedure(to_statepoint_), deferred :: to_statepoint
procedure :: to_summary => filter_to_summary
procedure(text_label_), deferred :: text_label
procedure :: initialize => filter_initialize
end type TallyFilter
@ -82,18 +81,6 @@ module tally_filter_header
contains
!===============================================================================
! TO_SUMMARY writes all the information needed to reconstruct the filter to the
! given filter_group. If this procedure is not overridden by the derived class,
! then it will call to_statepoint by default.
subroutine filter_to_summary(this, filter_group)
class(TallyFilter), intent(in) :: this
integer(HID_T), intent(in) :: filter_group
call this % to_statepoint(filter_group)
end subroutine filter_to_summary
!===============================================================================
! INITIALIZE sets up any internal data, as necessary. If this procedure is not
! overriden by the derived class, then it will do nothing by default.