Exemple #1
0
def sigma_t(iso, T=300.0, E_g=None, E_n=None, phi_n=None):
    """Calculates the total neutron cross-section for an isotope. 

    .. math::
        \\sigma_{s, g} = \\sum_{h} \\sigma_{s, g\\to h} 

    Args:
        * iso (int or str): An isotope to calculate the total cross-section for.

    Keyword Args:
        If any of these are None-valued, values from the cache are used.

        * T (float): Tempurature of the target material [kelvin].
        * E_g (sequence of floats): New, lower fidelity energy group structure [MeV]
          that is of length G+1. Ordered from lowest-to-highest energy.
        * E_n (sequence of floats): higher resolution energy group structure [MeV]
          that is of length N+1. Ordered from lowest-to-highest energy.
        * phi_n (sequence of floats): The high-fidelity flux [n/cm^2/s] to collapse the fission 
          cross-section over.  Length N.  Ordered from lowest-to-highest energy.

    Returns:
        * sig_t_g (numpy array): A numpy array of the total cross-section.
    """
    # Ensure that the low-fidelity group structure is in the cache
    if E_n is not None:
        xs_cache['E_n'] = E_n

    if E_g is not None:
        xs_cache['E_g'] = E_g

    if phi_n is not None:
        xs_cache['phi_n'] = phi_n

    # Get the total XS
    iso_zz = isoname.mixed_2_zzaaam(iso)
    sigma_a_g_iso_zz = 'sigma_a_g_{0}'.format(iso_zz)
    sigma_s_g_iso_zz_T = 'sigma_t_g_{0}_{1}'.format(iso_zz, T)
    sigma_t_g_iso_zz_T = 'sigma_t_g_{0}_{1}'.format(iso_zz, T)

    # Don't recalculate anything if you don't have to
    if sigma_t_g_iso_zz_T in xs_cache:
        return xs_cache[sigma_t_g_iso_zz_T]

    # This calculation requires the abosorption cross-section
    if sigma_a_g_iso_zz not in xs_cache:
        xs_cache[sigma_a_g_iso_zz] = sigma_a(iso, E_g, E_n, phi_n)

    # This calculation requires the scattering cross-section
    if sigma_s_g_iso_zz_T not in xs_cache:
        xs_cache[sigma_s_g_iso_zz_T] = sigma_s(iso, T, E_g, E_n, phi_n)

    # Sum over all h indeces
    sig_t_g = xs_cache[sigma_a_g_iso_zz] + xs_cache[sigma_s_g_iso_zz_T]

    # Put this value back into the cache, with the appropriate label
    xs_cache[sigma_t_g_iso_zz_T] = sig_t_g

    return sig_t_g
Exemple #2
0
def sigma_reaction(iso, rx, E_g=None, E_n=None, phi_n=None):
    """Calculates the neutron absorption reaction cross-section for an isotope for a new, lower resolution
    group structure using a higher fidelity flux.  Note that g indexes G, n indexes N, and G < N.

    Note: This always pulls the absorption reaction cross-section out of the nuc_data library.    

    Args:
        * iso (int or str): An isotope to calculate the absorption cross-section for.
        * rx (str): Reaction key. ('gamma', 'alpha', 'p', etc.)

    Keyword Args:
        If any of these are None-valued, values from the cache are used.

        * E_g (sequence of floats): New, lower fidelity energy group structure [MeV]
          that is of length G+1. Ordered from lowest-to-highest energy.
        * E_n (sequence of floats): higher resolution energy group structure [MeV]
          that is of length N+1. Ordered from lowest-to-highest energy.
        * phi_n (sequence of floats): The high-fidelity flux [n/cm^2/s] to collapse the fission 
          cross-section over.  Length N.  Ordered from lowest-to-highest energy.

    Returns:
        * sigma_rx_g (numpy array): A numpy array of the collapsed absorption cross-section.
    """
    # Ensure that the low-fidelity group structure is in the cache
    if E_n is not None:
        xs_cache['E_n'] = E_n

    if E_g is not None:
        xs_cache['E_g'] = E_g

    if phi_n is not None:
        xs_cache['phi_n'] = phi_n

    # Get the absorption XS
    iso_zz = isoname.mixed_2_zzaaam(iso)
    sigma_rx_n_iso_zz = 'sigma_rx_{1}_n_{0}'.format(iso_zz, rx)
    sigma_rx_g_iso_zz = 'sigma_rx_{1}_g_{0}'.format(iso_zz, rx)

    # Don't recalculate anything if you don't have to
    if sigma_rx_g_iso_zz in xs_cache:
        return xs_cache[sigma_rx_g_iso_zz]
    else:
        sigma_rx_n = xs_cache[sigma_rx_n_iso_zz]

    # Perform the group collapse, knowing that the right data is in the cache
    sigma_rx_g = partial_group_collapse(sigma_rx_n)

    # Put this value back into the cache, with the appropriate label
    xs_cache[sigma_rx_g_iso_zz] = sigma_rx_g

    return sigma_rx_g
Exemple #3
0
def convolve_initial_fuel_form(stream, chem_form):
    """Convolves a heavy metal mass stream with a chemical form.  The chemical 
    form is a dictionary that should have an "IHM" key, whose value gets replaced
    with the fuel mass stream in the resultant isovec.

    Args:
        * `stream` (MassStream [see bright]): An initial heavy metal mass stream.
        * `chem_form` (dict): The chemical form of the fuel.  For instance UOX 
          would be represented by ``{80160: 2.0, "IHM": 1.0}`` while a metal fuel
          would simply be ``{"IHM": 1.0}``.

    Returns:
        * `isovec` (dict): Normalized, mass-weighted isotopic vector of the initial fuel form.
        * `AW` (float): Atomic weight of the fuel mass stream (ie, just the IHM).
        * `MW` (float): Molecular weight of the chemical form after the fuel stream has been 
          inserted into it.  This should always be greater than or equal to AW.
    """

    AW = stream.atomic_weight()

    # Calculate the MW
    if "IHM" not in chem_form:
        raise KeyError("The fuel chemical form must contain the key 'IHM'.")

    MW = 0.0
    for key in chem_form:
        if key == "IHM":
            MW += chem_form[key] * AW
        else:
            key_aw = isoname.nuc_weight(key)
            MW += chem_form[key] * key_aw

    AW_over_MW = AW / MW

    # Calculate the isotopic vector
    isovec = {}
    for key in chem_form:
        if key == "IHM":
            comp = stream.comp
            for iso in comp:
                isovec[iso] = comp[iso] * AW_over_MW
        else:
            key_zz = isoname.mixed_2_zzaaam(key)
            key_aw = isoname.nuc_weight_zzaaam(key_zz)
            isovec[key_zz] = chem_form[key] * key_aw / MW

    return isovec, AW, MW
Exemple #4
0
def nist_2_zzaaam(nist_iso):
    """Converts a NIST style isotope to a zzaaam style.

    Args:
        * nist_iso (str): A nist isotope.

    Returns:
        * iso_zz (int): a zzaaam isotope.
    """
    m = re.match(nist_iso_pattern, nist_iso)

    if m.group(1) == "":
        elem = m.group(2).upper()
        iso_zz = isoname.LLzz[elem] * 10000
    else:
        iso_zz = isoname.mixed_2_zzaaam(m.group(2) + m.group(1))

    return iso_zz
Exemple #5
0
def cell_power(Material, SpecificPower, CellVolume, Density):
    """Calculates the power from a given unit cell.

    Args:
        * `Material` (dict): Normalized material dictionary; e.g. UOX, Metal (weight fraction, NOT atom fraction).
        * `SpecificPower` (float): Specific power of material [MW / kgIHM].
        * `CellVolume` (float): Unit cell volume that material occupies [cm^3].
        * `Density` (float): Density of material [g / cm^3].

    Returns:
        * `Power` (float): The power coming from this fuel cell [MW].
    """

    WeightFracIHM = 0.0
    for iso in Material.keys():
        if not ( isoname.zzLL[ isoname.mixed_2_zzaaam(iso)/10000 ] in isoname.ACT ):
            continue
        WeightFracIHM = WeightFracIHM + Material[iso]
    return SpecificPower * (10.0**-3) * Density * WeightFracIHM * CellVolume
Exemple #6
0
 def test_mixed_2_zzaaam(self):
     assert_equal(isoname.mixed_2_zzaaam(20040),   20040)
     assert_equal(isoname.mixed_2_zzaaam("PU239"), 942390)
     assert_equal(isoname.mixed_2_zzaaam(95642),   952421)
