Skip to content

Having problems computing PDE residuals for The_Well datasets (e.g., shear_flow) #62

@LeslisXu

Description

@LeslisXu

Hi all — I'm computing PDE residuals for The_Well datasets (e.g. turbulent_radiative_layer_2D and shear_flow) using finite differences, but the residuals are much larger than I expect. The data are generated by simulations, so I expected residuals close to zero.

What I do

  1. I compute derivatives with a central difference scheme (second-order in the interior, one-sided at boundaries). Here is my derivative function:

     import torch
    
     def compute_derivative(u, delta, order=1, axis=1):
         """
         Compute numerical derivatives along a specified axis.
    
         Args:
             u: Input tensor with shape [B, T, X, Y] (or [B, T, X, Y, C] for vector fields).
             delta: grid spacing along the chosen axis.
             order: 1 for first derivative, 2 for second derivative.
             axis: 1=time, 2=x, 3=y.
         """
         derivative = torch.zeros_like(u)
    
         if order == 1:
             # second-order central differences in interior, one-sided at boundaries
             if axis == 1:  # time
                 derivative[:, 1:-1, :, :] = (u[:, 2:, :, :] - u[:, :-2, :, :]) / (2.0 * delta)
                 derivative[:, 0:1, :, :]  = (-3.0*u[:, 0:1, :, :] + 4.0*u[:, 1:2, :, :] - u[:, 2:3, :, :]) / (2.0 * delta)
                 derivative[:, -1:, :, :]  = (3.0*u[:, -1:, :, :] - 4.0*u[:, -2:-1, :, :] + u[:, -3:-2, :, :]) / (2.0 * delta)
             elif axis == 2:  # x
                 derivative[:, :, 1:-1, :] = (u[:, :, 2:, :] - u[:, :, :-2, :]) / (2.0 * delta)
                 derivative[:, :, 0:1, :]  = (-3.0*u[:, :, 0:1, :] + 4.0*u[:, :, 1:2, :] - u[:, :, 2:3, :]) / (2.0 * delta)
                 derivative[:, :, -1:, :]  = (3.0*u[:, :, -1:, :] - 4.0*u[:, :, -2:-1, :] + u[:, :, -3:-2, :]) / (2.0 * delta)
             elif axis == 3:  # y
                 derivative[:, :, :, 1:-1] = (u[:, :, :, 2:] - u[:, :, :, :-2]) / (2.0 * delta)
                 derivative[:, :, :, 0:1]  = (-3.0*u[:, :, :, 0:1] + 4.0*u[:, :, :, 1:2] - u[:, :, :, 2:3]) / (2.0 * delta)
                 derivative[:, :, :, -1:]  = (3.0*u[:, :, :, -1:] - 4.0*u[:, :, :, -2:-1] + u[:, :, :, -3:-2]) / (2.0 * delta)
    
         elif order == 2:
             # second-order second derivative
             if axis == 1:
                 derivative[:, 1:-1, :, :] = (u[:, 2:, :, :] - 2*u[:, 1:-1, :, :] + u[:, :-2, :, :]) / (delta**2)
                 derivative[:, 0:1, :, :]  = (u[:, 2:3, :, :] - 2*u[:, 1:2, :, :] + u[:, 0:1, :, :]) / (delta**2)
                 derivative[:, -1:, :, :]  = (u[:, -1:, :, :] - 2*u[:, -2:-1, :, :] + u[:, -3:-2, :, :]) / (delta**2)
             elif axis == 2:
                 derivative[:, :, 1:-1, :] = (u[:, :, 2:, :] - 2*u[:, :, 1:-1, :] + u[:, :, :-2, :]) / (delta**2)
                 derivative[:, :, 0:1, :]  = (u[:, :, 2:3, :] - 2*u[:, :, 1:2, :] + u[:, :, 0:1, :]) / (delta**2)
                 derivative[:, :, -1:, :]  = (u[:, :, -1:, :] - 2*u[:, :, -2:-1, :] + u[:, :, -3:-2, :]) / (delta**2)
             elif axis == 3:
                 derivative[:, :, :, 1:-1] = (u[:, :, :, 2:] - 2*u[:, :, :, 1:-1] + u[:, :, :, :-2]) / (delta**2)
                 derivative[:, :, :, 0:1]  = (u[:, :, :, 2:3] - 2*u[:, :, :, 1:2] + u[:, :, :, 0:1]) / (delta**2)
                 derivative[:, :, :, -1:]  = (u[:, :, :, -1:] - 2*u[:, :, :, -2:-1] + u[:, :, :, -3:-2]) / (delta**2)
    
         return derivative
    
  2. From the HDF5 file I load fields with shapes:

     pressure: (4, 200, 256, 512)
     tracer:   (4, 200, 256, 512)
     velocity: (4, 200, 256, 512, 2)
    

    I call compute_physics_loss(...) with velocity[...,0] and velocity[...,1] as velocity_x and velocity_y.

  3. The PDEs I use:

    Momentum (component form)

    $$ \partial_t \mathbf{u} + \nabla p - \nu \Delta \mathbf{u} = -\mathbf{u}\cdot\nabla\mathbf{u}. $$

    Tracer

    $$ \partial_t s - D\Delta s = -\mathbf{u}\cdot\nabla s, $$

    where ν = 1/Reynolds and D = ν/Schmidt.

    and the PDE terms are calculated using

     # time derivatives (one-order)
     du_x_dt = compute_derivative(velocity_x, dt, order=1, axis=1)
     du_y_dt = compute_derivative(velocity_y, dt, order=1, axis=1)
     ds_dt = compute_derivative(tracer, dt, order=1, axis=1)
     
     # spatial derivatives
     # pressure gradients (one-order)
     dp_dx = compute_derivative(pressure, dx, order=1, axis=2)
     dp_dy = compute_derivative(pressure, dy, order=1, axis=3)
    
     # velocity Laplacian (second-order)
     du_x_dx2 = compute_derivative(velocity_x, dx, order=2, axis=2)
     du_x_dy2 = compute_derivative(velocity_x, dy, order=2, axis=3)
     laplacian_u_x = du_x_dx2 + du_x_dy2
     
     du_y_dx2 = compute_derivative(velocity_y, dx, order=2, axis=2)
     du_y_dy2 = compute_derivative(velocity_y, dy, order=2, axis=3)
     laplacian_u_y = du_y_dx2 + du_y_dy2
     
     # tracer Laplacian (second-order)
     ds_dx2 = compute_derivative(tracer, dx, order=2, axis=2)
     ds_dy2 = compute_derivative(tracer, dy, order=2, axis=3)
     laplacian_s = ds_dx2 + ds_dy2
     
     # convection term (u·∇u)
     du_x_dx = compute_derivative(velocity_x, dx, order=1, axis=2)
     du_x_dy = compute_derivative(velocity_x, dy, order=1, axis=3)
     advection_u_x = velocity_x * du_x_dx + velocity_y * du_x_dy
     
     du_y_dx = compute_derivative(velocity_y, dx, order=1, axis=2)
     du_y_dy = compute_derivative(velocity_y, dy, order=1, axis=3)
     advection_u_y = velocity_x * du_y_dx + velocity_y * du_y_dy
    
     # convection term (u·∇s)
     ds_dx = compute_derivative(tracer, dx, order=1, axis=2)
     ds_dy = compute_derivative(tracer, dy, order=1, axis=3)
     advection_s = velocity_x * ds_dx + velocity_y * ds_dy
    
  4. I compute residual tensors (shape [B, T, X, Y]) as:

     momentum_x_residual = du_x_dt + dp_dx - nu * laplacian_u_x + advection_u_x
     momentum_y_residual = du_y_dt + dp_dy - nu * laplacian_u_y + advection_u_y
     tracer_residual     = ds_dt - D * laplacian_s + advection_s
    
  5. I then compute the mean squared residuals via:

     res_mom_x = ((momentum_x_residual)**2).mean().item()
     res_mom_y = ((momentum_y_residual)**2).mean().item()
     res_tracer = ((tracer_residual)**2).mean().item()
    

    (Yes, that expression is the mean squared residual / MSE.)

Observed output

The momentum equation residual norms: 0.18399687111377716, 0.1050143912434578
The tracer equation residual norm: 2.9967291355133057

I expected values near zero but get values ~0.1–3.0.

What I checked so far

  • data shapes and indexing
  • derivative implementation (central diff interior + one-sided boundary)

Questions / help requested

  • Am I missing any common pitfalls that could cause such large residuals?
  • Could the dataset use different conventions (e.g. staggered grid, pressure vs velocity staggering, units/scaling, periodic BCs, ghost cells, or coordinate transforms) that I should account for?
  • Are there mistakes in my finite-difference discretization or sign conventions for the PDE terms?
  • Are there recommended sanity checks I should run to localize the issue?

Relative Information

dt=0.09999999999999999, dx=0.003921568859368563, dy=0.001956947147846222, Reynolds=500000.0, Schmidt=0.20000000298023224

Thanks in advance for any pointers!!!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions