diff --git a/openmc/_xml.py b/openmc/_xml.py
index 32679fd89e..b4e08c7519 100644
--- a/openmc/_xml.py
+++ b/openmc/_xml.py
@@ -1,22 +1,36 @@
-def clean_indentation(element, level=0, spaces_per_level=2):
- """
- copy and paste from https://effbot.org/zone/element-lib.htm#prettyprint
- it basically walks your tree and adds spaces and newlines so the tree is
- printed in a nice way
+def clean_indentation(element, level=0, spaces_per_level=2, trailing_indent=True):
+ """Set indentation of XML element and its sub-elements.
+ Copied and pastee from https://effbot.org/zone/element-lib.htm#prettyprint.
+ It walks your tree and adds spaces and newlines so the tree is
+ printed in a nice way.
+
+ Parameters
+ ----------
+ level : int
+ Indentation level for the element passed in (default 0)
+ spaces_per_level : int
+ Number of spaces per indentation level (default 2)
+ trailing_indent : bool
+ Whether or not to include an indentation after closing the element
+
"""
i = "\n" + level*spaces_per_level*" "
+ # ensure there's awlays some tail for the element passed in
+ if not element.tail:
+ element.tail = ""
+
if len(element):
if not element.text or not element.text.strip():
element.text = i + spaces_per_level*" "
- if not element.tail or not element.tail.strip():
+ if trailing_indent and (not element.tail or not element.tail.strip()):
element.tail = i
for sub_element in element:
clean_indentation(sub_element, level+1, spaces_per_level)
if not sub_element.tail or not sub_element.tail.strip():
sub_element.tail = i
else:
- if level and (not element.tail or not element.tail.strip()):
+ if trailing_indent and level and (not element.tail or not element.tail.strip()):
element.tail = i
diff --git a/openmc/material.py b/openmc/material.py
index c335968002..332f8c9659 100644
--- a/openmc/material.py
+++ b/openmc/material.py
@@ -1449,7 +1449,7 @@ class Materials(cv.CheckedList):
for material in self:
material.make_isotropic_in_lab()
- def _write_xml(self, file, header=True):
+ def _write_xml(self, file, header=True, level=0, spaces_per_level=2, trailing_indent=True):
"""Writes XML content of the materials to an open file handle.
Parameters
@@ -1458,33 +1458,46 @@ class Materials(cv.CheckedList):
Open file handle to write content into.
header : bool
Whether or not to write the XML header
+ level : int
+ Indentation level of materials element
+ spaces_per_level : int
+ Number of spaces per indentation
+ trailing_indentation : bool
+ Whether or not to write a trailing indentation for the materials element
+
"""
+ indentation = level*spaces_per_level*' '
# Write the header and the opening tag for the root element.
if header:
file.write("\n")
- file.write('\n')
+ file.write(indentation+'\n')
# Write the element.
if self.cross_sections is not None:
element = ET.Element('cross_sections')
element.text = str(self.cross_sections)
- clean_indentation(element, level=1)
+ clean_indentation(element, level=level+1)
element.tail = element.tail.strip(' ')
- file.write(' ')
+ file.write((level+1)*spaces_per_level*' ')
reorder_attributes(element) # TODO: Remove when support is Python 3.8+
ET.ElementTree(element).write(file, encoding='unicode')
# Write the elements.
for material in sorted(self, key=lambda x: x.id):
element = material.to_xml_element()
- clean_indentation(element, level=1)
+ clean_indentation(element, level=level+1)
element.tail = element.tail.strip(' ')
- file.write(' ')
+ file.write((level+1)*spaces_per_level*' ')
reorder_attributes(element) # TODO: Remove when support is Python 3.8+
ET.ElementTree(element).write(file, encoding='unicode')
# Write the closing tag for the root element.
- file.write('\n')
+ file.write(indentation+'\n')
+
+ # Write a trailing indentation for the next element
+ # at this level if needed
+ if trailing_indent:
+ file.write(indentation)
def export_to_xml(self, path: PathLike = 'materials.xml'):
diff --git a/openmc/model/model.py b/openmc/model/model.py
index 15effd3e4c..9c38d27c32 100644
--- a/openmc/model/model.py
+++ b/openmc/model/model.py
@@ -552,6 +552,9 @@ class Model:
settings_element = self.settings.to_xml_element(memo)
geometry_element = self.geometry.to_xml_element()
+ xml.clean_indentation(geometry_element, level=1)
+ xml.clean_indentation(settings_element, level=1)
+
# If a materials collection was specified, export it. Otherwise, look
# for all materials in the geometry and use that to automatically build
# a collection.
@@ -567,16 +570,18 @@ class Model:
fh.write("\n")
# Write the materials collection to the open XML file first.
# This will write the XML header also
- materials._write_xml(fh, False)
+ materials._write_xml(fh, False, level=1)
# Write remaining elements as a tree
ET.ElementTree(geometry_element).write(fh, encoding='unicode')
ET.ElementTree(settings_element).write(fh, encoding='unicode')
if self.tallies:
tallies_element = self.tallies.to_xml_element(memo)
+ xml.clean_indentation(tallies_element, level=1, trailing_indent=self.plots)
ET.ElementTree(tallies_element).write(fh, encoding='unicode')
if self.plots:
plots_element = self.plots.to_xml_element()
+ xml.clean_indentation(plots_element, level=1, trailing_indent=False)
ET.ElementTree(plots_element).write(fh, encoding='unicode')
fh.write("\n")
diff --git a/tests/regression_tests/model_xml/inputs_true.dat b/tests/regression_tests/model_xml/inputs_true.dat
index 5aedbfa143..c408026316 100644
--- a/tests/regression_tests/model_xml/inputs_true.dat
+++ b/tests/regression_tests/model_xml/inputs_true.dat
@@ -1,38 +1,67 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- eigenvalue
- 10000
- 10
- 5
-
-
- -4.0 -4.0 -4.0 4.0 4.0 4.0
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ fixed source
+ 10000
+ 1
+
+
+ 0 0 0
+
+
+
+ 14000000.0 1.0
+
+
+ ttb
+ true
+
+ 1000.0
+
+
+
+
+ 16
+
+
+ neutron photon electron positron
+
+
+ 1 2
+ current
+
+
+ 2
+ Al27 total
+ total (n,gamma)
+ tracklength
+
+
+ 2
+ Al27 total
+ total heating (n,gamma)
+ collision
+
+
+ 2
+ Al27 total
+ total heating (n,gamma)
+ analog
+
+