Beispiel #1
0
    def __init__(self, t=0.0, phi=0.0, temp=300.0, tol=1e-7, cwd='',
                 base_tape9=origen22.BASE_TAPE9, xscache=None, 
                 o2exe='o2_therm_linux.exe', *args, **kwargs):
        """Parameters
        ----------
        t : float
            Transmutations time [sec].
        phi : float or array of floats
            Neutron flux vector [n/cm^2/sec].  Currently this must either be 
            a scalar or match the group structure of EAF.
        temp : float, optional
            Temperature [K] of material, defaults to 300.0.
        tol : float
            Tolerance level for chain truncation.
        cwd : str, optional
            Current working directory for origen runs. Defaults to this dir.
        base_tape9 : str or dict, optional
            A base TAPE9.INP file.  If this is a str it is interpreted as a path 
            to a file, which is then read in and parsed.  If this is a dict, it is
            assumed to be in the format described in the main origen22 module.
        xscache : XSCache, optional
            A cross section cache to generate cross sections with.
        o2exe : str, optional
            Name or path to ORIGEN 2.2 executable.
        args : tuple, optional
            Other arguments ignored for compatibility with other Transmuters.
        kwargs : dict, optional
            Other keyword arguments ignored for compatibility with other Transmuters.
        """
        if not isinstance(base_tape9, Mapping):
            base_tape9 = origen22.parse_tape9(tape9=base_tape9)
        self.base_tape9 = base_tape9

        if xscache is None:
            eafds = EAFDataSource()
            eafds.load(temp=temp)
            gs = np.array([eafds.src_group_struct[0], eafds.src_group_struct[-1]])
            eafds.dst_group_struct = gs
            xscache = XSCache(group_struct=gs, data_source_classes=[SimpleDataSource, 
                                                                    NullDataSource])
            xscache.load(temp=temp)
            xscache.data_sources.insert(0, eafds)
        self.xscache = xscache

        self.t = t
        self._phi = None
        self.phi = phi
        self.temp = temp
        self.tol = tol
        self.cwd = os.path.abspath(cwd)
        self.o2exe = o2exe
Beispiel #2
0
    def __init__(self, t=0.0, phi=0.0, temp=300.0, tol=1e-7, cwd='',
                 base_tape9=origen22.BASE_TAPE9, xscache=None, 
                 o2exe='o2_therm_linux.exe', *args, **kwargs):
        """Parameters
        ----------
        t : float
            Transmutations time [sec].
        phi : float or array of floats
            Neutron flux vector [n/cm^2/sec].  Currently this must either be 
            a scalar or match the group structure of EAF.
        temp : float, optional
            Temperature [K] of material, defaults to 300.0.
        tol : float
            Tolerance level for chain truncation.
        cwd : str, optional
            Current working directory for origen runs. Defaults to this dir.
        base_tape9 : str or dict, optional
            A base TAPE9.INP file.  If this is a str it is interpreted as a path 
            to a file, which is then read in and parsed.  If this is a dict, it is
            assumed to be in the format described in the main origen22 module.
        xscache : XSCache, optional
            A cross section cache to generate cross sections with.
        o2exe : str, optional
            Name or path to ORIGEN 2.2 executable.
        args : tuple, optional
            Other arguments ignored for compatibility with other Transmuters.
        kwargs : dict, optional
            Other keyword arguments ignored for compatibility with other Transmuters.
        """
        if not isinstance(base_tape9, Mapping):
            base_tape9 = origen22.parse_tape9(tape9=base_tape9)
        self.base_tape9 = base_tape9

        if xscache is None:
            eafds = EAFDataSource()
            eafds.load(temp=temp)
            gs = np.array([eafds.src_group_struct[0], eafds.src_group_struct[-1]])
            eafds.dst_group_struct = gs
            xscache = XSCache(group_struct=gs, data_source_classes=[SimpleDataSource, 
                                                                    NullDataSource])
            xscache.load(temp=temp)
            xscache.data_sources.insert(0, eafds)
        self.xscache = xscache

        self.t = t
        self._phi = None
        self.phi = phi
        self.temp = temp
        self.tol = tol
        self.cwd = os.path.abspath(cwd)
        self.o2exe = o2exe
