From 3fa5047ef59381a13dc284c82874235373375993 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sun, 8 Feb 2015 22:33:55 -0500 Subject: [PATCH 1/9] Added lattice outer universe capability --- src/constants.F90 | 3 +++ src/geometry.F90 | 41 ++++++++++++++++++++--------------------- src/geometry_header.F90 | 2 +- src/initialize.F90 | 27 ++++++++++++++++++--------- src/input_xml.F90 | 22 ++++++++++++++-------- 5 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/constants.F90 b/src/constants.F90 index 7e30bd07ea..de3d5b1807 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -127,6 +127,9 @@ module constants SURF_CONE_Y = 10, & ! Cone parallel to y-axis SURF_CONE_Z = 11 ! Cone parallel to z-axis + ! Flag to say that the outside of a lattice is not defined + integer, parameter :: NO_OUTER_UNIV = -22 + ! Maximum number of lost particles integer, parameter :: MAX_LOST_PARTICLES = 10 diff --git a/src/geometry.F90 b/src/geometry.F90 index c79f0e6119..b8d23f24fb 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -280,17 +280,17 @@ contains p % coord % xyz = xyz else - - ! We're outside the lattice, so treat this as a normal cell with - ! the material specified for the outside - +! +! ! We're outside the lattice, so treat this as a normal cell with +! ! the material specified for the outside +! outside_lattice = .true. - p % last_material = p % material - p % material = c % material - - ! We'll still make a new coordinate for the particle, as - ! distance_to_boundary will still need to track through lattice - ! widths even though there's nothing in them but this material +! p % last_material = p % material +! p % material = c % material +! +! ! We'll still make a new coordinate for the particle, as +! ! distance_to_boundary will still need to track through lattice +! ! widths even though there's nothing in them but this material end if @@ -322,13 +322,13 @@ contains if (.not. outside_lattice) then p % coord % next % universe = lat % universes(i_x,i_y,i_z) else - - ! Set universe as the same for subsequent calls to find_cell - p % coord % next % universe = p % coord % universe - - ! Set coord cell for calls to distance_to_boundary - p % coord % next % cell = index_cell - + if (lat % outer == NO_OUTER_UNIV) then + call fatal_error("A particle is outside latttice " & + &// trim(to_str(lat % id)) // " but the lattice has no & + &defined outer universe.") + else + p % coord % next % universe = lat % outer + end if end if ! Move particle to next level @@ -336,10 +336,9 @@ contains end if - if (.not. outside_lattice) then - call find_cell(p, found) - if (.not. found) exit - end if + ! Find in the next lowest coordinate level. + call find_cell(p, found) + if (.not. found) exit end if diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index 93863865c4..f085c64728 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -30,7 +30,7 @@ module geometry_header real(8), allocatable :: lower_left(:) ! lower-left corner of lattice real(8), allocatable :: width(:) ! width of each lattice cell integer, allocatable :: universes(:,:,:) ! specified universes - integer :: outside ! material to fill area outside + integer :: outer ! universe to tile outside the lat end type Lattice !=============================================================================== diff --git a/src/initialize.F90 b/src/initialize.F90 index 1ab34ca0bf..297bf192f3 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -607,17 +607,17 @@ contains c % fill = universe_dict % get_key(id) elseif (lattice_dict % has_key(id)) then lid = lattice_dict % get_key(id) - mid = lattices(lid) % outside +! mid = lattices(lid) % outside c % type = CELL_LATTICE c % fill = lid - if (mid == MATERIAL_VOID) then - c % material = mid - else if (material_dict % has_key(mid)) then - c % material = material_dict % get_key(mid) - else - call fatal_error("Could not find material " // trim(to_str(mid)) & - &// " specified on lattice " // trim(to_str(lid))) - end if +! if (mid == MATERIAL_VOID) then +! c % material = mid +! else if (material_dict % has_key(mid)) then +! c % material = material_dict % get_key(mid) +! else +! call fatal_error("Could not find material " // trim(to_str(mid)) & +! &// " specified on lattice " // trim(to_str(lid))) +! end if else call fatal_error("Specified fill " // trim(to_str(id)) // " on cell "& @@ -654,6 +654,15 @@ contains end do end do + if (lat % outer /= NO_OUTER_UNIV) then + if (universe_dict % has_key(id)) then + lat % outer = universe_dict % get_key(id) + else + call fatal_error("Invalid universe number " // trim(to_str(id)) & + &// " specified on lattice " // trim(to_str(lat % id))) + end if + end if + end do TALLY_LOOP: do i = 1, n_tallies diff --git a/src/input_xml.F90 b/src/input_xml.F90 index e5c21bdc1c..dffb191bf6 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1341,14 +1341,20 @@ contains deallocate(temp_int_array) ! Read material for area outside lattice - lat % outside = MATERIAL_VOID - if (check_for_node(node_lat, "outside")) then - call get_node_value(node_lat, "outside", mid) - if (mid == 0 .or. mid == MATERIAL_VOID) then - lat % outside = MATERIAL_VOID - else - lat % outside = mid - end if +! lat % outside = MATERIAL_VOID +! if (check_for_node(node_lat, "outside")) then +! call get_node_value(node_lat, "outside", mid) +! if (mid == 0 .or. mid == MATERIAL_VOID) then +! lat % outside = MATERIAL_VOID +! else +! lat % outside = mid +! end if +! end if + + ! Read outer universe for area outside lattice. + lat % outer = NO_OUTER_UNIV + if (check_for_node(node_lat, "outer")) then + call get_node_value(node_lat, "outer", lat % outer) end if ! Add lattice to dictionary From 6dadd0231ded26a5343060056089b3626b4d14a2 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sun, 8 Feb 2015 23:02:55 -0500 Subject: [PATCH 2/9] Updated docs for lattice outer universes --- docs/source/usersguide/input.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/source/usersguide/input.rst b/docs/source/usersguide/input.rst index c834bbc92e..20fd66e2f7 100644 --- a/docs/source/usersguide/input.rst +++ b/docs/source/usersguide/input.rst @@ -833,11 +833,13 @@ sub-elements: *Default*: None - :outside: - The unique integer identifier of a material that is to be used to fill all - space outside of the lattice. This element is optional. + :outer: + The unique integer identifier of a universe that will be used to fill all + space outside of the lattice. The universe will be tiled repeatedly as if + it were placed in a lattice of infinite size. This element is optional. - *Default*: The region outside the defined lattice is treated as void. + *Default*: An error will be raised if a particle leaves a lattice with no + outer universe. :universes: A list of the universe numbers that fill each cell of the lattice. From 6482b8c1b124fb08f7c5e419dd607a6bd5cb71da Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 9 Feb 2015 23:22:53 -0500 Subject: [PATCH 3/9] Added an automated update script to lattice outer --- src/utils/update_lattices.py | 202 +++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100755 src/utils/update_lattices.py diff --git a/src/utils/update_lattices.py b/src/utils/update_lattices.py new file mode 100755 index 0000000000..0d1bf8d9e2 --- /dev/null +++ b/src/utils/update_lattices.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python +"""Update lattices in geometry .xml files to the latest format. + +Usage information can be obtained by running 'update_lattices.py --help': + +usage: update_lattices.py [-h] IN [IN ...] + +Update lattices in geometry.xml files to the latest format. This will remove +'outside' attributes/elements and replace them with 'outer' attributes. 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 +from random import randint +from shutil import move +import xml.etree.ElementTree as ET + + +def parse_args(): + """Read the input files from the commandline.""" + # Create argument parser. + parser = argparse.ArgumentParser(description="Update lattices in " + "geometry.xml files to the latest format. This will remove 'outside' " + "attributes/elements and replace them with 'outer' attributes. Note " + "that this script will not delete the given files; it will append " + "'.original' to the given files and write new ones.") + 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) + if 'id' in lat.attrib: + return int(lat.attrib['id'].strip()) + elif any([child.tag == 'id' for child in lat]): + elem = lat.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. + if 'outside' in lat.attrib: + material = lat.attrib['outside'].strip() + del lat.attrib['outside'] + + # Check subelements. + elif any([child.tag == 'outside' for child in lat]): + elem = lat.find('outside') + material = elem.text.strip() + lat.remove(elem) + + # No 'outside' specified. This means the outside is a void. + else: + material = 'void' + + return material + + + +if __name__ == '__main__': + args = parse_args() + for fname in args.input: + # Parse the XML data. + tree = ET.parse(fname) + root = tree.getroot() + + # Ignore files that do not contain lattices. + if all([child.tag != 'lattice' for child in root]): continue + + # 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) + + # Update the definitions of each lattice + for lat in root.iter('lattice'): + # Get the lattice's id. + lat_id = get_lat_id(lat) + + # Pop the 'outside' material. + material = pop_lat_outside(lat) + + # 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) + + # Move the original geometry file to preserve it. + move(fname, ''.join((fname, '.original'))) + + # Write a new geometry file. + tree.write(fname) From f15d8e318c228612d8032d26e0b5e8586d7749aa Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 9 Feb 2015 23:30:05 -0500 Subject: [PATCH 4/9] Updated tests for lattice outer universes --- tests/test_infinite_cell/geometry.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_infinite_cell/geometry.xml b/tests/test_infinite_cell/geometry.xml index 77ef6110a9..b16d7f497c 100644 --- a/tests/test_infinite_cell/geometry.xml +++ b/tests/test_infinite_cell/geometry.xml @@ -4,12 +4,13 @@ + width="2.0 2.0" outer="22"> 11 12 12 11 + From 2df30a7251d34e3fbfd044f2ec6907ce62e62857 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 9 Feb 2015 23:33:46 -0500 Subject: [PATCH 5/9] Removed commented-out code from lattice outer --- src/geometry.F90 | 10 ---------- src/initialize.F90 | 10 ---------- src/input_xml.F90 | 11 ----------- 3 files changed, 31 deletions(-) diff --git a/src/geometry.F90 b/src/geometry.F90 index b8d23f24fb..54b131d626 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -280,17 +280,7 @@ contains p % coord % xyz = xyz else -! -! ! We're outside the lattice, so treat this as a normal cell with -! ! the material specified for the outside -! outside_lattice = .true. -! p % last_material = p % material -! p % material = c % material -! -! ! We'll still make a new coordinate for the particle, as -! ! distance_to_boundary will still need to track through lattice -! ! widths even though there's nothing in them but this material end if diff --git a/src/initialize.F90 b/src/initialize.F90 index 297bf192f3..3f6aa7c52e 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -607,18 +607,8 @@ contains c % fill = universe_dict % get_key(id) elseif (lattice_dict % has_key(id)) then lid = lattice_dict % get_key(id) -! mid = lattices(lid) % outside c % type = CELL_LATTICE c % fill = lid -! if (mid == MATERIAL_VOID) then -! c % material = mid -! else if (material_dict % has_key(mid)) then -! c % material = material_dict % get_key(mid) -! else -! call fatal_error("Could not find material " // trim(to_str(mid)) & -! &// " specified on lattice " // trim(to_str(lid))) -! end if - else call fatal_error("Specified fill " // trim(to_str(id)) // " on cell "& &// trim(to_str(c % id)) // " is neither a universe nor a & diff --git a/src/input_xml.F90 b/src/input_xml.F90 index dffb191bf6..7364cfe906 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1340,17 +1340,6 @@ contains end do deallocate(temp_int_array) - ! Read material for area outside lattice -! lat % outside = MATERIAL_VOID -! if (check_for_node(node_lat, "outside")) then -! call get_node_value(node_lat, "outside", mid) -! if (mid == 0 .or. mid == MATERIAL_VOID) then -! lat % outside = MATERIAL_VOID -! else -! lat % outside = mid -! end if -! end if - ! Read outer universe for area outside lattice. lat % outer = NO_OUTER_UNIV if (check_for_node(node_lat, "outer")) then From a017ac0ccc1fc7b716376f040f25d24f9a086c48 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Tue, 10 Feb 2015 00:03:47 -0500 Subject: [PATCH 6/9] Added error message for lattices with 'outside' --- src/initialize.F90 | 2 +- src/input_xml.F90 | 9 ++++++++- src/utils/update_lattices.py | 1 - 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/initialize.F90 b/src/initialize.F90 index 3f6aa7c52e..921d34ff47 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -549,7 +549,7 @@ contains integer :: j ! index for various purposes integer :: k ! loop index for lattices integer :: m ! loop index for lattices - integer :: mid, lid ! material and lattice IDs + integer :: lid ! lattice IDs integer :: n_x, n_y, n_z ! size of lattice integer :: i_array ! index in surfaces/materials array integer :: id ! user-specified id diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 7364cfe906..27f090478b 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -904,7 +904,6 @@ contains integer :: universe_num integer :: n_cells_in_univ integer :: coeffs_reqd - integer :: mid integer :: temp_int_array3(3) integer, allocatable :: temp_int_array(:) real(8) :: phi, theta, psi @@ -1346,6 +1345,14 @@ contains call get_node_value(node_lat, "outer", lat % outer) end if + ! Check for 'outside' nodes which are no longer supported. + if (check_for_node(node_lat, "outside")) then + call fatal_error("The use of 'outside' in lattices is no longer & + &supported. Instead, use 'outer' which defines a universe rather & + &than a material. The utility openmc/src/utils/update_lattices.py& + & can be used automatically replace 'outside' with 'outer'.") + end if + ! Add lattice to dictionary call lattice_dict % add_key(lat % id, i) diff --git a/src/utils/update_lattices.py b/src/utils/update_lattices.py index 0d1bf8d9e2..efcf8c631b 100755 --- a/src/utils/update_lattices.py +++ b/src/utils/update_lattices.py @@ -154,7 +154,6 @@ def pop_lat_outside(lattice_element): return material - if __name__ == '__main__': args = parse_args() for fname in args.input: From 38a73b23194b8492f9030f1e5333a75cc30369f2 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Tue, 10 Feb 2015 00:15:16 -0500 Subject: [PATCH 7/9] Fixed typo for lattice outer universe --- src/initialize.F90 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/initialize.F90 b/src/initialize.F90 index 921d34ff47..180e509427 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -645,10 +645,11 @@ contains end do if (lat % outer /= NO_OUTER_UNIV) then - if (universe_dict % has_key(id)) then - lat % outer = universe_dict % get_key(id) + if (universe_dict % has_key(lat % outer)) then + lat % outer = universe_dict % get_key(lat % outer) else - call fatal_error("Invalid universe number " // trim(to_str(id)) & + call fatal_error("Invalid universe number " & + &// trim(to_str(lat % outer)) & &// " specified on lattice " // trim(to_str(lat % id))) end if end if From eaa149b7ad04abf2a4a3d8b7a4a277ae3ccd5b65 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Tue, 10 Feb 2015 12:14:01 -0500 Subject: [PATCH 8/9] Updated relaxNG for latticeo outer universes --- src/relaxng/geometry.rnc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/relaxng/geometry.rnc b/src/relaxng/geometry.rnc index 62827f04c1..e93c8a9a83 100644 --- a/src/relaxng/geometry.rnc +++ b/src/relaxng/geometry.rnc @@ -30,6 +30,6 @@ element geometry { (element lower_left { list { xsd:double+ } } | attribute lower_left { list { xsd:double+ } }) & (element width { list { xsd:double+ } } | attribute width { list { xsd:double+ } }) & (element universes { list { xsd:int+ } } | attribute universes { list { xsd:int+ } }) & - (element outside { xsd:int } | attribute outside { xsd:int })? + (element outer { xsd:int } | attribute outer { xsd:int })? }* } From 43c7bb1a6253eeb9baccb40534a395ad6653e77c Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Wed, 11 Feb 2015 20:28:21 -0500 Subject: [PATCH 9/9] Small updates for #359 --- src/CMakeLists.txt | 3 + src/constants.F90 | 2 +- src/geometry.F90 | 2 +- src/initialize.F90 | 2 +- src/input_xml.F90 | 6 +- .../{update_lattices.py => update_inputs.py} | 137 +++++++++++------- 6 files changed, 93 insertions(+), 59 deletions(-) rename src/utils/{update_lattices.py => update_inputs.py} (56%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 06c044e42c..dd920bfcc0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -269,6 +269,9 @@ install(PROGRAMS utils/statepoint_histogram.py install(PROGRAMS utils/statepoint_meshplot.py DESTINATION bin RENAME statepoint_meshplot) +install(PROGRAMS utils/update_inputs.py + DESTINATION bin + RENAME update_inputs) install(FILES ../man/man1/openmc.1 DESTINATION share/man/man1) install(FILES ../LICENSE DESTINATION "share/doc/${program}/copyright") diff --git a/src/constants.F90 b/src/constants.F90 index de3d5b1807..527284803c 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -128,7 +128,7 @@ module constants SURF_CONE_Z = 11 ! Cone parallel to z-axis ! Flag to say that the outside of a lattice is not defined - integer, parameter :: NO_OUTER_UNIV = -22 + integer, parameter :: NO_OUTER_UNIVERSE = -22 ! Maximum number of lost particles integer, parameter :: MAX_LOST_PARTICLES = 10 diff --git a/src/geometry.F90 b/src/geometry.F90 index 54b131d626..b7116ebc57 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -312,7 +312,7 @@ contains if (.not. outside_lattice) then p % coord % next % universe = lat % universes(i_x,i_y,i_z) else - if (lat % outer == NO_OUTER_UNIV) then + if (lat % outer == NO_OUTER_UNIVERSE) then call fatal_error("A particle is outside latttice " & &// trim(to_str(lat % id)) // " but the lattice has no & &defined outer universe.") diff --git a/src/initialize.F90 b/src/initialize.F90 index 180e509427..ec64a1f19b 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -644,7 +644,7 @@ contains end do end do - if (lat % outer /= NO_OUTER_UNIV) then + if (lat % outer /= NO_OUTER_UNIVERSE) then if (universe_dict % has_key(lat % outer)) then lat % outer = universe_dict % get_key(lat % outer) else diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 27f090478b..292b32e066 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1340,7 +1340,7 @@ contains deallocate(temp_int_array) ! Read outer universe for area outside lattice. - lat % outer = NO_OUTER_UNIV + lat % outer = NO_OUTER_UNIVERSE if (check_for_node(node_lat, "outer")) then call get_node_value(node_lat, "outer", lat % outer) end if @@ -1349,8 +1349,8 @@ contains if (check_for_node(node_lat, "outside")) then call fatal_error("The use of 'outside' in lattices is no longer & &supported. Instead, use 'outer' which defines a universe rather & - &than a material. The utility openmc/src/utils/update_lattices.py& - & can be used automatically replace 'outside' with 'outer'.") + &than a material. The utility openmc/src/utils/update_inputs.py & + &can be used automatically replace 'outside' with 'outer'.") end if ! Add lattice to dictionary diff --git a/src/utils/update_lattices.py b/src/utils/update_inputs.py similarity index 56% rename from src/utils/update_lattices.py rename to src/utils/update_inputs.py index efcf8c631b..cd6f98b63c 100755 --- a/src/utils/update_lattices.py +++ b/src/utils/update_inputs.py @@ -1,7 +1,7 @@ #!/usr/bin/env python -"""Update lattices in geometry .xml files to the latest format. +"""Update OpenMC's input XML files to the latest format. -Usage information can be obtained by running 'update_lattices.py --help': +Usage information can be obtained by running 'update_inputs.py --help': usage: update_lattices.py [-h] IN [IN ...] @@ -26,14 +26,27 @@ from shutil import move import xml.etree.ElementTree as ET +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 + with lattices containing 'outer' attributes, and the appropriate + cells/universes will be added. +""" + + def parse_args(): """Read the input files from the commandline.""" # Create argument parser. - parser = argparse.ArgumentParser(description="Update lattices in " - "geometry.xml files to the latest format. This will remove 'outside' " - "attributes/elements and replace them with 'outer' attributes. Note " - "that this script will not delete the given files; it will append " - "'.original' to the given files and write new ones.") + 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).') @@ -123,10 +136,10 @@ def find_new_id(current_ids, preferred=None): def get_lat_id(lattice_element): """Return the id integer of the lattice_element.""" assert isinstance(lattice_element, ET.Element) - if 'id' in lat.attrib: - return int(lat.attrib['id'].strip()) - elif any([child.tag == 'id' for child in lat]): - elem = lat.find('id') + 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.') @@ -137,15 +150,15 @@ def pop_lat_outside(lattice_element): assert isinstance(lattice_element, ET.Element) # Check attributes. - if 'outside' in lat.attrib: - material = lat.attrib['outside'].strip() - del lat.attrib['outside'] + if 'outside' in lattice_element.attrib: + material = lattice_element.attrib['outside'].strip() + del lattice_element.attrib['outside'] # Check subelements. - elif any([child.tag == 'outside' for child in lat]): - elem = lat.find('outside') + elif any([child.tag == 'outside' for child in lattice_element]): + elem = lattice_element.find('outside') material = elem.text.strip() - lat.remove(elem) + lattice_element.remove(elem) # No 'outside' specified. This means the outside is a void. else: @@ -154,48 +167,66 @@ def pop_lat_outside(lattice_element): return material +def update_geometry(geometry_root): + """Update the given XML geometry tree. Return True if changes were made.""" + root = geometry_root + was_updated = False + + # Ignore files that do not contain lattices. + if all([child.tag != 'lattice' for child in root]): return False + + # 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) + + # Update the definitions of each lattice + for lat in root.iter('lattice'): + # Get the lattice's id. + lat_id = get_lat_id(lat) + + # Ignore lattices that have 'outer' specified. + if any([child.tag == 'outer' for child in lat]): continue + + # Pop the 'outside' material. + material = pop_lat_outside(lat) + + # 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 + + 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 - # Ignore files that do not contain lattices. - if all([child.tag != 'lattice' for child in root]): continue + if root.tag == 'geometry': + was_updated = update_geometry(root) - # 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) + if was_updated: + # Move the original geometry file to preserve it. + move(fname, fname + '.original') - # Update the definitions of each lattice - for lat in root.iter('lattice'): - # Get the lattice's id. - lat_id = get_lat_id(lat) - - # Pop the 'outside' material. - material = pop_lat_outside(lat) - - # 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) - - # Move the original geometry file to preserve it. - move(fname, ''.join((fname, '.original'))) - - # Write a new geometry file. - tree.write(fname) + # Write a new geometry file. + tree.write(fname)