OpenMC/scripts/openmc-update-inputs

266 lines
8.3 KiB
Text
Raw Permalink Normal View History

#!/usr/bin/env python
2015-02-11 20:28:21 -05:00
"""Update OpenMC's input XML files to the latest format.
Usage information can be obtained by running 'openmc-update-inputs --help':
usage: openmc-update-inputs [-h] IN [IN ...]
Update geometry.xml files to the latest format. This will remove 'outside'
attributes/elements from lattices and replace them with 'outer' attributes. For
'cell' elements, any 'surfaces' attributes/elements will be renamed
'region'. Note that this script will not delete the given files; it will append
'.original' to the given files and write new ones.
positional arguments:
IN Input geometry.xml file(s).
optional arguments:
-h, --help show this help message and exit
"""
from __future__ import print_function
import argparse
2015-02-13 14:20:48 -05:00
from itertools import chain
from random import randint
from shutil import move
import xml.etree.ElementTree as ET
2015-02-11 20:28:21 -05:00
description = "Update OpenMC's input XML files to the latest format."
epilog = """\
If any of the given files do not match the most up-to-date formatting, then they
will be automatically rewritten. The old out-of-date files will not be deleted;
they will be moved to a new file with '.original' appended to their name.
Formatting changes that will be made:
geometry.xml: Lattices containing 'outside' attributes/tags will be replaced
2015-02-11 20:28:21 -05:00
with lattices containing 'outer' attributes, and the appropriate
cells/universes will be added. Any 'surfaces' attributes/elements on a cell
will be renamed 'region'.
2015-02-11 20:28:21 -05:00
"""
def parse_args():
"""Read the input files from the commandline."""
# Create argument parser.
2015-02-11 20:28:21 -05:00
parser = argparse.ArgumentParser(
description=description,
epilog=epilog,
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('input', metavar='IN', type=str, nargs='+',
help='Input geometry.xml file(s).')
# Parse and return commandline arguments.
return parser.parse_args()
def get_universe_ids(geometry_root):
"""Return a set of universe id numbers."""
root = geometry_root
out = {0}
# Get the ids of universes defined by cells.
for cell in root.iter('cell'):
# Get universe attributes.
if 'universe' in cell.attrib:
uid = cell.attrib['universe']
out.add(int(uid))
# Get universe elements.
elif cell.find('universe') is not None:
elem = cell.find('universe')
uid = elem.text
out.add(int(uid))
# Get the ids of universes defined by lattices.
for lat in root.iter('lattice'):
# Get id attributes.
if 'id' in lat.attrib:
uid = lat.attrib['id']
out.add(int(uid))
# Get id elements.
elif lat.find('id') is not None:
elem = lat.find('id')
uid = elem.text
out.add(int(uid))
return out
def get_cell_ids(geometry_root):
"""Return a set of cell id numbers."""
root = geometry_root
out = set()
# Get the ids of universes defined by cells.
for cell in root.iter('cell'):
# Get id attributes.
if 'id' in cell.attrib:
cid = cell.attrib['id']
out.add(int(cid))
# Get id elements.
elif cell.find('id') is not None:
elem = cell.find('id')
cid = elem.text
out.add(int(cid))
return out
def find_new_id(current_ids, preferred=None):
"""Return a new id that is not already present in current_ids."""
distance_from_preferred = 21
max_random_attempts = 10000
# First, try to find an id near the preferred number.
if preferred is not None:
assert isinstance(preferred, int)
for i in range(1, distance_from_preferred):
if (preferred - i not in current_ids) and (preferred - i > 0):
return preferred - i
if (preferred + i not in current_ids) and (preferred + i > 0):
return preferred + i
# If that was unsuccessful, attempt to randomly guess a new id number.
for i in range(max_random_attempts):
num = randint(1, 2147483647)
if num not in current_inds:
return num
# Raise an error if an id was not found.
raise RuntimeError('Could not find a unique id number for a new universe.')
def get_lat_id(lattice_element):
"""Return the id integer of the lattice_element."""
assert isinstance(lattice_element, ET.Element)
2015-02-11 20:28:21 -05:00
if 'id' in lattice_element.attrib:
return int(lattice_element.attrib['id'].strip())
elif any([child.tag == 'id' for child in lattice_element]):
elem = lattice_element.find('id')
return int(elem.text.strip())
else:
raise RuntimeError('Could not find the id for a lattice.')
def pop_lat_outside(lattice_element):
"""Return lattice's outside material and remove from attributes/elements."""
assert isinstance(lattice_element, ET.Element)
# Check attributes.
2015-02-11 20:28:21 -05:00
if 'outside' in lattice_element.attrib:
material = lattice_element.attrib['outside'].strip()
del lattice_element.attrib['outside']
# Check subelements.
2015-02-11 20:28:21 -05:00
elif any([child.tag == 'outside' for child in lattice_element]):
elem = lattice_element.find('outside')
material = elem.text.strip()
2015-02-11 20:28:21 -05:00
lattice_element.remove(elem)
# No 'outside' specified. This means the outside is a void.
else:
material = 'void'
return material
2015-02-11 20:28:21 -05:00
def update_geometry(geometry_root):
"""Update the given XML geometry tree. Return True if changes were made."""
root = geometry_root
was_updated = False
2015-02-11 20:28:21 -05:00
# Get a set of already-used universe and cell ids.
uids = get_universe_ids(root)
cids = get_cell_ids(root)
taken_ids = uids.union(cids)
2015-02-13 14:20:48 -05:00
# Replace 'outside' with 'outer' in lattices.
for lat in chain(root.iter('lattice'), root.iter('hex_lattice')):
2015-02-11 20:28:21 -05:00
# Get the lattice's id.
lat_id = get_lat_id(lat)
2015-02-11 20:28:21 -05:00
# Ignore lattices that have 'outer' specified.
if any([child.tag == 'outer' for child in lat]): continue
2015-02-13 14:20:48 -05:00
if 'outer' in lat.attrib: continue
2015-02-11 20:28:21 -05:00
# Pop the 'outside' material.
material = pop_lat_outside(lat)
2015-02-11 20:28:21 -05:00
# Get an id number for a new outer universe. Ideally, the id should
# be close to the lattice's id.
new_uid = find_new_id(taken_ids, preferred=lat_id)
assert new_uid not in taken_ids
# Add the new universe filled with the old 'outside' material to the
# geometry.
new_cell = ET.Element('cell')
new_cell.attrib['id'] = str(new_uid)
new_cell.attrib['universe'] = str(new_uid)
new_cell.attrib['material'] = material
root.append(new_cell)
taken_ids.add(new_uid)
# Add the new universe to the lattice's 'outer' attribute.
lat.attrib['outer'] = str(new_uid)
was_updated = True
2015-02-13 14:20:48 -05:00
# Remove 'type' from lattice definitions.
for lat in root.iter('lattice'):
elem = lat.find('type')
if elem is not None:
lat.remove(elem)
was_updated = True
if 'type' in lat.attrib:
del lat.attrib['type']
was_updated = True
# Change 'width' to 'pitch' in lattice definitions.
for lat in root.iter('lattice'):
elem = lat.find('width')
if elem is not None:
elem.tag = 'pitch'
was_updated = True
if 'width' in lat.attrib:
lat.attrib['pitch'] = lat.attrib['width']
del lat.attrib['width']
was_updated = True
# Change 'surfaces' to 'region' in cell definitions
for cell in root.iter('cell'):
elem = cell.find('surfaces')
if elem is not None:
elem.tag = 'region'
was_updated = True
if 'surfaces' in cell.attrib:
cell.attrib['region'] = cell.attrib['surfaces']
del cell.attrib['surfaces']
was_updated = True
2015-02-11 20:28:21 -05:00
return was_updated
if __name__ == '__main__':
args = parse_args()
for fname in args.input:
# Parse the XML data.
tree = ET.parse(fname)
root = tree.getroot()
was_updated = False
2015-02-11 20:28:21 -05:00
if root.tag == 'geometry':
was_updated = update_geometry(root)
2015-02-11 20:28:21 -05:00
if was_updated:
# Move the original geometry file to preserve it.
move(fname, fname + '.original')
2015-02-11 20:28:21 -05:00
# Write a new geometry file.
tree.write(fname)