Beispiel #3
0
    def __init__(self, rc):
        self.rc = rc
        self.statelibs = {}
        self.builddir = 'build-' + rc.reactor
        if not os.path.isdir(self.builddir):
            os.makedirs(self.builddir)
        self.eafds = data_source.EAFDataSource()
        self.omcds = data_source.OpenMCDataSource(
                        cross_sections=rc.openmc_cross_sections,
                        src_group_struct=rc.openmc_group_struct)
        data_sources = [self.omcds]
        if not rc.is_thermal:
            data_sources.append(self.eafds)
        data_sources += [data_source.SimpleDataSource(),
                         data_source.NullDataSource()]
        for ds in data_sources[:]:
            ds.load(rc.temperature)
        self.xscache = XSCache(data_sources=data_sources, scalars={922380000: 1.05})
        self.xscache.load()
        self.tape9 = None

        if self.rc.origen_call is NotSpecified:
            if self.rc.is_thermal:
                self.origen_call = "o2_therm_linux.exe"
            else:
                self.origen_call = "o2_fast_linux.exe"
        else:
            self.origen_call = self.rc.origen_call
Beispiel #4
0
    def __init__(self,
                 t=0.0,
                 phi=0.0,
                 temp=300.0,
                 tol=1e-7,
                 rxs=None,
                 log=None,
                 *args,
                 **kwargs):
        """Parameters
        ----------
        t : float
            Transmutations time [sec].
        phi : float or array of floats
            Neutron flux vector [n/cm^2/sec].  Currently this must either be 
            a scalar or match the group structure of EAF.
        temp : float, optional
            Temperature [K] of material, defaults to 300.0.
        tol : float
            Tolerance level for chain truncation.
        rxs : set of ints or strs
            Reaction ids or names to use in transmutation which produce well-defined 
            children.  This set should thus not include fission.  If None, then the 
            reactions from EAF are used.
        log : file-like or None
            The log file object should be written. A None imples the log is 
            not desired.
        args : tuple, optional
            Other arguments ignored for compatibility with other Transmuters.
        kwargs : dict, optional
            Other keyword arguments ignored for compatibility with other Transmuters.
        """
        eafds = EAFDataSource()
        eafds.load(temp=temp)
        gs = np.array([eafds.src_group_struct[0], eafds.src_group_struct[-1]])
        eafds.dst_group_struct = gs
        self.xscache = XSCache(group_struct=gs,
                               data_source_classes=(NullDataSource, ))
        self.xscache.data_sources.insert(0, eafds)

        self.t = t
        self._phi = None
        self.phi = phi
        self.temp = temp
        self.log = log
        self.tol = tol
        if rxs is None:
            rxs = [
                'gamma', 'gamma_1', 'gamma_2', 'p', 'p_1', 'p_2', 'd', 'd_1',
                'd_2', 't', 't_1', 't_2', 'He3', 'He3_1', 'He3_2', 'a', 'a_1',
                'a_2', 'z_2a', 'z_2p', 'z_2p_1', 'z_2p_2', 'z_2n', 'z_2n_1',
                'z_2n_2', 'z_3n', 'z_3n_1', 'z_3n_2', 'na', 'na_1', 'na_2',
                'z_2na', 'np', 'np_1', 'np_2', 'n2a', 'nd', 'nd_1', 'nd_2',
                'nt', 'nt_1', 'nt_2', 'nHe3', 'nHe3_1', 'nHe3_2', 'z_4n',
                'z_4n_1', 'n', 'n_1', 'n_2', 'z_3np'
            ]
        rxs = set([rxname.id(rx) for rx in rxs])
        rxs.discard(rxname.id('fission'))
        self.rxs = rxs
Beispiel #5
0
def main_gen(ns):
    """Generates an open TAPE9.INP file. by default this only uses completely open 
    data.
    """
    files = glob(os.path.join(ns.build_dir, 'ENSDF', 'ensdf.*'))
    if len(files) == 0:
        grab_ensdf_decay(ns.build_dir)
        files = glob(os.path.join(ns.build_dir, 'ENSDF', 'ensdf.*'))
    print("parsing ENSDF decay data")
    decays, branches = parse_ensdf(files)
    print("creating ORIGEN decay libraries")
    t9decay = gendecay(decays, branches, metastable_cutoff=ns.metastable_cutoff)
    print("creating ORIGEN cross section libraries")
    xsc = XSCache(data_source_classes=[EAFDataSource, SimpleDataSource, 
                                       NullDataSource])
    xsc.load()
    t9xsfpy = origen22.xslibs(xscache=xsc, verbose=True)
    t9 = origen22.merge_tape9([t9decay, t9xsfpy])
    origen22.write_tape9(t9, outfile=ns.filename)
Beispiel #6
0
def main_gen(ns):
    """Generates an open TAPE9.INP file. by default this only uses completely open 
    data.
    """
    files = glob(os.path.join(ns.build_dir, 'ENSDF', 'ensdf.*'))
    if len(files) == 0:
        grab_ensdf_decay(ns.build_dir)
        files = glob(os.path.join(ns.build_dir, 'ENSDF', 'ensdf.*'))
    print("parsing ENSDF decay data")
    decays, branches = parse_ensdf(files)
    print("creating ORIGEN decay libraries")
    t9decay = gendecay(decays,
                       branches,
                       metastable_cutoff=ns.metastable_cutoff)
    print("creating ORIGEN cross section libraries")
    xsc = XSCache(
        data_source_classes=[EAFDataSource, SimpleDataSource, NullDataSource])
    xsc.load()
    t9xsfpy = origen22.xslibs(xscache=xsc, verbose=True)
    t9 = origen22.merge_tape9([t9decay, t9xsfpy])
    origen22.write_tape9(t9, outfile=ns.filename)
Beispiel #7
0
def test_xslibs():
    exp = {
        42: {
            "_type": "xsfpy",
            "_subtype": "activation_products",
            "title": "PyNE Cross Section Data for Activation Products",
        },
        43: {
            "_type": "xsfpy",
            "_subtype": "actinides",
            "title": "PyNE Cross Section Data for Actinides & Daughters",
        },
        44: {
            "_type": "xsfpy",
            "_subtype": "fission_products",
            "title": "PyNE Cross Section Data for Fission Products",
        },
    }
    xsc = XSCache(data_sources=[NullDataSource])
    nucs = [922350000, 10010000, 461080000]
    obs = origen22.xslibs(nucs=nucs, xscache=xsc, nlb=(42, 43, 44))
    obs_meta = {}
    for n in exp:
        obs_meta[n] = {}
        for field in ["_type", "_subtype", "title"]:
            obs_meta[n][field] = obs[n][field]
    assert_equal(exp, obs_meta)
    for n in exp:
        for field in obs[n]:
            if not field.startswith("sigma_"):
                continue
            assert_true(all([v == 0.0 for v in obs[n][field].values()]))
    assert_true(
        set(obs[42].keys())
        >= set(origen22.ACTIVATION_PRODUCT_FIELDS + origen22.XSFPY_FIELDS)
    )
    assert_true(
        set(obs[43].keys()) >= set(origen22.ACTINIDE_FIELDS + origen22.XSFPY_FIELDS)
    )
    assert_true(
        set(obs[44].keys())
        >= set(origen22.FISSION_PRODUCT_FIELDS + origen22.XSFPY_FIELDS)
    )
Beispiel #8
0
class OpenMCOrigen(object):
    """An that combines OpenMC for k-code calculations and ORIGEN for
    transmutation.
    """

    reactions = {rxname.id(_) for _ in ('total', 'absorption', 'gamma', 'gamma_1',
                 'z_2n', 'z_2n_1', 'z_3n', 'proton', 'alpha', 'fission')}

    def __init__(self, rc):
        self.rc = rc
        self.statelibs = {}
        self.builddir = 'build-' + rc.reactor
        if not os.path.isdir(self.builddir):
            os.makedirs(self.builddir)
        self.eafds = data_source.EAFDataSource()
        self.omcds = data_source.OpenMCDataSource(
                        cross_sections=rc.openmc_cross_sections,
                        src_group_struct=rc.openmc_group_struct)
        data_sources = [self.omcds]
        if not rc.is_thermal:
            data_sources.append(self.eafds)
        data_sources += [data_source.SimpleDataSource(),
                         data_source.NullDataSource()]
        for ds in data_sources[:]:
            ds.load(rc.temperature)
        self.xscache = XSCache(data_sources=data_sources, scalars={922380000: 1.05})
        self.xscache.load()
        self.tape9 = None

        if self.rc.origen_call is NotSpecified:
            if self.rc.is_thermal:
                self.origen_call = "o2_therm_linux.exe"
            else:
                self.origen_call = "o2_fast_linux.exe"
        else:
            self.origen_call = self.rc.origen_call

    def pwd(self, state, directory):
        """Path to directory we will be running specific physics codes in.

        Parameters
        ----------
        state : namedtuple (State)
            The state we are running physics codes for.
        directory : string
            The name of the sub-directory we would like.

        Returns
        -------
        str
            The path to the desired directory.
        """
        return os.path.join(self.builddir, str(hash(state)), directory)

    def context(self, state):
        """Unite parameters in the run-control file and  the current state.

        Parameters
        ----------
        state : namedtuple (State)
            A State tuple that contains perturbation parameters specific to that
            state.

        Returns
        -------
        ctx : dict
            A dictionary with all relevant perturbation parameters.
        """
        rc = self.rc
        ctx = dict(rc._dict)
        ctx.update(zip(rc.perturbation_params, state))
        return ctx

    def generate_run(self, run, fname):
        """Generate transmutation tables, neutron production/destruction rates, and
        burnup statistics for a sequence of states with the same initial
        conditions.

        Parameters
        ----------
        run : list of States
            A list of States that has the same initial conditions at increasing
            burnup times.

        Returns
        -------
        libs : list of dicts
            Libraries to write out - one for the full fuel and one for each tracked nuclide.
        """
        self.libs = {'xs': [], 'phi_g': {
            'E_g': {'EAF': self.eafds.src_group_struct,
                    'OpenMC': self.omcds.src_group_struct},
            'phi_g': []},
           "fuel": {
            "TIME": [0],
            "NEUT_PROD": [0],
            "NEUT_DEST": [0],
            "BUd":  [0],
            "material": [self.rc.fuel_material],
            "tracked_nucs": {nucname.name(n): [self.rc.fuel_material.comp.get(n, 0) * 1000]
                             for n in self.rc.track_nucs},
            "phi_tot": [0]
            }}

        for nuc in self.rc.track_nucs:
            self.libs[nuc] = {
                "TIME": [0],
                "NEUT_PROD": [0],
                "NEUT_DEST": [0],
                "BUd": [0],
                "material": [Material({nuc: 1}, 1000)],
                "tracked_nucs": {nucname.name(n): [0]
                	                 for n in self.rc.track_nucs},
                "phi_tot": [0]
                }
            self.libs[nuc]["tracked_nucs"][nucname.name(nuc)] = [1000]

        print([state.burn_times for state in run])
        for i, state in enumerate(run):
            if i > 0:
                transmute_time = state.burn_times - run[i-1].burn_times
                results = self.generate(state, transmute_time)
                self.libs = self._update_libs_with_results(self.libs, results)
                self.rc.writers[0].write(self.libs, fname)
        return self.libs

    def _update_libs_with_results(self, matlibs, newlibs):
        """Update a set of libraries with results from a single timestep in-place.

        Parameters
        ----------
        matlibs : dict
            A set of libraries with nuclides as keys.
        newlibs: dict
            A set of libraries for a single timestep, with nuclides as keys.

        Returns
        -------
        libs : dict
            The updated library.
        """
        for mat, newlib in newlibs.items():
            if mat == 'xs':
                matlibs[mat].append(newlib)
                continue
            elif mat == 'phi_g':
                matlibs[mat][mat].append(newlib)
                continue
            oldlib = matlibs[mat]
            for nuc in self.rc.track_nucs:
                name = nucname.name(nuc)
                nuc_frac = newlib["material"].comp.get(nuc, 0)
                mass = newlib["material"].mass
                oldlib["tracked_nucs"][name].append(nuc_frac * mass)
            for key, value in newlib.items():
                if isinstance(key, int):
                    continue
                else:
                    oldlib[key].append(value)
        return matlibs

    def generate(self, state, transmute_time):
        """Runs physics codes on a specific state. First runs OpenMC for transport,
        then uses the results to run ORIGEN for a single timestep.

        Parameters
        ----------
        state : namedtuple (State)
            A namedtuple containing the state parameters.
        transmute_time : float
            The length of the time step we would like to run ORIGEN for.

        Returns
        -------
        results : dict
            Dict of physics code results. Keys are either nuclide ID's or "fuel"
            for the full fuel results.
        """
        print("generating for a state with transmute_time {}".format(transmute_time))
        if state in self.statelibs:
            return self.statelibs[state]
        rc = self.rc
        k, phi_g, xstab = self.openmc(state)
        results = {"fuel": {}}
        results.update(dict(zip(rc.track_nucs, [{} for _ in rc.track_nucs])))
        if 'flux' in rc:
            phi_tot = state.flux
        elif 'fuel_specific_power' in rc:
            G = len(phi_g)
            fission_id = rxname.id("fission")
            if G == 1:
                fission_xs = {xs[0]: xs[2] * 1e-24 for xs in xstab  # xs is in barns not cm2
                              if xs[1] == fission_id}
            else:
                fission_xs = {xs[0]: np.sum(xs[2]) * 1e-24 / len(xs[2]) 
                              for xs in xstab  # xs is in barns not cm2
                              if xs[1] == fission_id}
            fuel_material = self.libs["fuel"]["material"][-1]
            fuel_material.atoms_per_molecule = sum([self.rc.fuel_chemical_form[m]
                                                    for m in self.rc.fuel_chemical_form])
            fuel_atom_frac = fuel_material.to_atom_frac()
            fuel_number_density = 6.022e23 * self.rc.fuel_density / \
                (fuel_material.molecular_mass() * fuel_material.atoms_per_molecule)
            number_densities = {nuc: fuel_atom_frac[nuc] * fuel_number_density
                                for nuc in fuel_material.comp}
            sum_N_i_sig_fi = sum([number_densities[nuc] * fission_xs.get(nuc, 0)
                                  for nuc in fuel_material.comp])
            sum_N_i_sig_fi = sum_N_i_sig_fi[sum_N_i_sig_fi != 0]
            fuel_specific_power_mwcc = self.rc.fuel_density * 1e-6 * state.fuel_specific_power
            # see http://iriaxp.iri.tudelft.nl/~leege/SCALE44/origens.PDF for formula
            # (search for "the specific power due to fission", on p. 22 of the PDF)
            phi_tot = sum(3.125e16*fuel_specific_power_mwcc/sum_N_i_sig_fi)
        results = self.run_all_the_origens(state, transmute_time, phi_tot, results)
        results['xs'] = xstab
        results['phi_g'] = {'EAF': self.eafds.src_phi_g,
                            'OpenMC': self.omcds.src_phi_g}
        self.statelibs[state] = results
        statedir = os.path.join(self.builddir, str(hash(state)))
        for dir in os.listdir(self.builddir):
            if(os.path.join(self.builddir, dir) != statedir):
                shutil.rmtree(os.path.join(self.builddir, dir))
        return results

    def run_all_the_origens(self, state, transmute_time, phi_tot, results):
        """Call ORIGEN as much as necessary and unite the results.

        Parameters
        ----------
        state : namedtuple (State)
            A namedtuple containing the state parameters.
        transmute_time : float
            The length of the transmutation timestep. Has units of [days].
        phi_tot : float
            The total neutron flux.
        results : dict
            A dict with material identifiers as keys, and dictionaries as
            values. The basic data structure to fill.

        Returns
        -------
        dict
           A dict of all the ORIGEN results.
        """
        if self.rc.verbose:
            print("making tape9 for {0} with phi={1}".format(state, phi_tot))
        mat = self.libs['fuel']['material'][-1]
        mat.density = self.rc.fuel_density
        mat.atoms_per_molecule = 3.0
        atom_dens = mat.to_atom_dens()
        for ds in self.xscache.data_sources:
            ds.atom_dens = atom_dens
        self.tape9 = origen22.make_tape9(self.rc.track_nucs, self.xscache, nlb=(219, 220, 221))
        self.tape9 = origen22.merge_tape9((self.tape9,
                                          origen22.loads_tape9(brightlitetape9)))
        origen22.write_tape9(self.tape9)
        for mat_id in results.keys():
            pwd = self.pwd(state, "origen{}".format(mat_id))
            mat = self.libs[mat_id]["material"][-1]
            if not os.path.isdir(pwd):
                os.makedirs(pwd)
            with indir(pwd):
                if not os.path.isfile("TAPE6.OUT"):
                    self._make_origen_input(transmute_time, phi_tot, mat)
        origen_results = []
        if self.rc.threads == 1:
            for mat_id in results.keys():
                pwd = self.pwd(state, "origen{}".format(mat_id))
                origen_params = (state.burn_times,
                                 transmute_time,
                                 phi_tot,
                                 mat_id,
                                 self.libs[mat_id]["material"][-1],
                                 pwd,
                                 self.origen_call)
                origen_results.append(_origen(origen_params))
        else:
            origen_params_ls = [(state.burn_times,
                                 transmute_time,
                                 phi_tot,
                                 mat_id,
                                 self.libs[mat_id]["material"][-1],
                                 self.pwd(state, "origen{}".format(mat_id)),
                                 self.origen_call)
                                for mat_id in results]
            pool = Pool(self.rc.threads)
            origen_results = pool.map(_origen, origen_params_ls)
            pool.close()
            pool.join()
        for result in origen_results:
            result[1]["material"] = Material(dict(result[1]["material"]),
                                             1000,
                                             attrs={"units": "g"})
        return dict(origen_results)

    def openmc(self, state):
        """Runs OpenMC for a given state.

        Parameters
        ----------
        state : namedtuple (State)
            A namedtuple containing the state parameters.

        Returns
        -------
        k : float
            Neutron multiplication factor.
        phi_g : list of floats
            Group flux.
        xstab : list of tuples
            A list of tuples of the format (nuc, rx, xs).
        """
        # make inputs
        pwd = self.pwd(state, "omc")
        if not os.path.isdir(pwd):
            os.makedirs(pwd)
        self._make_omc_input(state)
        statepoint = _find_statepoint(pwd)
        if statepoint is None:
            with indir(pwd):
                subprocess.check_call(['openmc', '-s', '{}'.format(self.rc.threads)])
            statepoint = _find_statepoint(pwd)
        # parse & prepare results
        k, phi_g, e_g = self._parse_statepoint(statepoint)
        if self.rc.plot_group_flux:
            plot_e_g, plot_phi_g = self._find_plot_data(statepoint)
            with indir(pwd):
                self._plot_group_flux(plot_e_g, plot_phi_g)
        xstab = self._generate_xs(e_g, phi_g)
        return k, phi_g, xstab

    def _find_plot_data(self, statepoint_path):
        sp = statepoint.StatePoint(statepoint_path)
        tally = sp.tallies[3].get_values(['flux'])
        phi_g = tally.flatten()
        phi_g /= phi_g.sum()
        e_g = sp.tallies[3].find_filter('energy').bins
        return e_g, phi_g


    def _plot_group_flux(self, e_g, phi_g):
        """Plot the group flux output by OpenMC and save plot to file.

        Parameters
        ----------
        e_g : array
            Energy bins
        phi_g : array
            Flux values
        """
        fig = plt.figure(figsize=(12, 8))
        plt.loglog(*stair_step(e_g, phi_g), figure=fig)
        plt.title("Flux vs Energy in")
        plt.xlabel('E [MeV]')
        plt.ylabel('Flux [N/cm$^2\cdot$s]')
        plt.savefig("flux")
        plt.close()

    def _make_omc_input(self, state):
        """Make OpenMC input files for a given state.

        Parameters
        ----------
        state : namedtuple (State)
            A namedtuple containing the state parameters.

        Returns
        -------
        None
        """
        pwd = self.pwd(state, "omc")
        ctx = self.context(state)
        rc = self.rc
        # settings
        settings = SETTINGS_TEMPLATE.format(**ctx)
        with open(os.path.join(pwd, 'settings.xml'), 'w') as f:
            f.write(settings)
        # materials
        valid_nucs = self.nucs_in_cross_sections()
        # discard Cd-119m1 as a valid nuc
        valid_nucs.discard(481190001)
        ctx['_fuel_nucs'] = _mat_to_nucs(rc.fuel_material[valid_nucs])
        curr_fuel = self.libs['fuel']['material'][-1][valid_nucs]
        for nuc in curr_fuel.comp:
            if curr_fuel.comp[nuc] < self.rc.track_nuc_threshold:
                del curr_fuel.comp[nuc]
        ctx['_fuel_nucs'] = _mat_to_nucs(curr_fuel[valid_nucs])        
        ctx['_clad_nucs'] = _mat_to_nucs(rc.clad_material[valid_nucs])
        ctx['_cool_nucs'] = _mat_to_nucs(rc.cool_material[valid_nucs])
        materials = MATERIALS_TEMPLATE.format(**ctx)
        with open(os.path.join(pwd, 'materials.xml'), 'w') as f:
            f.write(materials)
        # geometry
        ctx['lattice'] = ctx['lattice'].strip().replace('\n', '\n      ')
        ctx['_latt_shape0'] = ctx['lattice_shape'][0]
        ctx['_latt_shape1'] = ctx['lattice_shape'][1]
        ctx['_latt_x_pitch'] = ctx['unit_cell_pitch'] * ctx['lattice_shape'][0]
        ctx['_latt_y_pitch'] = ctx['unit_cell_pitch'] * ctx['lattice_shape'][1]
        ctx['_latt_x_half_pitch'] = ctx['_latt_x_pitch'] / 2.0
        ctx['_latt_y_half_pitch'] = ctx['_latt_y_pitch'] / 2.0
        geometry = GEOMETRY_TEMPLATE.format(**ctx)
        with open(os.path.join(pwd, 'geometry.xml'), 'w') as f:
            f.write(geometry)
        # tallies
        ctx['_egrid'] = " ".join(map(str, sorted(ctx['group_structure'])))
        ctx['_eafds_egrid'] = " ".join(map(str, sorted(self.eafds.src_group_struct)))
        ctx['_omcds_egrid'] = " ".join(map(str, sorted(self.omcds.src_group_struct)))
        # nucs = core_nucs & valid_nucs
        tallies = TALLIES_TEMPLATE.format(**ctx)
        with open(os.path.join(pwd, 'tallies.xml'), 'w') as f:
            f.write(tallies)
        # plots
        plots = PLOTS_TEMPLATE.format(**ctx)
        with open(os.path.join(pwd, 'plots.xml'), 'w') as f:
            f.write(plots)

    def nucs_in_cross_sections(self):
        """Returns the set of nuclides present in the cross_sections.xml file.

        Returns
        -------
        nucs : set
            A set of all nuclides known to OpenMC, in ID form.
        """
        return {n.nucid for n in self.omcds.cross_sections.ace_tables
                if n.nucid is not None}

    def _parse_statepoint(self, statepoint_path, tally_id=1):
        """Parses a statepoint file and reads in the relevant fluxes, assigns them
        to the DataSources or the XSCache, and returns k, phi_g, and E_g.

        Parameters
        ----------
        statepoint : xsgen.statepoint.StatePoint
            An OpenMC StatePoint.
        tally_id : int
            The tally id we wish to read group flux from.

        Returns
        -------
        k : float
            Neutron multiplication factor.
        phi_g : list of floats
            Group flux.
        e_g : list of floats
            Group structure.
        """
        sp = statepoint.StatePoint(statepoint_path)
        temp_tally = []
        temp_tally.append(sp.tallies[2].get_values(['flux']).flatten())
        temp_tally.append(sp.tallies[3].get_values(['flux']).flatten())
        # compute group fluxes for data sources
        for tally, ds in zip(temp_tally[:2], (self.eafds, self.omcds)):
            ds.src_phi_g = np.array(tally[::-1])
            ds.src_phi_g /= tally.sum()
        # compute return values
        k, kerr = sp.k_combined
        tally = sp.tallies[tally_id].get_values(['flux'])
        phi_g = tally.flatten()
        phi_g /= phi_g.sum()
        e_g = sp.tallies[tally_id].find_filter('energy').bins
        e_g = e_g[::-1]
        return k, phi_g, e_g

    def _generate_xs(self, e_g, phi_g):
        """Grab xs data from cache depending on group flux.

        Parameters
        ----------
        e_g : list of floats
            Group structure.
        phi_g : list of floats
            Group flux.

        Returns
        -------
        data : list of tuples
            A list of tuples of the format (nuc, rx, xs).
        """
        rc = self.rc
        verbose = rc.verbose
        xscache = self.xscache
        xscache.clear()
        xscache['E_g'] = e_g
        xscache['phi_g'] = phi_g
        G = len(phi_g)
        temp = rc.temperature
        rxs = self.reactions
        nucs = rc.track_nucs
        dt = np.dtype([('nuc', 'i4'), ('rx', np.uint32), ('xs', 'f8', G)])
        data = np.empty(len(nucs)*len(rxs), dtype=dt)
        i = 0
        for nuc in nucs:
            for rx in rxs:
                try:
                    xs = xscache[nuc, rx, temp]
                except KeyError:
                    continue
                if(len(xs) < G):
                    continue
                if verbose:
                    print("OpenMC XS:", nucname.name(nuc), rxname.name(rx), xs, type(xs), temp)
                data[i] = nuc, rx, xs
                i += 1
        return data

    def _make_origen_input(self, transmute_time, phi_tot, mat):
        """Make ORIGEN input files for a given state.

        Parameters
        ----------
        transmute_time : float
            The time, in days, to run the transmutation for.
        phi_tot: float
            Total neutron flux.
        mat : pyne.material.Material
            The fuel material to transmute.
        Returns
        -------
        None
        """
        # may need to filter tape4 for Bad Nuclides
        # if sum(mat.comp.values()) > 1:
        for nuc in mat.comp:
            if mat.comp[nuc] < self.rc.track_nuc_threshold:
                del mat.comp[nuc]
        origen22.write_tape4(mat)
        origen22.write_tape5_irradiation("IRF",
                                         transmute_time,
                                         phi_tot,
                                         xsfpy_nlb=(219, 220, 221),
                                         cut_off=self.rc.track_nuc_threshold)
        origen22.write_tape9(self.tape9)