Untitled
unknown
python
12 days ago
7.6 kB
4
Indexable
import numpy as np def LU_factor(A): ''' Perform LU factorization on matrix A. L has implied 1s along its diagonal, and U is stored in the upper triangular part of A. The lower triangular part of A stores the multipliers for L. ''' N = A.shape[0] for col in range(N): for row in range(col + 1, N): mod_row = np.copy(A[row]) factor = mod_row[col] / A[col, col] mod_row -= factor * A[col, :] # Store the factor in the modified row mod_row[col] = factor # Store only the part of the modified row that makes U mod_row = mod_row[col:N] A[row, col:N] = mod_row return A def LU_solve(A, b): ''' Solve the system Ax = b using the LU factorization stored in A. ''' N = A.shape[0] x = np.zeros(N) y = np.zeros(N) # dummy variable for L^-1 b # Forward solve for y in Ly = b for row in range(N): RHS = b[row] for col in range(row): RHS -= y[col] * A[row, col] y[row] = RHS # Backward solve for x in Ux = y for row in range(N - 1, -1, -1): RHS = y[row] for col in range(row + 1, N): RHS -= x[col] * A[row, col] x[row] = RHS / A[row, row] return x def inverse_power(A, B, epsilon=1e-6): ''' Solve the generalized eigenvalue problem Ax = lB using the inverse power iteration algorithm. Args: A: LHS matrix (cannot be singular) B: RHS matrix epsilon: tolerance on eigenvalue Output: l: smallest eigenvalue of the problem x: associated eigenvector of smallest eigenvalue ''' Nrows, Ncols = A.shape # Generate initial guess x = np.random.random(Nrows) x = x / np.linalg.norm(x) # normalize the initial guess l_old = 0 converged = False # Compute LU factorization of A A_factored = LU_factor(np.copy(A)) iteration = 0 b_0s = [] while not converged: iteration += 1 b = LU_solve(A_factored, np.dot(B, x)) b_0s.append(b[0]) l = np.linalg.norm(b) x = b / l converged = (np.fabs(l - l_old) < epsilon) #print(f"Iteration: {iteration}, magnitude of l: {1/l:.4f}, epsilon: {np.fabs(l - l_old):.3e}") l_old = l sign = b_0s[iteration - 1] / b_0s[iteration - 2] return sign / l, x def create_grid(R, I): """ Create cell edges and centers for a domain of size R and for I cells Args: R: size of domain I: number of cells Returns: Delta_r: width of each cell centers: cell centers of the grid edges: cell edges of the grid """ Delta_r = float(R) / I # divide size by # of cells # Calculate the centers by getting each Delta_r # and adding 0.5 * Delta_r centers = np.arange(I) * Delta_r + 0.5 * Delta_r # Get edges by going beyond one cell edges = np.arange(I + 1) * Delta_r return Delta_r, centers, edges def diffusion_setup(R, I, D, Sig_a, nuSig_f, BC, geometry): ''' Set up the diffusion equation in a 1-D geometry using cell-averaged quantities. Args: R: size of domain I: number of cells D: function, D(r), returns diffusion coefficient at r Sig_a: function, Sig_a(r), returns macroscopic absorption cross-section at r nuSig_f: function, nuSig_f(r), returns nu * macroscopic fission cross-section at r BC: boundary conditions at r=R in form [A, B, C] geometry: 0 = slab 1 = cylindrical 2 = spherical Returns: centers: cell centers of grid A: LHS matrix B: RHS matrix ''' Delta_R, centers, edges = create_grid(R, I) A = np.zeros((I + 1, I + 1)) B = np.zeros((I + 1, I + 1)) # Define surface areas S at cell edges and volumes V at dr if geometry == 0: # slab S = np.zeros_like(edges) + 1 # 1-D slab surface area S[0] = 0. # reflective BC at center V = np.zeros_like(edges) + Delta_R elif geometry == 1: # cylinder S = 2. * np.pi * edges # cylinder surface area V = np.pi * (edges[1:I + 1]**2 - edges[0:I]**2) # cylinder volume elif geometry == 2: # sphere S = 4 * np.pi * edges**2 # sphere surface area V = 4/3 * np.pi * (edges[1:I + 1]**3 - edges[0:I]**3) # sphere volume # Setup the BC at R A[I, I] = (BC[0] / 2 + BC[1] / Delta_R) A[I, I - 1] = (BC[0] / 2 - BC[1] / Delta_R) # Fill A matrix Dplus = 0 for i in range(I): r = centers[i] Dminus = Dplus Dplus = 2 * (D(r) * D(r + Delta_R)) / (D(r) + D(r + Delta_R)) A[i, i] = 1 / (Delta_R * V[i]) * Dplus * S[i + 1] + Sig_a(r) B[i, i] = nuSig_f(r) if i > 0: A[i, i - 1] = -1 * Dminus / (Delta_R * V[i]) * S[i] A[i, i] += 1. / (Delta_R * V[i]) * Dminus * S[i] A[i, i + 1] = -Dplus / (Delta_R * V[i]) * S[i + 1] return centers, A, B def DiffusionEigenvalue(R, I, D, Sig_a, nuSig_f, BC, geometry, epsilon=1e-8): ''' Solve the diffusion eigenvalue problem. Args: R: size of domain I: number of cells D: function, D(r), returns diffusion coefficient at r Sig_a: function, Sig_a(r), returns macroscopic absorption cross-section at r nuSig_f: function, nuSig_f(r), returns nu * macroscopic fission cross-section at r BC: boundary conditions at r=R in form [A, B, C] geometry: 0 = slab 1 = cylindrical 2 = spherical epsilon: tolerance on eigenvalue Returns: k: effective multiplication factor phi: eigenvector (flux) centers: cell centers of grid ''' centers, A, B = diffusion_setup(R, I, D, Sig_a, nuSig_f, BC, geometry) l, phi = inverse_power(A, B, epsilon) k = 1 / l phi = phi[0:I] return k, phi, centers # Define the nuclear data for Pu-239 sigma_f = 1.85 # barns sigma_a = 2.11 # barns sigma_tr = 6.8 # barns nu = 2.98 density = 19.74 # g/cm^3 N_A = 6.02214076e23 # Avogadro's number M = 239 # molar mass of Pu-239 # Calculate macroscopic cross-sections N = density * N_A / M # number density in atoms/cm^3 Sigma_f = sigma_f * 1e-24 * N # macroscopic fission cross-section Sigma_a = sigma_a * 1e-24 * N # macroscopic absorption cross-section Sigma_tr = sigma_tr * 1e-24 * N # macroscopic transport cross-section D = 1 / (3 * Sigma_tr) # diffusion coefficient # Define the functions for D, Sigma_a, and nuSigma_f def D_func(r): if r < 2: # inner hollow region return 100 # cm else: return D def Sigma_a_func(r): if r < 2: # inner hollow region return 0 else: return Sigma_a def nuSigma_f_func(r): if r < 2: # inner hollow region return 0 else: return nu * Sigma_f # Boundary conditions: reflective at r=0, vacuum at r=R BC = [1, 0, 0] # reflective at r=0, vacuum at r=R # Geometry: spherical geometry = 2 # Radius of the reactor R = 7 # cm # Number of cells I = 100 # Compute k_eff with the Pu slug inserted (solid sphere) k_eff_solid, phi_solid, centers_solid = DiffusionEigenvalue(R, I, D_func, Sigma_a_func, nuSigma_f_func, BC, geometry) # Compute k_eff without the Pu slug inserted (hollow sphere) k_eff_hollow, phi_hollow, centers_hollow = DiffusionEigenvalue(R, I, D_func, Sigma_a_func, nuSigma_f_func, BC, geometry) print(f"k_eff with Pu slug inserted: {k_eff_solid:.4f}") print(f"k_eff without Pu slug inserted: {k_eff_hollow:.4f}")
Editor is loading...
Leave a Comment