In [1]:
%reload_ext autoreload
%autoreload 2
In [2]:
from PDESolver import *
2D tests in periodic conditions¶
In [3]:
# Symbolic variables
x, y, xi, eta = symbols('x y xi eta', real=True)
u = Function('u')(x, y)
# Symbolic equation
symbol = xi**2 + eta**2 + 1
equation = Eq(psiOp(symbol, u), -sin(x) * sin(y))
# Define exact solution
def u_exact(x, y):
return -np.sin(x) * np.sin(y) / 3
# Solver creation
solver = PDESolver(equation)
# Domain
Lx, Ly = 2 * np.pi, 2 * np.pi
Nx, Ny = 128, 128
# Setup with exact solution as source
solver.setup(Lx=Lx, Ly=Ly, Nx=Nx, Ny=Ny, initial_condition=None)
# Stationary solution
u_num = solver.solve_stationary_psiOp()
# Accuracy test
solver.test(u_exact=u_exact, threshold=20, component='real')
solver.show_stationary_solution(u=u_num, component='real')
********************************* * Partial differential equation * *********************************
⎛ 2 2 ⎞
psiOp⎝η + ξ + 1, u(x, y)⎠ = -sin(x)⋅sin(y)
********************
* Equation parsing *
********************
Equation rewritten in standard form: sin(x)*sin(y) + psiOp(eta**2 + xi**2 + 1, u(x, y))
⚠️ psiOp detected: skipping expansion for safety
Expanded equation: sin(x)*sin(y) + psiOp(eta**2 + xi**2 + 1, u(x, y))
Analyzing term: sin(x)*sin(y)
--> Classified as source term
Analyzing term: psiOp(eta**2 + xi**2 + 1, u(x, y))
--> Classified as pseudo linear term (psiOp)
Final linear terms: {}
Final nonlinear terms: []
Symbol terms: []
Pseudo terms: [(1, eta**2 + xi**2 + 1)]
Source terms: [sin(x)*sin(y)]
⚠️ Pseudo‑differential operator detected: all other linear terms have been rejected.
⚠️ For psiOp, use interactive_symbol_analysis.
*******************************
* Solving the stationnary PDE *
*******************************
boundary condition: periodic
✅ Elliptic pseudo-differential symbol: inversion allowed.
Right inverse asymptotic symbol:
1
───────────
2 2
η + ξ + 1
⚡ Optimization: Symbol independent of x and y — direct product in 2D Fourier.
Testing a stationary solution.
Test error : 3.321e-16
With psiOp() depending on spatial variables¶
In [4]:
# 1) Define symbolic variables
x, y, xi, eta = symbols('x y xi eta', real=True)
u = Function('u')(x, y)
# 2) Define the 2D symbol p(x, y, xi, eta)
p_symbol = xi**2 + eta**2 + x**2 + y**2 + 1
# 3) Choose an exact solution u_exact(x, y)
u_exact_expr = exp(-x**2 - y**2)
def u_exact(x, y):
return np.exp(-x**2 - y**2)
# 4) Compute the right-hand side f_expr = P[u_exact] symbolically
f_expr = (-3*x**2 - 3*y**2 + 5)*u_exact_expr
# 5) Build the stationary equation: psiOp(p_symbol, u) = f_expr
equation_2d = Eq(psiOp(p_symbol, u), f_expr)
# 6) Initialize and setup the solver
solver2d = PDESolver(equation_2d)
solver2d.setup(Lx=10.0, Ly=10.0, Nx=64, Ny=64, initial_condition=None)
# 7) Solve stationary psiOp problem (first-order asymptotic inverse)
u_num2d = solver2d.solve_stationary_psiOp(order=1)
# 8) Test numerical vs exact
solver2d.test(u_exact=u_exact, threshold=3e-1, component='real')
*********************************
* Partial differential equation *
*********************************
2 2
⎛ 2 2 2 2 ⎞ ⎛ 2 2 ⎞ - x - y
psiOp⎝η + x + ξ + y + 1, u(x, y)⎠ = ⎝- 3⋅x - 3⋅y + 5⎠⋅ℯ
********************
* Equation parsing *
********************
Equation rewritten in standard form: -(-3*x**2 - 3*y**2 + 5)*exp(-x**2 - y**2) + psiOp(eta**2 + x**2 + xi**2 + y**2 + 1, u(x, y))
⚠️ psiOp detected: skipping expansion for safety
Expanded equation: -(-3*x**2 - 3*y**2 + 5)*exp(-x**2 - y**2) + psiOp(eta**2 + x**2 + xi**2 + y**2 + 1, u(x, y))
Analyzing term: -(-3*x**2 - 3*y**2 + 5)*exp(-x**2 - y**2)
--> Classified as source term
Analyzing term: psiOp(eta**2 + x**2 + xi**2 + y**2 + 1, u(x, y))
--> Classified as pseudo linear term (psiOp)
Final linear terms: {}
Final nonlinear terms: []
Symbol terms: []
Pseudo terms: [(1, eta**2 + x**2 + xi**2 + y**2 + 1)]
Source terms: [-(-3*x**2 - 3*y**2 + 5)*exp(-x**2 - y**2)]
⚠️ Pseudo‑differential operator detected: all other linear terms have been rejected.
⚠️ For psiOp, use interactive_symbol_analysis.
*******************************
* Solving the stationnary PDE *
*******************************
boundary condition: periodic
✅ Elliptic pseudo-differential symbol: inversion allowed.
Right inverse asymptotic symbol:
4.0⋅ⅈ⋅η⋅y 4.0⋅ⅈ⋅x⋅ξ
──────────────────────── + ────────────────────────
2 2
⎛ 2 2 2 2 ⎞ ⎛ 2 2 2 2 ⎞
⎝η + x + ξ + y + 1⎠ ⎝η + x + ξ + y + 1⎠ 1
- ─────────────────────────────────────────────────── + ─────────────────────
2 2 2 2 2 2 2 2
η + x + ξ + y + 1 η + x + ξ + y + 1
⚙️ 2D Kohn-Nirenberg Quantification
Testing a stationary solution. Test error : 2.038e-01
Transport equation¶
In [5]:
# Definition of the 2D transport equation
t, x, y, kx, ky, omega = symbols('t x y kx ky omega')
u_func = Function('u')
u = u_func(t, x, y)
transport_eq = Eq(diff(u, t), -diff(u, x) - diff(u, y)) # Diagonal transport
# Creation of the solver with periodic conditions
solver = PDESolver(transport_eq)
# Configuration of the 2D domain and initial condition
L = 10.0 # Square domain [-5, 5] × [-5, 5]
N = 512 # Spatial resolution
Lt=3.0
solver.setup(
Lx=L, Ly=L,
Nx=N, Ny=N,
Lt=Lt, Nt=2000,
initial_condition=lambda x, y: np.exp(-(x**2 + y**2)), # 2D Gaussian
n_frames=100
)
# Exact solution with periodic wrapping
def u_exact(x, y, t):
Lx = Ly = L # Square domain
x_shift = (x - t + Lx/2) % Lx - Lx/2 # Periodic wrapping in x
y_shift = (y - t + Ly/2) % Ly - Ly/2 # Periodic wrapping in y
return np.exp(-(x_shift**2 + y_shift**2))
# Solving and validation
solver.solve()
# Automatic testing
n_test = 4
test_set = [solver.test(u_exact=u_exact, t_eval=i * Lt / n_test, threshold=5e-2, component='real') for i in range(n_test + 1)]
*********************************
* Partial differential equation *
*********************************
∂ ∂ ∂
──(u(t, x, y)) = - ──(u(t, x, y)) - ──(u(t, x, y))
∂t ∂x ∂y
********************
* Equation parsing *
********************
Equation rewritten in standard form: Derivative(u(t, x, y), t) + Derivative(u(t, x, y), x) + Derivative(u(t, x, y), y)
Expanded equation: Derivative(u(t, x, y), t) + Derivative(u(t, x, y), x) + Derivative(u(t, x, y), y)
Analyzing term: Derivative(u(t, x, y), t)
Derivative found: Derivative(u(t, x, y), t)
--> Classified as linear
Analyzing term: Derivative(u(t, x, y), x)
Derivative found: Derivative(u(t, x, y), x)
--> Classified as linear
Analyzing term: Derivative(u(t, x, y), y)
Derivative found: Derivative(u(t, x, y), y)
--> Classified as linear
Final linear terms: {Derivative(u(t, x, y), t): 1, Derivative(u(t, x, y), x): 1, Derivative(u(t, x, y), y): 1}
Final nonlinear terms: []
Symbol terms: []
Pseudo terms: []
Source terms: []
*******************************
* Linear operator computation *
*******************************
Characteristic equation before symbol treatment:
ⅈ⋅(kx + ky - ω)
--- Symbolic symbol analysis ---
symb_omega: 0
symb_k: 0
Raw characteristic equation:
ⅈ⋅(kx + ky - ω)
Temporal order from dispersion relation: 1
self.pseudo_terms = []
--- Solutions found ---
[kx + ky]
--- Final linear operator ---
-ⅈ⋅(kx + ky)
*****************
* CFL condition *
*****************
CFL condition violated: dt = 0.0015, max allowed dt = 3.035639631116778e-05
********************
* Symbol condition *
********************
✅ Spectral stability satisfied: Re(a(k)) ≤ 0
⚠️ Insufficient high-frequency dissipation
✅ Reasonable spectral growth
✔ Symbol analysis completed.
*******************
* Symbol plotting *
*******************
******************* * Solving the PDE * *******************
Closest available time to t_eval=0.0: 0.0 Test error t = 0.0: 1.166e-12
Closest available time to t_eval=0.75: 0.75 Test error t = 0.75: 4.030e-02
Closest available time to t_eval=1.5: 1.5 Test error t = 1.5: 4.030e-02
Closest available time to t_eval=2.25: 2.25 Test error t = 2.25: 4.030e-02
Closest available time to t_eval=3.0: 3.0 Test error t = 3.0: 4.029e-02
In [6]:
# Define the symbols and the unknown function
t, x, y, kx, ky, omega = symbols('t x y kx ky omega')
u_func = Function('u')
u = u_func(t, x, y)
# Define the 2D heat equation
eq = Eq(diff(u, t), diff(u, x, x) + diff(u, y, y))
# Initialize the solver
solver = PDESolver(eq)
Lt=2.0
# Configure the solver with the problem parameters
solver.setup(
Lx=2 * np.pi, Ly=2 * np.pi, # Spatial domain: [0, 2π] × [0, 2π]
Nx=512, Ny=512, # Spatial resolution: 512×512 points
Lt=Lt, Nt=100, # Total time: 1.0, number of time steps: 100
initial_condition=lambda x, y: np.sin(x) * np.sin(y), # Initial condition
n_frames=50
)
# Solve the equation
solver.solve()
# Exact solution at t = 1
def u_exact(x, y, t):
"""
Analytical solution of the 2D Poisson equation.
The solution is given by u(x, y, t) = sin(x) * sin(y) * exp(-2t).
"""
return np.sin(x) * np.sin(y) * np.exp(-2.0 * t)
# Automatic testing
n_test = 4
test_set = [solver.test(u_exact=u_exact, t_eval=i * Lt / n_test, threshold=5e-2, component='real') for i in range(n_test + 1)]
# Animate the solution
ani = solver.animate(overlay='contour')
HTML(ani.to_jshtml())
*********************************
* Partial differential equation *
*********************************
2 2
∂ ∂ ∂
──(u(t, x, y)) = ───(u(t, x, y)) + ───(u(t, x, y))
∂t 2 2
∂x ∂y
********************
* Equation parsing *
********************
Equation rewritten in standard form: Derivative(u(t, x, y), t) - Derivative(u(t, x, y), (x, 2)) - Derivative(u(t, x, y), (y, 2))
Expanded equation: Derivative(u(t, x, y), t) - Derivative(u(t, x, y), (x, 2)) - Derivative(u(t, x, y), (y, 2))
Analyzing term: Derivative(u(t, x, y), t)
Derivative found: Derivative(u(t, x, y), t)
--> Classified as linear
Analyzing term: -Derivative(u(t, x, y), (x, 2))
Derivative found: Derivative(u(t, x, y), (x, 2))
--> Classified as linear
Analyzing term: -Derivative(u(t, x, y), (y, 2))
Derivative found: Derivative(u(t, x, y), (y, 2))
--> Classified as linear
Final linear terms: {Derivative(u(t, x, y), t): 1, Derivative(u(t, x, y), (x, 2)): -1, Derivative(u(t, x, y), (y, 2)): -1}
Final nonlinear terms: []
Symbol terms: []
Pseudo terms: []
Source terms: []
*******************************
* Linear operator computation *
*******************************
Characteristic equation before symbol treatment:
2 2
kx + ky - ⅈ⋅ω
--- Symbolic symbol analysis ---
symb_omega: 0
symb_k: 0
Raw characteristic equation:
2 2
kx + ky - ⅈ⋅ω
Temporal order from dispersion relation: 1
self.pseudo_terms = []
--- Solutions found ---
⎡ ⎛ 2 2⎞⎤
⎣ⅈ⋅⎝- kx - ky ⎠⎦
--- Final linear operator ---
2 2
- kx - ky
*****************
* CFL condition *
*****************
********************
* Symbol condition *
********************
✅ Spectral stability satisfied: Re(a(k)) ≤ 0
✅ Proper high-frequency dissipation
✅ Reasonable spectral growth
✔ Symbol analysis completed.
*******************
* Symbol plotting *
*******************
******************* * Solving the PDE * *******************
Closest available time to t_eval=0.0: 0.0 Test error t = 0.0: 3.068e-03
Closest available time to t_eval=0.5: 0.48 Test error t = 0.48: 4.088e-02
Closest available time to t_eval=1.0: 1.0 Test error t = 1.0: 4.081e-02
Closest available time to t_eval=1.5: 1.48 Test error t = 1.48: 4.075e-02
Closest available time to t_eval=2.0: 2.0 Test error t = 2.0: 4.069e-02
********************* * Solution plotting * *********************
Out[6]: