Homogenization_with_FFT/first_order_homogenization.py

140 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import numpy as np
def solve_poisson_fft_tilde(f_tilde, Qx, Qy):
"""
Solve the Poisson equation in Fourier space directly.
"""
k2 = Qx**2 + Qy**2
k2[0, 0] = 1 # avoid division by zero
u_tilde = -f_tilde / k2 # Attention, here is for solving the Poisson equation with \Delta u + f = 0, so here the f_tilde could be right hand side of the Poisson equation
u_tilde[0, 0] = 0 # set the zero-frequency component to zero
return u_tilde
def fft_gradient(u_tilde, Qx, Qy):
"""
For a given u_tilde in Fourier space, compute its gradient in real space.
"""
ux_tilde = 1j * Qx * u_tilde
uy_tilde = 1j * Qy * u_tilde
ux = np.fft.ifft2(ux_tilde).real
uy = np.fft.ifft2(uy_tilde).real
return ux, uy
def divergence_to_tilde(fx, fy, Qx, Qy):
"""
Compute the divergence of a vector field (fx, fy) in real space, then return its Fourier transform.
"""
fx_tilde = np.fft.fft2(fx)
fy_tilde = np.fft.fft2(fy)
div_tilde = 1j * Qx * fx_tilde + 1j * Qy * fy_tilde
return div_tilde
def solve_microfluctuation_poisson(G, G_0, f, Qx, Qy, epsilon=1e-8, max_iter=5000):
"""
To solve the equation: ∇·(G∇N) + f = 0
With fixed point iteration: G_0 ∇²N + ∇·((G-G_0)∇N) + f = 0
Solve N^(0) with ∇²N = -f/G_0 as initial guess
"""
f_tilde = np.fft.fft2(f)
N_init_tilde = solve_poisson_fft_tilde(-f_tilde / G_0, Qx, Qy)
N_tilde = N_init_tilde.copy()
for _ in range(max_iter): # The loop simply repeats the loop body max_iter times, without requiring a count value in each loop iteration.
# Compute the gradient of N in real space
Nx, Ny = fft_gradient(N_tilde, Qx, Qy)
# Compute (G-G_0)*grad(u) in real space, then compute its divergence in Fourier space
temp_x = (G - G_0)*Nx
temp_y = (G - G_0)*Ny
f_n_tilde = divergence_to_tilde(temp_x, temp_y, Qx, Qy)
# Slove the Poisson equation in Fourier space
# G_0 ΔN = -f - f_n
# ΔN = -(f_n + f)/G_0
N_next_tilde = solve_poisson_fft_tilde(-(f_n_tilde + f_tilde)/G_0, Qx, Qy)
# Check the convergence
if np.linalg.norm(np.fft.ifft2(N_next_tilde - N_tilde)) < epsilon:
N_tilde = N_next_tilde
break
N_tilde = N_next_tilde
N = np.fft.ifft2(N_tilde).real
return N
def homogeneous_effective_modulus(G, G_0, Qx, Qy, n, m):
# For N1_1:
# f1 = -∇·(G(y)*e_1) = -∇·(G(y), 0)
# ∇·(G(y),0) = dG/dx
dG_dx = np.fft.ifft2(1j * Qx * np.fft.fft2(G)).real
f1 = dG_dx
N1_1 = solve_microfluctuation_poisson(G, G_0, f1, Qx, Qy)
# For N1_2:
# f2 = -∇·(G(y)*e_2) = -∇·(0, G(y))
# ∇·(0,G(y)) = dG/dy
dG_dy = np.fft.ifft2(1j * Qy * np.fft.fft2(G)).real
f2 = dG_dy
N1_2 = solve_microfluctuation_poisson(G, G_0, f2, Qx, Qy)
# C_0 = ∫ G(y) [∇N1(y) + I] dy
# N1 = [N1_1, N1_2]
N1_1_tilde = np.fft.fft2(N1_1)
N1_2_tilde = np.fft.fft2(N1_2)
N1_1x, N1_1y = fft_gradient(N1_1_tilde, Qx, Qy)
N1_2x, N1_2y = fft_gradient(N1_2_tilde, Qx, Qy)
# ∇N1 is [[N1_1x, N1_1y],
# [N1_2x, N1_2y]]
# ∇N1 + I is [[N1_1x+1, N1_1y ],
# [N1_2x , N1_2y+1]]
N1_grad_plus_I = np.zeros((n,m,2,2))
N1_grad_plus_I[:,:,0,0] = N1_1x + 1.0
N1_grad_plus_I[:,:,0,1] = N1_1y
N1_grad_plus_I[:,:,1,0] = N1_2x
N1_grad_plus_I[:,:,1,1] = N1_2y + 1.0
# Compute C_0 = ∫ G(y)*[∇N1+I] dy
C0 = np.zeros((2,2))
for i in range(2):
for j in range(2):
# Integral of unit cell, the area of the unit cell is 1
C0[i,j] = np.mean(G * N1_grad_plus_I[:,:,i,j])
return C0, N1_1, N1_2, N1_grad_plus_I
# Define gradient function to compute ∇v0
def fft_gradient_2d(u_tilde, Qx, Qy):
ux_tilde = (1j * Qx) * u_tilde
uy_tilde = (1j * Qy) * u_tilde
ux = np.fft.ifft2(ux_tilde).real
uy = np.fft.ifft2(uy_tilde).real
return ux, uy
def first_order_homogenization(F_macro, Qx_macro, Qy_macro, C0, N1_1, N1_2, eta):
F_macro_tilde = np.fft.fft2(F_macro)
denominator = (C0[0,0]*Qx_macro**2 + (C0[0,1]+C0[1,0])*Qx_macro*Qy_macro + C0[1,1]*Qy_macro**2) #Deducing process is in the appendix
denominator[0,0] = 1.0 # avoid division by zero
v0_tilde = F_macro_tilde / denominator # v0_tilde = -F_tilde / denominator there should not be a minus sign here
v0_tilde[0,0] = 0.0
v0 = np.fft.ifft2(v0_tilde).real
v0_ux, v0_uy = fft_gradient_2d(np.fft.fft2(v0), Qx_macro, Qy_macro)
# u(x) ≈ v_0(x) + η N_1(x/η) · ∇v_0(x)
n = int(1/eta)
N1_1_global = N1_1[::n,::n]
N1_2_global = N1_2[::n,::n]
# tile
N1_1_global = np.tile(N1_1_global, (n,n))
N1_2_global = np.tile(N1_2_global, (n,n))
u = v0 + eta * (N1_1_global * v0_ux + N1_2_global * v0_uy)
return u, v0, v0_ux, v0_uy