def from_endf(cls, ev, mt): """Generate an angular distribution from an ENDF evaluation Parameters ---------- ev : openmc.data.endf.Evaluation ENDF evaluation mt : int The MT value of the reaction to get angular distributions for Returns ------- openmc.data.AngleDistribution Angular distribution """ file_obj = StringIO(ev.section[4, mt]) # Read HEAD record items = get_head_record(file_obj) lvt = items[2] ltt = items[3] # Read CONT record items = get_cont_record(file_obj) li = items[2] nk = items[4] center_of_mass = (items[3] == 2) # Check for obsolete energy transformation matrix. If present, just skip # it and keep reading if lvt > 0: warn('Obsolete energy transformation matrix in MF=4 angular ' 'distribution.') for _ in range((nk + 5) // 6): file_obj.readline() if ltt == 0 and li == 1: # Purely isotropic energy = np.array([0., ev.info['energy_max']]) mu = [Uniform(-1., 1.), Uniform(-1., 1.)] elif ltt == 1 and li == 0: # Legendre polynomial coefficients params, tab2 = get_tab2_record(file_obj) n_energy = params[5] energy = np.zeros(n_energy) mu = [] for i in range(n_energy): items, al = get_list_record(file_obj) temperature = items[0] energy[i] = items[1] coefficients = np.asarray([1.0] + al) mu.append(Legendre(coefficients)) elif ltt == 2 and li == 0: # Tabulated probability distribution params, tab2 = get_tab2_record(file_obj) n_energy = params[5] energy = np.zeros(n_energy) mu = [] for i in range(n_energy): params, f = get_tab1_record(file_obj) temperature = params[0] energy[i] = params[1] if f.n_regions > 1: raise NotImplementedError( 'Angular distribution with multiple ' 'interpolation regions not supported.') mu.append( Tabular(f.x, f.y, INTERPOLATION_SCHEME[f.interpolation[0]])) elif ltt == 3 and li == 0: # Legendre for low energies / tabulated for high energies params, tab2 = get_tab2_record(file_obj) n_energy_legendre = params[5] energy_legendre = np.zeros(n_energy_legendre) mu = [] for i in range(n_energy_legendre): items, al = get_list_record(file_obj) temperature = items[0] energy_legendre[i] = items[1] coefficients = np.asarray([1.0] + al) mu.append(Legendre(coefficients)) params, tab2 = get_tab2_record(file_obj) n_energy_tabulated = params[5] energy_tabulated = np.zeros(n_energy_tabulated) for i in range(n_energy_tabulated): params, f = get_tab1_record(file_obj) temperature = params[0] energy_tabulated[i] = params[1] if f.n_regions > 1: raise NotImplementedError( 'Angular distribution with multiple ' 'interpolation regions not supported.') mu.append( Tabular(f.x, f.y, INTERPOLATION_SCHEME[f.interpolation[0]])) energy = np.concatenate((energy_legendre, energy_tabulated)) return AngleDistribution(energy, mu)
def from_ace(cls, ace, location_dist, location_start): """Generate an angular distribution from ACE data Parameters ---------- ace : openmc.data.ace.Table ACE table to read from location_dist : int Index in the XSS array corresponding to the start of a block, e.g. JXS(9). location_start : int Index in the XSS array corresponding to the start of an angle distribution array Returns ------- openmc.data.AngleDistribution Angular distribution """ # Set starting index for angle distribution idx = location_dist + location_start - 1 # Number of energies at which angular distributions are tabulated n_energies = int(ace.xss[idx]) idx += 1 # Incoming energy grid energy = ace.xss[idx:idx + n_energies] * EV_PER_MEV idx += n_energies # Read locations for angular distributions lc = ace.xss[idx:idx + n_energies].astype(int) idx += n_energies mu = [] for i in range(n_energies): if lc[i] > 0: # Equiprobable 32 bin distribution idx = location_dist + abs(lc[i]) - 1 cos = ace.xss[idx:idx + 33] pdf = np.zeros(33) pdf[:32] = 1.0 / (32.0 * np.diff(cos)) cdf = np.linspace(0.0, 1.0, 33) mu_i = Tabular(cos, pdf, 'histogram', ignore_negative=True) mu_i.c = cdf elif lc[i] < 0: # Tabular angular distribution idx = location_dist + abs(lc[i]) - 1 intt = int(ace.xss[idx]) n_points = int(ace.xss[idx + 1]) data = ace.xss[idx + 2:idx + 2 + 3 * n_points] data.shape = (3, n_points) mu_i = Tabular(data[0], data[1], INTERPOLATION_SCHEME[intt]) mu_i.c = data[2] else: # Isotropic angular distribution mu_i = Uniform(-1., 1.) mu.append(mu_i) return cls(energy, mu)
def _get_photon_products_ace(ace, rx): """Generate photon products from an ACE table Parameters ---------- ace : openmc.data.ace.Table ACE table to read from rx : openmc.data.Reaction Reaction that generates photons Returns ------- photons : list of openmc.Products Photons produced from reaction with given MT """ n_photon_reactions = ace.nxs[6] photon_mts = ace.xss[ace.jxs[13]:ace.jxs[13] + n_photon_reactions].astype(int) photons = [] for i in range(n_photon_reactions): # Determine corresponding reaction neutron_mt = photon_mts[i] // 1000 # Restrict to photons that match the requested MT. Note that if the # photon is assigned to MT=18 but the file splits fission into # MT=19,20,21,38, we assign the photon product to each of the individual # reactions if neutron_mt == 18: if rx.mt not in (18, 19, 20, 21, 38): continue elif neutron_mt != rx.mt: continue # Create photon product and assign to reactions photon = Product('photon') # ================================================================== # Photon yield / production cross section loca = int(ace.xss[ace.jxs[14] + i]) idx = ace.jxs[15] + loca - 1 mftype = int(ace.xss[idx]) idx += 1 if mftype in (12, 16): # Yield data taken from ENDF File 12 or 6 mtmult = int(ace.xss[idx]) assert mtmult == neutron_mt # Read photon yield as function of energy photon.yield_ = Tabulated1D.from_ace(ace, idx + 1) elif mftype == 13: # Cross section data from ENDF File 13 # Energy grid index at which data starts threshold_idx = int(ace.xss[idx]) - 1 n_energy = int(ace.xss[idx + 1]) energy = ace.xss[ace.jxs[1] + threshold_idx: ace.jxs[1] + threshold_idx + n_energy]*EV_PER_MEV # Get photon production cross section photon_prod_xs = ace.xss[idx + 2:idx + 2 + n_energy] neutron_xs = list(rx.xs.values())[0](energy) idx = np.where(neutron_xs > 0.) # Calculate photon yield yield_ = np.zeros_like(photon_prod_xs) yield_[idx] = photon_prod_xs[idx] / neutron_xs[idx] photon.yield_ = Tabulated1D(energy, yield_) else: raise ValueError("MFTYPE must be 12, 13, 16. Got {0}".format( mftype)) # ================================================================== # Photon energy distribution location_start = int(ace.xss[ace.jxs[18] + i]) distribution = AngleEnergy.from_ace(ace, ace.jxs[19], location_start) assert isinstance(distribution, UncorrelatedAngleEnergy) # ================================================================== # Photon angular distribution loc = int(ace.xss[ace.jxs[16] + i]) if loc == 0: # No angular distribution data are given for this reaction, # isotropic scattering is asssumed in LAB energy = np.array([photon.yield_.x[0], photon.yield_.x[-1]]) mu_isotropic = Uniform(-1., 1.) distribution.angle = AngleDistribution( energy, [mu_isotropic, mu_isotropic]) else: distribution.angle = AngleDistribution.from_ace(ace, ace.jxs[17], loc) # Add to list of distributions photon.distribution.append(distribution) photons.append(photon) return photons
def from_ace(cls, ace, idx, ldis): """Generate correlated angle-energy distribution from ACE data Parameters ---------- ace : openmc.data.ace.Table ACE table to read from idx : int Index in XSS array of the start of the energy distribution data (LDIS + LOCC - 1) ldis : int Index in XSS array of the start of the energy distribution block (e.g. JXS[11]) Returns ------- openmc.data.CorrelatedAngleEnergy Correlated angle-energy distribution """ # Read number of interpolation regions and incoming energies n_regions = int(ace.xss[idx]) n_energy_in = int(ace.xss[idx + 1 + 2*n_regions]) # Get interpolation information idx += 1 if n_regions > 0: breakpoints = ace.xss[idx:idx + n_regions].astype(int) interpolation = ace.xss[idx + n_regions:idx + 2*n_regions].astype(int) else: breakpoints = np.array([n_energy_in]) interpolation = np.array([2]) # Incoming energies at which distributions exist idx += 2*n_regions + 1 energy = ace.xss[idx:idx + n_energy_in]*EV_PER_MEV # Location of distributions idx += n_energy_in loc_dist = ace.xss[idx:idx + n_energy_in].astype(int) # Initialize list of distributions energy_out = [] mu = [] # Read each outgoing energy distribution for i in range(n_energy_in): idx = ldis + loc_dist[i] - 1 # intt = interpolation scheme (1=hist, 2=lin-lin). When discrete # lines are present, the value given is 10*n_discrete_lines + intt n_discrete_lines, intt = divmod(int(ace.xss[idx]), 10) if intt not in (1, 2): warn("Interpolation scheme for continuous tabular distribution " "is not histogram or linear-linear.") intt = 2 # Secondary energy distribution n_energy_out = int(ace.xss[idx + 1]) data = ace.xss[idx + 2:idx + 2 + 4*n_energy_out].copy() data.shape = (4, n_energy_out) data[0,:] *= EV_PER_MEV # Create continuous distribution eout_continuous = Tabular(data[0][n_discrete_lines:], data[1][n_discrete_lines:]/EV_PER_MEV, INTERPOLATION_SCHEME[intt], ignore_negative=True) eout_continuous.c = data[2][n_discrete_lines:] if np.any(data[1][n_discrete_lines:] < 0.0): warn("Correlated angle-energy distribution has negative " "probabilities.") # If discrete lines are present, create a mixture distribution if n_discrete_lines > 0: eout_discrete = Discrete(data[0][:n_discrete_lines], data[1][:n_discrete_lines]) eout_discrete.c = data[2][:n_discrete_lines] if n_discrete_lines == n_energy_out: eout_i = eout_discrete else: p_discrete = min(sum(eout_discrete.p), 1.0) eout_i = Mixture([p_discrete, 1. - p_discrete], [eout_discrete, eout_continuous]) else: eout_i = eout_continuous energy_out.append(eout_i) lc = data[3].astype(int) # Secondary angular distributions mu_i = [] for j in range(n_energy_out): if lc[j] > 0: idx = ldis + abs(lc[j]) - 1 intt = int(ace.xss[idx]) n_cosine = int(ace.xss[idx + 1]) data = ace.xss[idx + 2:idx + 2 + 3*n_cosine] data.shape = (3, n_cosine) mu_ij = Tabular(data[0], data[1], INTERPOLATION_SCHEME[intt]) mu_ij.c = data[2] else: # Isotropic distribution mu_ij = Uniform(-1., 1.) mu_i.append(mu_ij) # Add cosine distributions for this incoming energy to list mu.append(mu_i) return cls(breakpoints, interpolation, energy, energy_out, mu)
def isotropic_angle(E_min, E_max): return openmc.data.AngleDistribution( [E_min, E_max], [Uniform(-1., 1.), Uniform(-1., 1.)])
def from_ace(cls, ace, i_reaction): # Get nuclide energy grid n_grid = ace.nxs[3] grid = ace.xss[ace.jxs[1]:ace.jxs[1] + n_grid] * EV_PER_MEV # Convert data temperature to a "300.0K" number for indexing # temperature data strT = str(int(round( ace.temperature * EV_PER_MEV / K_BOLTZMANN))) + "K" if i_reaction > 0: mt = int(ace.xss[ace.jxs[3] + i_reaction - 1]) rx = cls(mt) # Get Q-value of reaction rx.q_value = ace.xss[ace.jxs[4] + i_reaction - 1] * EV_PER_MEV # ================================================================== # CROSS SECTION # Get locator for cross-section data loc = int(ace.xss[ace.jxs[6] + i_reaction - 1]) # Determine starting index on energy grid threshold_idx = int(ace.xss[ace.jxs[7] + loc - 1]) - 1 # Determine number of energies in reaction n_energy = int(ace.xss[ace.jxs[7] + loc]) energy = grid[threshold_idx:threshold_idx + n_energy] # Read reaction cross section xs = ace.xss[ace.jxs[7] + loc + 1:ace.jxs[7] + loc + 1 + n_energy] # For damage energy production, convert to eV if mt == 444: xs *= EV_PER_MEV # Fix negatives -- known issue for Y89 in JEFF 3.2 if np.any(xs < 0.0): warn("Negative cross sections found for MT={} in {}. Setting " "to zero.".format(rx.mt, ace.name)) xs[xs < 0.0] = 0.0 tabulated_xs = Tabulated1D(energy, xs) tabulated_xs._threshold_idx = threshold_idx rx.xs[strT] = tabulated_xs # ================================================================== # YIELD AND ANGLE-ENERGY DISTRIBUTION # Determine multiplicity ty = int(ace.xss[ace.jxs[5] + i_reaction - 1]) rx.center_of_mass = (ty < 0) if i_reaction < ace.nxs[5] + 1: if ty != 19: if abs(ty) > 100: # Energy-dependent neutron yield idx = ace.jxs[11] + abs(ty) - 101 yield_ = Tabulated1D.from_ace(ace, idx) else: # 0-order polynomial i.e. a constant yield_ = Polynomial((abs(ty), )) neutron = Product('neutron') neutron.yield_ = yield_ rx.products.append(neutron) else: assert mt in FISSION_MTS rx.products, rx.derived_products = _get_fission_products_ace( ace) for p in rx.products: if p.emission_mode in ('prompt', 'total'): neutron = p break else: raise Exception( "Couldn't find prompt/total fission neutron") # Determine locator for ith energy distribution lnw = int(ace.xss[ace.jxs[10] + i_reaction - 1]) while lnw > 0: # Applicability of this distribution neutron.applicability.append( Tabulated1D.from_ace(ace, ace.jxs[11] + lnw + 2)) # Read energy distribution data neutron.distribution.append( AngleEnergy.from_ace(ace, ace.jxs[11], lnw, rx)) lnw = int(ace.xss[ace.jxs[11] + lnw - 1]) else: # Elastic scattering mt = 2 rx = cls(mt) # Get elastic cross section values elastic_xs = ace.xss[ace.jxs[1] + 3 * n_grid:ace.jxs[1] + 4 * n_grid] # Fix negatives -- known issue for Ti46,49,50 in JEFF 3.2 if np.any(elastic_xs < 0.0): warn("Negative elastic scattering cross section found for {}. " "Setting to zero.".format(ace.name)) elastic_xs[elastic_xs < 0.0] = 0.0 tabulated_xs = Tabulated1D(grid, elastic_xs) tabulated_xs._threshold_idx = 0 rx.xs[strT] = tabulated_xs # No energy distribution for elastic scattering neutron = Product('neutron') neutron.distribution.append(UncorrelatedAngleEnergy()) rx.products.append(neutron) # ====================================================================== # ANGLE DISTRIBUTION (FOR UNCORRELATED) if i_reaction < ace.nxs[5] + 1: # Check if angular distribution data exist loc = int(ace.xss[ace.jxs[8] + i_reaction]) if loc < 0: # Angular distribution is given as part of a product # angle-energy distribution angle_dist = None elif loc == 0: # Angular distribution is isotropic energy = [0.0, grid[-1]] mu = Uniform(-1., 1.) angle_dist = AngleDistribution(energy, [mu, mu]) else: angle_dist = AngleDistribution.from_ace(ace, ace.jxs[9], loc) # Apply angular distribution to each uncorrelated angle-energy # distribution if angle_dist is not None: for d in neutron.distribution: d.angle = angle_dist # ====================================================================== # PHOTON PRODUCTION rx.products += _get_photon_products_ace(ace, rx) return rx