mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-27 13:45:36 -04:00
Merge remote-tracking branch 'upstream/develop' into pyapi_filters
This commit is contained in:
commit
e464e22690
23 changed files with 687 additions and 696 deletions
88
data/convert_mcnp_70.py
Executable file
88
data/convert_mcnp_70.py
Executable 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
77
data/convert_mcnp_71.py
Executable 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)
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -366,6 +366,7 @@ Core Classes
|
|||
openmc.data.ThermalScattering
|
||||
openmc.data.CoherentElastic
|
||||
openmc.data.FissionEnergyRelease
|
||||
openmc.data.DataLibrary
|
||||
|
||||
Angle-Energy Distributions
|
||||
--------------------------
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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 = {}
|
||||
|
||||
|
|
|
|||
|
|
@ -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'.
|
||||
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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'.
|
||||
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = []
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
! ============================================================================
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
131
src/summary.F90
131
src/summary.F90
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue