Jump to content



Featured Articles

Check out the latest featured articles.

File Library

Check out the latest downloads available in the File Library.

New Article

Product Viscosity vs. Shear

Featured File

Vertical Tank Selection

New Blog Entry

Low Flow in Pipes- posted in Ankur's blog

0

2D Heat And Mass Transfer Modelling Using Fdm In Python

python modelling heat transfer mass transfer

5 replies to this topic
Share this topic:
| More

#1 luoluowudi

luoluowudi

    Brand New Member

  • Members
  • 3 posts

Posted 03 September 2023 - 10:02 PM

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



#2 Pilesar

Pilesar

    Gold Member

  • Members
  • 1,386 posts

Posted 03 September 2023 - 11:23 PM

I am not expert so my free advice is worth what you pay for it. I will throw some ideas out here to consider trying: Leave the axial diffusion term in and just constrain it very tightly. Fix some variables and plot the convergence of the other equations. Unconstrain the problem by opening boundaries. Constrain the equations around a known solution to watch convergence and adjust the constraints gradually to get the needed solution. Plot the solution path. Use a different convergence technique. Choose better initial estimate values.



#3 breizh

breizh

    Gold Member

  • Admin
  • 6,349 posts

Posted 04 September 2023 - 03:15 AM

Hi,

Not familiar with python, I will keep your initial model and reduce the radial coefficient of diffusion to see the impact on the result.

Hope this is helping you.

Breizh 



#4 luoluowudi

luoluowudi

    Brand New Member

  • Members
  • 3 posts

Posted 04 September 2023 - 06:47 AM

Hi Breizh

Thank you for your reply.

Do you mean taking out the lamda_er in the equation without axial diffusion term?

u_(s ) ρ_f C_(p,f)  ∂T/∂z=(∂^2 T/∂r^2 )+(1/r  ∂T/∂r)-ΔHρ_c r_BT

 

Hi,

Not familiar with python, I will keep your initial model and reduce the radial coefficient of diffusion to see the impact on the result.

Hope this is helping you.

Breizh 



#5 luoluowudi

luoluowudi

    Brand New Member

  • Members
  • 3 posts

Posted 04 September 2023 - 06:47 AM

Thanks Pilesar, the solution sounds promising. I will try it out 

I am not expert so my free advice is worth what you pay for it. I will throw some ideas out here to consider trying: Leave the axial diffusion term in and just constrain it very tightly. Fix some variables and plot the convergence of the other equations. Unconstrain the problem by opening boundaries. Constrain the equations around a known solution to watch convergence and adjust the constraints gradually to get the needed solution. Plot the solution path. Use a different convergence technique. Choose better initial estimate values.



#6 breizh

breizh

    Gold Member

  • Admin
  • 6,349 posts

Posted 04 September 2023 - 07:33 AM

Hi,

No , what I mean is to divide the value of lambda by 10, 20,..,100 ... to see the impact on the calculation.

My 2 cents

Breizh 






Similar Topics