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
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
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
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
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
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)
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
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