meshpro / meshgen-comparison

:spider_web: A comparison of mesh generators.
GNU General Public License v3.0
40 stars 8 forks source link

spectral radius for scalar wave equation #9

Open krober10nd opened 3 years ago

krober10nd commented 3 years ago

import firedrake as fd from firedrake import dot, grad import finat

def estimate_timestep(mesh, V, c, estimate_max_eigenvalue=True): """Estimate the maximum stable timestep based on the spectral radius using optionally the Gershgorin Circle Theorem to estimate the maximum generalized eigenvalue. Otherwise computes the maximum generalized eigenvalue exactly Currently only works with KMV elements. """

u, v = fd.TrialFunction(V), fd.TestFunction(V)
quad_rule = finat.quadrature.make_quadrature(
    V.finat_element.cell, V.ufl_element().degree(), "KMV"
)
dxlump = fd.dx(rule=quad_rule)
A = fd.assemble(u * v * dxlump)
ai, aj, av = A.petscmat.getValuesCSR()
av_inv = []
for value in av:
    if value == 0:
        av_inv.append(0.0)
    else:
        av_inv.append(1 / value)
Asp = scipy.sparse.csr_matrix((av, aj, ai))
Asp_inv = scipy.sparse.csr_matrix((av_inv, aj, ai))

K = fd.assemble(c*c*dot(grad(u), grad(v)) * dxlump)
ai, aj, av = K.petscmat.getValuesCSR()
Ksp = scipy.sparse.csr_matrix((av, aj, ai))

# operator
Lsp = Asp_inv.multiply(Ksp)
if estimate_max_eigenvalue:
    # absolute maximum of diagonals
    max_eigval = np.amax(np.abs(Lsp.diagonal()))
else:
    print(
        "Computing exact eigenvalues is extremely computationally demanding!",
        flush=True,
    )
    max_eigval = scipy.sparse.linalg.eigs(
        Ksp, M=Asp, k=1, which="LM", return_eigenvectors=False
    )[0]

# print(max_eigval)
max_dt = np.float(2 / np.sqrt(max_eigval))
print(
    f"Maximum stable timestep should be about: {np.float(2 / np.sqrt(max_eigval))} seconds",
    flush=True,
)
return max_dt
krober10nd commented 3 years ago

Re inputs: mesh is a Firedrake.Mesh, V is a Firedrake.FunctionSpace, and c is a Function(V) with spatially varying scalar wavespeeds.

nschloe commented 3 years ago

Oh man, I'd love to see a simple Python FEM package without C++ (beyond those in numpy/scipy). Anyway, this seems a little too specific with all the input argument. You'd want to pin one single value for each mesh. Looking at the spectral radius is actually a good idea, I'll add that for the Laplacian.

krober10nd commented 3 years ago

Oh man, I'd love to see a simple Python FEM package without C++ (beyond those in numpy/scipy)

Yea, I think scikit-fem strives for some of that. I can't tell you how much time I've spent (wasted?) trying to install Firedrake on different machines.

I agree it's a little too specific but the spectral radius in general is useful as it incorporates both the discretization (order and such) and the mesh quality at the same time.

For my application the difference between a minimum dihedral angle bound of 1 degree and 18 degrees means a variation in maximum stable timestep between 0.0001 and 0.0012 seconds. To get that high of a dihedral angle bound, I typically need to iterate with DistMesh 300-500 iterations (depending on the minimum element size).

krober10nd commented 3 years ago

Here's the result.

Overthrust_stable_timestep