Skip to content

Discussion: OpenMC .xml file support #55

Closed

Description

@yardasol

Whereas Serpent2 uses plain text input files, OpenMC uses .xml files (settings.xml, materials.xml, geometry.xml). A user would typically create these .xml files using the Python API.

This issue can be closed when functions exist within the Depcode class that can work with the .xml files used by OpenMC -- specifically the .xml files that the OpenMC python API creates. These functions should utilize the OpenMC API where possible.

In particular, we want similar functionality as the following functions:

  • Depcode
    • write_depcode_input
    • read_depcode_template
    • insert_path_to_geometry
    • change_sim_par
    • create_iter_matfile
    • write_mat_file
  • Simulation
    • switch_to_next_geometry

Activity

  1. added this to the OpenMC support milestone on Jun 12, 2021
  2. yardasol commented on Jun 18, 2021

    @yardasol
    ContributorAuthor

    Depcode Discussion:

    In my meeting with @munkm today we discussed some aspects of open development and having a discussion about implementation is one of them.

    write_depcode_input

    This function stores the lines of a "template" serpent input file in memory, and modifies parameters such as geometry, neutron population and number of batches, and reactor power based on the current depletion step. The flow for the current algorithm is as follows
    write_depcode_input gv

    To support OpenMC, I can think of two directions:

    1. Create an entirely new function that utilizes the OpenMC API. This may require renaming write_depcode_input to write_serpent_input
    def write_serpent_input(self, temp_file, inp_file, reactor, dts, restart):
        """ Writes prepared data into the serpent code input file.
    
        Parameters
        ----------
        temp_file : str
            Path to user template file for depletion code
        inp_file : str
            Path to input file for depletio ncod rerunning.
        .
        .
        .
        """
        #.
        #.
        #.
    
    
    def write_openmc_input(self, temp_dir, inp_dir, reactor, dts, restart):
        """ Writes prepared data into the openmc code input files.
    
        Parameters
        ----------
        temp_dir : str
            Path to parent directory of template input files for OpenMC.
        inp_dir : str
            Path to parent directory of input files for depletion code rerunning.
        .
        .
        .
        """
        .
        .
        .
    1. Add a keyword argument to the function to specify which depletion code we are using ('serpent' for Serpent, 'openmc' for OpenMC), and then refactor the current function into a series of if-statements OR a collection of helper functions for each depletion code option. Below I have shown the former.
    def write_depcode_input(self, depcode_name, temp_file, inp_file, reactor, dts, restart):
        """ Writes prepared data into the depletion code input file.
    
        Parameters
        ----------
        depcode_name : str
            name for depletion code software. Options are 'openmc', 'serpent'.
        .
        .
        .
        """
        if (depcode_name == "serpent"):
            # repeat body of current write_depcode_input code here
            .
            .
            .
        else if (depcode_name == "openmc"):
            # Use OpenMC API to write prepared data into OpenMC input files
            .
            .
            .

    In either case, the flow for the OpenMC algorithm could look like the following
    write_openmc_input gv

    read_depcode_template

    This function stores the lines of the serpent template input file in memory. OpenMC uses a different xml file for geometry, materials, and settings each. I can think of the following implementation:

    1. Utilize the OpenMC API to initialize these objects from template files
    def read_openmc_templates(self, settings_fname, materials_fname):
        """ reads prepared template input files for openmc fr further
        changes in the files to prepare input files for multiple runs
    
        Parameters
        ----------
        settings_fname : str 
            Path to user template settings file for OpenMC
        materials_fname : str
            Path to user template materials file for OpenMC
    
        Returns
        -------
        settings_xml : openmc.Settings
            OpenMC Settings object
        materials_xml : openmc.Materials
            OpenMC Materials object
        geometry_xml : openmc.Geometry
            OpenMC Geometry object
        """
        settings_xml = openmc.Settings.from_xml(path=settings_fname)
        materials_xml = openmc.Materials.from_xml(path=materials_fname)
        geometry_xml = openmc.Geometry.from_xml(path=str(self.geo_file[0]), materials=materials_xml)
        return settings_xml,materials_xml, geometry_xml

    insert_path_to_geometry

    This function inserts a path in the serpent input file pointing to a serpent geometry file. In OpenMC, geometry input file must be named geometry.xml. Because we don't need to pass the data parameter for the OpenMC case, I can only think of one option for OpenMC support here:

    1. Create an entirely new function that utilizes the OpenMC API. This may require renaming insert_path_to_geometry to something else.
    def create_openmc_geometry(self, geometry_xml):
        """Creates 'geometry.xml' file for OpenMC depcode.
    
        Parameters
        ----------
        geometry_xml : openmc.Geometry
            OpenMC Geometry object
        """
        geometry_xml.export_to_xml()

    change_sim_par

    This function changes the neutron population and number of cycles in the serpent template file to the user-specified neutron population and number of cycles. In OpenMC, the settings.xml file stores these parameters. We can utilize the OpenMC API to create this settings file.

    Because we don't need to pass the data parameter for the OpenMC case, I can only think of one option for OpenMC support here:

    1. Create an entirely new function that utilizes the OpenMC API. This may require renaming change_sim_par to something else.
    def change_openmc_par(self, settings_xml):
        """Creates 'settings.xml' file for OpenMC depcode.
        
        Parameters
        ----------
        settings_xml : openmc.Settings
            OpenMC Settings object
        """
        settings_xml.batches = self.active_cycles + self.inactive_cycles
        settings_xml.inactive = self.inactive_cycles
        settings_xml.particles = self.npop
        settings_xml.export_to_xml()

    create_iter_matfile

    This function creates a rewritable iterative material file based on an initial material file for the SaltProc simulation. It is in serpent format. In OpenMC, the materials.xml file stores the material data. We can utilize the OpenMC API to create this material file.

    Because we don't need to pass the data parameter for the OpenMC case, I can only think of one option for OpenMC support here:

    1. Create an entirely new function that utilizes the OpenMC API. This may require renaming create_iter_matfile to something else.
    def create_openmc_iter_matfile(self, materials_xml):
        """Creates 'materials.xml' file for OpenMC depcode.
        
        Parameters
        ----------
        materials_xml : openmc.Materials
            OpenMC Materials object
        """
        materials_xml.export_to_xml()

    write_mat_file

    This function updates the composition of materials in the serpent materials file after each depletion step. In OpenMC, we can utilize the OpenMC API to do this without having to fuss around with formatting strings and writing directly to files.

    I can think of two ways to add OpenMC support for this function:

    1. Create an entirely new function that utilizes the OpenMC API. This may require renaming write_mat_file to write_serpent_file
    def write_openmc_mat_file(self, dep_dict, mat_file, cumulative_time_at_eds):
        """Writes the iteration material file containing burnable materials
            composition used in depletion runs and updated after each depletion
            step.
    
        Parameters
        ----------
        .
        .
        .
        """
        matf = openmc.Materials()
        for key, value in dep_dict.items():
            mat = openmc.Material(name=key)
            mat.density = dep_dict[key].density
            mat.temperature = dep_dict[key].temp
            mat.volume = dep_dict[key].vol
            for nuc_code, wt_frac in dep_dict[key].com.items():
                mat.add_nuclide(nuc_code,wt_frac, 'wo')
            matf.append(mat)
        matf.export_to_xml(mat_file)
    1. . Add an if-statement to the function to specify which depletion code we are using based on the codename field of the Depcode class ('serpent' for Serpent, 'openmc' for OpenMC), and then refactor the current function into the body of one of the if-statements OR a collection of helper functions for each depletion code option. Below I have shown the former.
    def write_mat_file(self, dep_dict, mat_file, cumulative_time_at_eds):
        """Writes the iteration input file containing burnable materials
            composition used in depletion runs and updated after each depletion
            step.
    
        Parameters
        ----------
        .
        .
        .
        """
        if (self.codename == "serpent"):
            # repeat body of current write_mat_file here
            .
            .
            .
        else if (self.codename == "openmc"):
            # repeat body of proposed write_openmc_mat_file here
            .
            .
            .

    Given that Depcode already has the codename field built-in, I think approach 2 makes more sense for this function.

  3. 1 remaining item

  4. yardasol commented on Jun 22, 2021

    @yardasol
    ContributorAuthor

    @LukeSeifert @munkm @andrewryh please see the above discussion.

  5. self-assigned this
    on Jun 22, 2021
  6. yardasol commented on Jun 22, 2021

    @yardasol
    ContributorAuthor
    2. Add a keyword argument to the function to specify which depletion code we are using ('serpent' for Serpent, 'openmc' for OpenMC), and then refactor the current function into a series of `if`-statements OR a collection of helper functions for each depletion code option. Below I have shown the former.
    
    def write_depcode_input(self, depcode_name, temp_file, inp_file, reactor, dts, restart):
        """ Writes prepared data into the depletion code input file.
    
        Parameters
        ----------
        depcode_name : str
            name for depletion code software. Options are 'openmc', 'serpent'.
        .
        .
        .
        """
        if (depcode_name == "serpent"):
            # repeat body of current write_depcode_input code here
            .
            .
            .
        else if (depcode_name == "openmc"):
            # Use OpenMC API to write prepared data into OpenMC input files
            .
            .
            .

    I just realized that Depcode has a field codename, so the proposed key word argument is redundant. We can still use the if-statement structure though,

  7. yardasol commented on Jun 30, 2021

    @yardasol
    ContributorAuthor

    Refactor Depcode

    In my meeting with @katyhuff on Saturday, we discussed refactoring Depcode to be a parent class and create new classes -- SerpentDepcode and OpenMCDepcode -- as subclasses of Depcode that implement the respective versions of code-specific methods where necessary, and would inherit methods that are generalizable to either depletion code.

    I have looked at the current methods in Depcode and all of them are specifically designed for use with Serpent. I see two ways that we could make Depcode a parent class:

    1. Make Depcode contain only the current class parameters and no methods. Copy all current methods in Depcode to DepcodeSerpent. Perform the suggested implementations above for DepcodeOpenMC.

    2. Make Depcode contain the current class parameters and make it a subclass of abc. We will create abstract methods wherever possible. This will enable a common API for the Depcode class than can be passed to both the DepcodeOpenMC and DepcodeSerpent. This would also mean we avoid having to completely refactor the Simulation class, as well as the other SaltProc classes**.

    Below is an example of what an implementation of approach 2 could look like. Approach 1 would be similar, except we would not import anything from the abc module, nor use abstract classes.

    from abc import ABC, abstractmethod
    
    # I borrows this handy doc function from the OpenMC folks.
    # See https://docs.openmc.org/en/stable/_modules/openmc/deplete/abc.html#Integrator
    def add_params(cls):
        cls.__doc__ += cls._params
        return cls
    
    @add_params
    class Depcode(ABC):
            r"""Abstract class for running depletion simulation code.
            """
            
            _params = r"""
            Parameters
            ----------
            codename : str
                Name of depletion code.
            exec_path : str
                Path to depletion code executable.
            template_fname : str
                Path to user input file for depletion code.
            input_fname : str
                Name of input file for depletion code rerunning.
            iter_matfile : str
                Name of iterative, rewritable material file for depletion code
                rerunning. This file is being modified during simulation.
            geo_file : str or list
                Path to file that contains the reactor geometry.
                List of `str` if reactivity control by
                switching geometry is `On` or just `str` otherwise.
            npop : int
                Size of neutron population per cycle for Monte Carlo.
            active_cycles : int
                Number of active cycles.
            inactive_cycles : int
                Number of inactive cycles.
            """
    
            def __init__(self, ...):
                .
                .
                .
    
            @abstractmethod
            def write_depcode_input(self, ...):
            """ Writes prepared data into the depletion code input file.
                .
                .
                .
            """
    
            @abstractmethod
            def run_depcode(self, cores, nodes):
            """Runs depletion code as subprocess with the given parameters.
            
            Parameters
            ----------
            cores : int
                Number of cores to use for depletion code run.
            nodes : int
                Number of nodes to use for depletion code run.
            """
      
            .
            .
            .
    
    
    @add_params
    def DepcodeSerpent(Depcode):
            r"""Class contains contains information about input, output, geometry, and
            template file for for running Serpent depletion simulation.
            """
            self.codename="Serpent"
    
            .
            .
            .
    
    @add_params
    def DepcodeOpenMC(Depcode):
            r"""Class contains information about input, output, geometry, and
            template file for running OpenMC depletion simulation
            """
            .
            .
            .

    **I am not 100% sure that this is true.

  8. changed the title [-]OpenMC .xml file support[/-] [+]Discussion: OpenMC .xml file support[/+] on Sep 15, 2021
  9. locked and limited conversation to collaborators on Oct 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions