Hi Guys
I am solving these 3 equations to get a temperature and concentration profile in a PFR.
Mass balance: u_(s ) ∂C/∂z=D_(er ) ((∂^2 C)/(∂r^2 )+1/r (∂C_i)/∂r)+D_(er ) ((∂^2 C)/(∂z^2 ))-ρ_c r_BT Energy balance: u_(s ) ρ_f C_(p,f) ∂T/∂z=λ_(er ) ((∂^2 T)/(∂r^2 )+1/r ∂T/∂r)+λ_(er ) ((∂^2 T)/(∂z^2 ))-ΔHρ_c r_BT Reaction rate r_BT=k_0 e^(-E/RT) C
The mass balance includes radius diffusion, axis diffusion and convection and source term and same for energy balance.
I discretise the equation and wrote in Python and it works well. The discretisation form as shown below:
T_(i,j)=1/4 (T_(i+1,j)+T_(i-1,j)+T_(i,j+1)+T_(i,j-1) )+Δr/8r[i] (T_(i+1,j)-T_(i-1,j) )-AΔr/(8λ_(er ) ) (T_(i,j+1)-T_(i,j-1) )-〖Δr〗^2/(4λ_(er ) ) B C_(i,j)=〖1/4(C〗_(i+1,j)+C_(i-1,j)+C_(i,j+1)+C_(i,j-1))+Δr/8r[i] (C_(i+1,j)-C_(i-1,j) )-(u_(s ) Δr)/(8D_(er ) ) (C_(i,j+1)-C_(i,j-1) )-〖〖Δr〗^2/(4D_(er ) ) ρ〗_c 〖*r〗_BT where A = u_(s ) ρ_f C_(p,f) , B= 6×ΔHρ_c r_BT
However, I do not want to include axial diffusion term, so the equatios become like:
Energy Balance: u_(s ) ρ_f C_(p,f) ∂T/∂z=λ_(er ) ((∂^2 T)/(∂r^2 )+1/r ∂T/∂r)-ΔHρ_c r_BT Mass Balance: u_(s ) ∂C/∂z=D_(er ) ((∂^2 C)/(∂r^2 )+1/r (∂C_i)/∂r)-ρ_c r_BT
with discretisation form:
T_(i,j)=1/2 (T_(i+1,j)+T_(i-1,j) )+Δr/4r[i] (T_(i+1,j)-T_(i-1,j) )-AΔr/(4λ_(er ) ) (T_(i,j+1)-T_(i,j-1) )-〖Δr〗^2/(2λ_(er ) ) B C_(i,j)=〖1/2(C〗_(i+1,j)+C_(i-1,j))+Δr/4r[i] (C_(i+1,j)-C_(i-1,j) )-(u_(s ) Δr)/(4D_(er ) ) (C_(i,j+1)-C_(i,j-1) )-〖〖Δr〗^2/(2D_(er ) ) ρ〗_c 〖*r〗_BT
When I delete the axial diffusion term, the code cannot be converged, would you please give some advice? I have been working on it for a week but had no progress..
Code is shown below, it would be much appreciate if you could give some advice..
import numpy as np import matplotlib.ticker as ticker import matplotlib.pyplot as plt from matplotlib.ticker import FuncFormatter import pandas as pd from numba import jit, prange # Set total range r_range = 0.01 # m z_range = 0.1 # m # Set grid size grid_size = 0.00005 # m # Calculate the number of points num_points_r = int(r_range / grid_size) + 1 num_points_z = int(z_range / grid_size) + 1 # Set grid r_start, r_end = 0, r_range z_start, z_end = 0, z_range r = np.linspace(r_start, r_end, num_points_r) z = np.linspace(z_start, z_end, num_points_z) # grid distance dr = r[1] - r[0] dz = z[1] - z[0] # initialization of C, T C = np.ones((num_points_r, num_points_z)) * 3000 T = np.ones((num_points_r, num_points_z)) * 450 Q_reaction = np.zeros((num_points_r, num_points_z)) # Set parameter u_s = 0.00049 # superficial velocity m/s Der = 0.000083 # effective radial diffusion coefficient m2/s rho_catalyst = 4000 # kg/m3 rho_fluid = 704 # kg/m3 cp_fluid = 3037 # J/ kg K lambda_er = 1.923 # effective radial thermal conductivity, W/ m K # lambda_er = 6.523 # effective radial thermal conductivity, W/ m K delta_H = 65000 # J/ mol_H2 Ts = 570 # wall temperature A = u_s * rho_fluid * cp_fluid # Set reaction parameter k = 10816 # m3/kg s E = 117000 # J/mol R_gas = 8.314 # J/mol K # Set initial parameter C0 = 3622 # mol/m3 rho/MW kg/m3 / kg/mol T0 = 570 # K Q0 = k * np.exp(-E / (R_gas * T0)) * C0 Q_reaction[:, :] = Q0 # initial reaction rate, mol_BT/kg_cat s # Boundary conditions T[:, 0] = T0 # Inlet T[:, -1] = T[:, -2] # Outlet T[0, :] = T[1, :] # lower boundary T[-1, :] = T0 # constant wall temp, up boundary C[:, 0] = C0 # Inlet C[:, -1] = C[:, -2] # Outlet C[0, :] = C[1, :] # lower boundary C[-1, :] = C[-2, :] # upper boundary Q_reaction[:, 0] = Q0 # print("Q_reaction:", Q_reaction) # iterative to convergence tolerance = 1e-5 not_converged = True iteration = 0 max_iterations = 50000000 close_to_convergence = False # Parallelize code using jit compiler @jit(nopython=True, parallel=True) def compute_iteration(C_current, T_current, Q_reaction_current, dr_val, r_val, A_val, lambda_er_val, delta_H_val, rho_catalyst_val, Der_val, u_s_val, k_val, E_val, R_gas_val): C_result = np.copy(C_current) T_result = np.copy(T_current) Q_reaction_result = np.copy(Q_reaction_current) for j in prange(1, num_points_z - 1): # loop at z direction for i in prange(1, num_points_r - 1): # loop at r direction T_result[i, j] = (1 / 2) * (T_current[i - 1, j] + T_current[i + 1, j]) + (dr_val / (4 * r_val[i])) * \ (T_current[i + 1, j] - T_current[i - 1, j]) - ( (A_val * dr_val) * (T_current[i, j + 1] - T_current[i, j - 1]) / ( 4 * lambda_er_val)) + \ (-delta_H_val * 6 * Q_reaction_current[i, j] * rho_catalyst_val * dr_val ** 2) / ( 2 * lambda_er_val) C_result[i, j] = (1 / 2) * (C_current[i - 1, j] + C_current[i + 1, j]) + (dr_val / (4 * r_val[i])) * ( C_current[i + 1, j] - C_current[i - 1, j]) - \ ((u_s_val * dr_val) / (4 * Der_val)) * (C_current[i, j + 1] - C_current[i, j - 1]) - ( rho_catalyst_val * Q_reaction_current[i, j]) * \ dr_val ** 2 / (Der_val * 2) Q_reaction_result[i, j] = k_val * np.exp(-E_val / (R_gas_val * T_result[i, j])) * C_result[i, j] return C_result, T_result, Q_reaction_result while not_converged and iteration < max_iterations: C_new, T_new, Q_reaction_new = compute_iteration(C, T, Q_reaction, dr, r, A, lambda_er, delta_H, rho_catalyst, Der, u_s, k, E, R_gas) # Compute the maximum difference between current and previous iterations max_diff_T = np.max(np.abs(T_new - T)) max_diff_C = np.max(np.abs(C_new - C)) if (iteration + 1) % 1000 == 0: print( f"Iteration {iteration + 1}: Max difference in T = {max_diff_T:.10f}, Max difference in C = {max_diff_C:.10f}") # If the maximum difference is below our threshold, set the flag to check for convergence in the next iteration if max_diff_T < tolerance and max_diff_C < tolerance: close_to_convergence = True # Check for convergence if close_to_convergence is True if close_to_convergence: not_converged = max_diff_T > tolerance or max_diff_C > tolerance # Update C, T, and Q_reaction for the next iteration C, T, Q_reaction = C_new, T_new, Q_reaction_new # Boundary conditions remain the same T[:, 0] = T0 # Inlet T[:, -1] = T[:, -2] # Outlet T[0, :] = T[1, :] # lower boundary T[-1, :] = T0 # constant wall temp, up boundary C[:, 0] = C0 # Inlet C[:, -1] = C[:, -2] # Outlet C[0, :] = C[1, :] # lower boundary C[-1, :] = C[-2, :] # upper boundary Q_reaction[:, 0] = Q0 iteration += 1 fig, ax = plt.subplots(2, 1, figsize=(10, 10)) # 3 rows, 1 column # Temperature plot img1 = ax[0].imshow(T, cmap='hot', interpolation='nearest', origin='lower', vmin=np.min(T), vmax=np.max(T), aspect=0.5 * len(z) / len(r), extent=[z_start, z_end, r_start, r_end]) fig.colorbar(img1, ax=ax[0], format=ticker.FuncFormatter(lambda x, pos: '{:.10f}'.format(x)), label='Temperature (K)') ax[0].set_title('Heat Distribution') ax[0].set_xlabel('z') ax[0].set_ylabel('r') # Concentration plot img2 = ax[1].imshow(C, cmap='hot', interpolation='nearest', origin='lower', vmin=np.min(C), vmax=np.max(C), aspect=0.5 * len(z) / len(r), extent=[z_start, z_end, r_start, r_end]) fig.colorbar(img2, ax=ax[1], format=ticker.FuncFormatter(lambda x, pos: '{:.10f}'.format(x)), label='Concentration (mol/m3)') ax[1].set_title('Concentration') ax[1].set_xlabel('z') ax[1].set_ylabel('r') plt.tight_layout() plt.show() r_index_half = num_points_r // 2 # Index corresponding to r = r_range/2 plt.figure(figsize=(12, 6)) # Subplot for Temperature plt.subplot(1, 2, 1) # 1 row, 2 columns, first subplot plt.plot(z, T[0, :], label=f'r = 0') plt.plot(z, T[r_index_half, :], label=f'r = r_range/2') plt.plot(z, T[-1, :], label=f'r = r_range') plt.title("Temperature along z axis") plt.xlabel("z") plt.gca().yaxis.set_major_formatter(FuncFormatter(lambda x, pos: '{:.10f}'.format(x))) # Change to decimal format plt.ylabel("Temperature") plt.legend() # Subplot for Concentration plt.subplot(1, 2, 2) # 1 row, 2 columns, second subplot plt.plot(z, C[0, :], label=f'r = 0') plt.plot(z, C[r_index_half, :], label=f'r = r_range/2') plt.plot(z, C[-1, :], label=f'r = r_range') plt.title("Concentration along z axis") plt.xlabel("z") plt.gca().yaxis.set_major_formatter(FuncFormatter(lambda x, pos: '{:.10f}'.format(x))) # Change to decimal format plt.ylabel("Concentration") plt.legend() plt.tight_layout() plt.show() # Plot 4 plt.plot(z, Q_reaction[r_index_half, :], label='MCH') plt.title("Reaction rate") plt.xlabel("z") plt.gca().yaxis.set_major_formatter(FuncFormatter(lambda x, pos: '{:.10f}'.format(x))) # Change to decimal format plt.ylabel("Rate") plt.legend() plt.show()