Numerical libraries for PDEs#
Give n the enormous complexity that PDE can present, there are scientific libraries targeted to their solutions. Here we will show just a few to highlight their use. In particular, two libraries that use the powerful finite element method to solve PDE in complex geometries, will be shown at the end.
findiff#
https://github.com/maroba/findiff
uv pip install findiff
%matplotlib inline
#!/usr/bin/env python3
from findiff import Diff, Id, PDE, FinDiff, BoundaryConditions
import numpy as np
shape = (100, 100)
x, y = np.linspace(0, 1, shape[0]), np.linspace(0, 1, shape[1])
dx, dy = x[1]-x[0], y[1]-y[0]
X, Y = np.meshgrid(x, y, indexing='ij')
L = FinDiff(0, dx, 2) + FinDiff(1, dy, 2)
f = np.zeros(shape)
bc = BoundaryConditions(shape)
bc[1,:] = FinDiff(0, dx, 1), 0 # Neumann BC
bc[-1,:] = 300. - 200*Y # Dirichlet BC
bc[:, 0] = 300. # Dirichlet BC
bc[1:-1, -1] = FinDiff(1, dy, 1), 0 # Neumann BC
pde = PDE(L, f, bc)
u = pde.solve()
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
Cell In[1], line 4
1 get_ipython().run_line_magic('matplotlib', 'inline')
2 #!/usr/bin/env python3
----> 4 from findiff import Diff, Id, PDE, FinDiff, BoundaryConditions
5 import numpy as np
8 shape = (100, 100)
ModuleNotFoundError: No module named 'findiff'
import matplotlib.pyplot as plt
plt.imshow(u)
Py-pde#
uv pip install py-pde
import pde
grid = pde.UnitGrid([64, 64]) # generate grid
state = pde.ScalarField.random_uniform(grid) # generate initial condition
eq = pde.DiffusionPDE(diffusivity=0.1) # define the pde
result = eq.solve(state, t_range=10) # solve the pde
result.plot() # plot the resulting field
import numpy as np
from pde import CartesianGrid, solve_laplace_equation
grid = CartesianGrid([[0, 2 * np.pi], [0, 2 * np.pi]], 64)
bcs = {"x": {"value": "sin(y)"}, "y": {"value": "sin(x)"}}
res = solve_laplace_equation(grid, bcs)
res.plot()
import pde
grid = pde.UnitGrid([32, 32]) # generate grid
state = pde.ScalarField.random_uniform(grid) # generate initial condition
storage = pde.MemoryStorage()
trackers = [
"progress", # show progress bar during simulation
"steady_state", # abort when steady state is reached
storage.tracker(interrupts=1), # store data every simulation time unit
pde.PlotTracker(show=True), # show images during simulation
# print some output every 5 real seconds:
pde.PrintTracker(interrupts=pde.RealtimeInterrupts(duration=5)),
]
eq = pde.DiffusionPDE(0.1) # define the PDE
eq.solve(state, 3, dt=0.1, tracker=trackers)
for field in storage:
print(field.integral)
MFEM#
Although it is c++, it has a python wrapper
https://mfem.org/
https://github.com/mfem/PyMFEM
https://mfem.org/examples/
https://colab.research.google.com/github/mfem/pymfem/blob/master/examples/jupyter/ex1.ipynb
https://colab.research.google.com/github/mfem/pymfem/blob/master/examples/jupyter/ex9.ipynb
https://github.com/mfem/PyMFEM/tree/master/examples
Only works on linux
uv pip install pip
pip install mfem # be sure to be used the uv installed pip
(for advanced options, like parallelization, you should download the sources and compile with the right options)
import mfem.ser as mfem
# Create a square mesh
mesh = mfem.Mesh(10, 10, "TRIANGLE")
# Define the finite element function space
fec = mfem.H1_FECollection(1, mesh.Dimension()) # H1 order=1
fespace = mfem.FiniteElementSpace(mesh, fec)
# Define the essential dofs
ess_tdof_list = mfem.intArray()
ess_bdr = mfem.intArray([1]*mesh.bdr_attributes.Size())
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list)
# Define constants for alpha (diffusion coefficient) and f (RHS)
alpha = mfem.ConstantCoefficient(1.0)
rhs = mfem.ConstantCoefficient(1.0)
"""
Note
-----
In order to represent a variable diffusion coefficient, you
must use a numba-JIT compiled function. For example:
>>> @mfem.jit.scalar
>>> def alpha(x):
>>> return x+1.0
"""
# Define the bilinear and linear operators
a = mfem.BilinearForm(fespace)
a.AddDomainIntegrator(mfem.DiffusionIntegrator(alpha))
a.Assemble()
b = mfem.LinearForm(fespace)
b.AddDomainIntegrator(mfem.DomainLFIntegrator(rhs))
b.Assemble()
# Initialize a gridfunction to store the solution vector
x = mfem.GridFunction(fespace)
x.Assign(0.0)
# Form the linear system of equations (AX=B)
A = mfem.OperatorPtr()
B = mfem.Vector()
X = mfem.Vector()
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B)
print("Size of linear system: " + str(A.Height()))
# Solve the linear system using PCG and store the solution in x
AA = mfem.OperatorHandle2SparseMatrix(A)
M = mfem.GSSmoother(AA)
mfem.PCG(AA, M, B, X, 1, 200, 1e-12, 0.0)
a.RecoverFEMSolution(X, b, x)
# Extract vertices and solution as numpy arrays
verts = mesh.GetVertexArray()
sol = x.GetDataArray()
# Plot the solution using matplotlib
import matplotlib.pyplot as plt
import matplotlib.tri as tri
triang = tri.Triangulation(verts[:,0], verts[:,1])
fig, ax = plt.subplots()
ax.set_aspect('equal')
tpc = ax.tripcolor(triang, sol, shading='gouraud')
fig.colorbar(tpc)
plt.show()
Fenics#
https://fenicsproject.org/
https://fenicsproject.org/documentation/
https://docs.fenicsproject.org/dolfinx/v0.9.0/python/demos.html
https://jsdokken.com/dolfinx-tutorial/chapter1/fundamentals.html
https://docs.pyvista.org/
Installation is much more complicated than
uv pip install fenics dolphin mpi4py
It is better to use a docker container or use the binder setups
Example: 2D poisson equation
Notation and method intro: https://jsdokken.com/dolfinx-tutorial/chapter1/fundamentals.html
Implementation (use binder) : https://jsdokken.com/dolfinx-tutorial/chapter1/fundamentals_code.html
From : https://jsdokken.com/dolfinx-tutorial/chapter2/diffusion_code.html
To Check#
https://getfem-examples.readthedocs.io/en/latest/demo_unit_disk.html
https://www.pygimli.org/_tutorials_auto/2_modelling/plot_2-mod-fem.html
https://flocode.substack.com/p/049-pynite-01-introduction-to-finite
https://bleyerj.github.io/comet-fenicsx/
https://gmshmodel.readthedocs.io/en/latest/