zwicker-group / py-pde

Python package for solving partial differential equations using finite differences.

Home Page:https://py-pde.readthedocs.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Numerical Stability Issues

Benj-Ward opened this issue · comments

AssertionError: 
Not equal to tolerance rtol=1e-07, atol=1e-07
The numba compiled implementation of the right hand side is not compatible with the numpy implementation. This check can be disabled by setting the class attribute `check_implementation` to `False`.
Mismatched elements: 32 / 512 (6.25%)
Max absolute difference: 1626.21024656
Max relative difference: 0.99993583
 x: array([[ 2.723976e-01,  2.359749e-01,  1.897466e-01,  1.486356e-01,
         1.166606e-01,  9.313345e-02,  7.616062e-02,  6.394502e-02,
         5.510184e-02,  4.864081e-02,  4.387244e-02,  4.031899e-02,...
 y: array([[ 1.343445e+01,  2.359749e-01,  1.897466e-01,  1.486356e-01,
         1.166606e-01,  9.313345e-02,  7.616062e-02,  6.394502e-02,
         5.510184e-02,  4.864081e-02,  4.387244e-02,  4.031899e-02,...

I am getting the above error from the following code. I suspect the numerical error is originating in the interaction of my boundary conditions and terms one and two of my pde. The code works if only term term three is given to the pde. I have tried removing zeros of all kinds from the code by making the bounds_z = [1,32]. I've set my gradient boundary conditions to non-zero values (.01). I would like to try what the error suggests and set check_implementation to False, but I don't know how to do that and would appreciate guidance. Perhaps I have a syntax error, or there is a bug in the package. I would appreciate any guidance and will try to be helpful. I also tried replacing the spatially dependent term {cells} with my solving variable u, which caused a similar error although Mismatched elements was 496/512, Max absolute difference was 1.37332679e-06, and Max relative difference was 0.37903554. So, I got more numerically unstable elements, but each was less severe, I think.

from pde import CylindricalSymGrid, ScalarField, PDE, CartesianGrid, MemoryStorage, plot_kymograph

grid = pde.CylindricalSymGrid(radius = 16,bounds_z = [0,32],shape = [16,32], periodic_z=False)
state = ScalarField(grid, result.data)
#implement an initial guess for initial scalar field. Use final state of above simulation.

trackers = [
    "progress",  # show progress bar during simulation
    "steady_state", #,  # abort when steady state is reached
    #storage.tracker(interval=10),  # store data every simulation time unit
    #pde.PlotTracker(show=True),  # show images during simulation
    #print(some output every 5 real seconds:
    #pde.PrintTracker(interval=pde.RealtimeInterrupts(duration=1))),
]

# Define Boundary conditions, value = dirichlet, derivative = neumann
CONST = np.zeros(16)
value = np.array([])
for i in range(0,4):
    value = np.append(value,[10**9])
for i in range(0,12):
    value = np.append(value,[0])

bc_z = [{'type': 'mixed', 'value': value, 'const': CONST},{"value": 1}]
bc_r = {"derivative": 0}

cells = f"2 *z"
term_1 = f"dot(u,laplace({cells}))"
term_2 = f"dot(gradient({cells}), gradient(u))"
term_3 = f"laplace(u)"

eq = pde.PDE({"u": f"{term_1} + {term_2} + {term_3}"}, bc= [bc_r,bc_z])   # Define PDE 

result = eq.solve(state, 10**9, dt=0.01, tracker=trackers)
result.plot()

This indeed looks like an internal error. Could you please provide a self-contained example, so I can reproduce the error on my side? Currently, result.data is not defined on line 4.

Ah yes result.data is a best guess derived from a simpler form of the PDE. Replacing it with a random scalar field produces a very similar error likely caused by the same problem.

Not equal to tolerance rtol=1e-07, atol=1e-07
The numba compiled implementation of the right hand side is not compatible with the numpy implementation. This check can be disabled by setting the class attribute `check_implementation` to `False`.
Mismatched elements: 32 / 512 (6.25%)
Max absolute difference: 409.1006068
Max relative difference: 1.04468402
 x: array([[ 9.960797e-02,  7.314522e-02,  1.020577e-04, -2.053950e-02,
         2.272106e-01,  4.124929e-02, -5.837862e-02,  7.207774e-02,
        -3.943749e-02,  8.503645e-02,  6.884458e-02,  6.635742e-02,...
 y: array([[ 5.836001e+00,  7.314522e-02,  1.020577e-04, -2.053950e-02,
         2.272106e-01,  4.124929e-02, -5.837862e-02,  7.207774e-02,
        -3.943749e-02,  8.503645e-02,  6.884458e-02,  6.635742e-02,...
import pde 
import numpy as np
import matplotlib.pylab as plt
from pde import CylindricalSymGrid, ScalarField, PDE, CartesianGrid, MemoryStorage, plot_kymograph

grid = pde.CylindricalSymGrid(radius = 16,bounds_z = [0,32],shape = [16,32], periodic_z=False)
state = ScalarField.random_uniform(grid, 0.2, 0.3)
#implement an initial guess for initial scalar field. Use final state of above simulation.

trackers = [
    "progress",  # show progress bar during simulation
    "steady_state", #,  # abort when steady state is reached
    #storage.tracker(interval=10),  # store data every simulation time unit
    #pde.PlotTracker(show=True),  # show images during simulation
    #print(some output every 5 real seconds:
    #pde.PrintTracker(interval=pde.RealtimeInterrupts(duration=1))),
]

# Define Boundary conditions, value = dirichlet, derivative = neumann
CONST = np.zeros(16)
value = np.array([])
for i in range(0,4):
    value = np.append(value,[10**9])
for i in range(0,12):
    value = np.append(value,[0])

bc_z = [{'type': 'mixed', 'value': value, 'const': CONST},{"value": 1}]
bc_r = {"derivative": 0}

cells = "2 * z"
term_1 = f"dot(u,laplace({cells}))"
term_2 = f"dot(gradient({cells}), gradient(u))"
term_3 = f"laplace(u)"

eq = pde.PDE({"u": f"{term_1} + {term_2} + {term_3}"}, bc= [bc_r,bc_z])   # Define PDE

result = eq.solve(state, 10**0, dt=0.01, tracker=trackers)
result.plot()

Thank you for providing a full example. The error was a bit subtle and had to do with the concrete implementations of the dot operator: The error was caused by your definition of term_1, which wants to calculate a dot product between two scalar fields, which is not supported (but did not throw an error!). I just updated the package to throw an error for this case. You should be able to fix your code by using term_1 = f"u * laplace({cells})", which I assume is what you wanted to do.