Exemple #7
0
def chi(iso, E_g=None, E_n=None, phi_n=None):
    """Calculates the neutron fission energy spectrum for an isotope for a new, lower resolution
    group structure using a higher fidelity flux.  Note that g indexes G, n indexes N, and G < N.

    Args:
        * iso (int or str): An isotope to calculate the fission energy spectrum for.

    Keyword Args:
        If any of these are None-valued, values from the cache are used.

        * E_g (sequence of floats): New, lower fidelity energy group structure [MeV]
          that is of length G+1. Ordered from lowest-to-highest energy.
        * E_n (sequence of floats): higher resolution energy group structure [MeV]
          that is of length N+1. Ordered from lowest-to-highest energy.
        * phi_n (sequence of floats): The high-fidelity flux [n/cm^2/s] to collapse the fission 
          cross-section over.  Length N.  Ordered from lowest-to-highest energy.

    Returns:
        * chi_g (numpy array): A numpy array of the fission energy spectrum.
    """
    # Ensure that the low-fidelity group structure is in the cache
    if E_n is not None:
        xs_cache['E_n'] = E_n

    if E_g is not None:
        xs_cache['E_g'] = E_g

    if phi_n is not None:
        xs_cache['phi_n'] = phi_n

    # Get the fission XS
    iso_zz = isoname.mixed_2_zzaaam(iso)
    chi_g_iso_zz = 'chi_g_{0}'.format(iso_zz)

    # Don't recalculate anything if you don't have to
    if chi_g_iso_zz in xs_cache:
        return xs_cache[chi_g_iso_zz]

    # Get the the set of isotopes we know we need chi for.  
    if 'fissionable_isos' not in xs_cache:
        with tb.openFile(nuc_data, 'r') as f:
            fi = set(f.root.neutron.xs_mg.fission.cols.iso_zz)
        xs_cache['fissionable_isos'] = fi
    fissionable_isos = xs_cache['fissionable_isos']

    if (iso_zz not in fissionable_isos) and (86 <= iso_zz/10000):
        fissionable_isos.add(iso_zz)

    # Perform the group collapse on a continuous chi
    nE = 101
    G = len(xs_cache['E_g']) - 1
    chi_g = np.zeros(G, dtype=float)

    if (iso_zz in fissionable_isos):
        for g in range(G):
            E_space = np.logspace(np.log10(xs_cache['E_g'][g]), np.log10(xs_cache['E_g'][g+1]), nE)
            dnumer = _chi(E_space)

            numer = integrate.trapz(dnumer, E_space)
            denom = (xs_cache['E_g'][g+1] - xs_cache['E_g'][g])

            chi_g[g] = (numer / denom)

    # renormalize chi
    chi_g = chi_g / chi_g.sum()

    # Put this value back into the cache, with the appropriate label
    xs_cache[chi_g_iso_zz] = chi_g

    return chi_g
Exemple #8
0
def sigma_s_gh(iso, T, E_g=None, E_n=None, phi_n=None):
    """Calculates the neutron scattering cross-section kernel for an isotope for a new, lower resolution
    group structure using a higher fidelity flux.  Note that g, h index G, n indexes N, and G < N.
    g is for the incident energy and h is for the exiting energy.

    .. math::
        \\sigma_{s, g\\to h} = \\frac{\\int_{E_g}^{E_{g+1}} \\int_{E_h}^{E_{h+1}} \\sigma_s(E) P(E \\to E^\\prime) \\phi(E) dE^\\prime dE}
                                {\\int_{E_g}^{E_{g+1}} \\phi(E) dE}

    Note: This always pulls the scattering length out of the nuc_data library.    

    Args:
        * iso (int or str): An isotope to calculate the fission cross-section for.
        * T (float): Tempurature of the target material [kelvin].

    Keyword Args:
        If any of these are None-valued, values from the cache are used.

        * E_g (sequence of floats): New, lower fidelity energy group structure [MeV]
          that is of length G+1. Ordered from lowest-to-highest energy.
        * E_n (sequence of floats): higher resolution energy group structure [MeV]
          that is of length N+1. Ordered from lowest-to-highest energy.
        * phi_n (sequence of floats): The high-fidelity flux [n/cm^2/s] to collapse the fission 
          cross-section over.  Length N.  Ordered from lowest-to-highest energy.

    Returns:
        * sig_s_gh (numpy array): A numpy array of the scattering kernel.
    """
    # Ensure that the low-fidelity group structure is in the cache
    if E_n is not None:
        xs_cache['E_n'] = E_n

    if E_g is not None:
        xs_cache['E_g'] = E_g

    if phi_n is not None:
        xs_cache['phi_n'] = phi_n

    # Get the fission XS
    iso_zz = isoname.mixed_2_zzaaam(iso)
    sigma_s_gh_iso_zz_T = 'sigma_s_gh_{0}_{1}'.format(iso_zz, T)

    # Don't recalculate anything if you don't have to
    if sigma_s_gh_iso_zz_T in xs_cache:
        return xs_cache[sigma_s_gh_iso_zz_T]

    # Get some needed data
    G = len(xs_cache['E_g']) - 1
    b = xs_cache['b_{0}'.format(iso_zz)]
    M_A = xs_cache['M_A_{0}'.format(iso_zz)]

    # Initialize the scattering kernel
    sig_s_gh = np.zeros((G, G), dtype=float)


    # Calculate all values of the kernel
    for g, h in product(range(G), range(G)):
        # Numerator inetgration term 
        dnumer = lambda _E_prime, _E: sigma_s_E(_E, b, M_A, T) *  P(_E, _E_prime, M_A, T) * xs_cache['phi_g'][g]

        # Integral
        nE = 26
        E_space = np.logspace(np.log10(xs_cache['E_g'][g]), np.log10(xs_cache['E_g'][g+1]), nE)
        E_prime_space = np.logspace(np.log10(xs_cache['E_g'][h]), np.log10(xs_cache['E_g'][h+1]), nE)

        numer = msmintegrate.dbltrapz(dnumer, E_space, E_prime_space)

        # Denominator term, analytically integrated
        denom = xs_cache['phi_g'][g] * (xs_cache['E_g'][g+1] - xs_cache['E_g'][g])

        # Cross section value
        sig_s_gh[g, h] = numer / denom 

    # Put this value back into the cache, with the appropriate label
    xs_cache[sigma_s_gh_iso_zz_T] = sig_s_gh

    return sig_s_gh