Hi there,
I am trying to solve the in-homogeneous Helmholtz equation with damping and forcing. The equation is;
c^2\nabla^2p - c^2\omega i d \nabla^2p + \omega^2 p = f
where the d is the damping parameter, the i is complex number the f is the forcing, \omega is the eigenvalue and the p is the eigenvector. In matrix form;
(\mathbf{A} + \omega \mathbf{B} + \omega^2 \mathbf{C})\mathbf{p} = \mathbf{D}\mathbf{p}
where \mathbf{B} is the damping matrix and \mathbf{D} is the forcing matrix. For 1D case, the boundary conditions are Neumann at both ends.
I am aiming to damp the system with \mathbf{B} and did try 4 different cases;
- \mathbf{A} + \omega^2 \mathbf{C} = 0 (no damping and no forcing)
- \mathbf{A} + \omega \mathbf{B} + \omega^2 \mathbf{C} = 0 (damping and no forcing)
- (\mathbf{A}-\mathbf{D}) + \omega^2 \mathbf{C} = 0 (no damping and forcing)
- (\mathbf{A}-\mathbf{D}) + \omega \mathbf{B} + \omega^2 \mathbf{C} = 0 (damping and forcing)
When I run this cases, I get very weird behaviour. Here is the list of the eigenvalues for the cases;
- \mathbf{A} + \omega^2 \mathbf{C} = 0 gives \omega=1069.2399-0j
- \mathbf{A} + \omega \mathbf{B} + \omega^2 \mathbf{C} = 0 gives \omega=1139.6967-0.2463j
- (\mathbf{A}-\mathbf{D}) + \omega^2 \mathbf{C} = 0 gives \omega=1074.748+3.2431j
- (\mathbf{A}-\mathbf{D}) + \omega \mathbf{B} + \omega^2 \mathbf{C} = 0 gives \omega=1146.0758+3.5129j
The complex part of the eigenvalue is representing the growth rate of the system. As I impose a damping, the last case’s complex part (+3.5129j) should be lower than the 3. case’s complex part (3.2431j) according to the law of superposition but it is not. Here is the 1D MWE;
from dolfinx.mesh import meshtags,locate_entities,create_unit_interval
from dolfinx.fem import FunctionSpace, form, Constant
from dolfinx.fem.petsc import assemble_matrix
from ufl import Measure, TestFunction, TrialFunction, grad, inner
from petsc4py import PETSc
from slepc4py import SLEPc
from mpi4py import MPI
import numpy as np
degree = 1
mesh = create_unit_interval(MPI.COMM_WORLD, 20)
L, x_h = 0.1, 0.7
def subdomain_func(x, x_f=x_h, a_f=L/2, eps=1e-16):
x = x[0]
return np.logical_and(x_f - a_f - eps <= x, x <= x_f + a_f + eps)
marked_cells = locate_entities(mesh, mesh.topology.dim, subdomain_func)
forcing_tag = 2
subdomains = meshtags(mesh, mesh.topology.dim, marked_cells, np.full(len(marked_cells), forcing_tag, dtype=np.int32))
dx = Measure('dx', domain=mesh, subdomain_data=subdomains)
V = FunctionSpace(mesh, ("Lagrange", degree))
u, v = TrialFunction(V), TestFunction(V)
c = Constant(mesh, PETSc.ScalarType(340)) #speed of sound
a_form = form(-c**2* inner(grad(u), grad(v))*dx)
A = assemble_matrix(a_form)
A.assemble()
d_damp = 0.25
b_form = form(c**2 * 1j*d_damp*inner(grad(u),grad(v))*dx(2))
B = assemble_matrix(b_form)
B.assemble()
c_form = form(inner(u , v) * dx)
C = assemble_matrix(c_form)
C.assemble()
rows = [ 0, 0, 0, 4, 8, 12, 16, 20, 24, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28]
cols = [2, 3, 5, 6, 2, 3, 5, 6, 2, 3, 5, 6, 2, 3, 5, 6, 2, 3, 5, 6, 2, 3,
5, 6, 2, 3, 5, 6]
vals = [ 2.11898752e-01+1.22975798e-01j, 1.56520670e+00+9.08370344e-01j,
-1.56520670e+00-9.08370344e-01j, -2.11898752e-01-1.22975798e-01j,
8.63179873e+01+5.00947893e+01j, 6.37594561e+02+3.70029077e+02j,
-6.37594561e+02-3.70029077e+02j, -8.63179873e+01-5.00947893e+01j,
9.73640596e+02+5.65053959e+02j, 7.19187238e+03+4.17381524e+03j,
-7.19187238e+03-4.17381524e+03j, -9.73640596e+02-5.65053959e+02j,
2.69712743e+03+1.56528245e+03j, 1.99225426e+04+1.15620812e+04j,
-1.99225426e+04-1.15620812e+04j, -2.69712743e+03-1.56528245e+03j,
9.73640596e+02+5.65053959e+02j, 7.19187238e+03+4.17381524e+03j,
-7.19187238e+03-4.17381524e+03j, -9.73640596e+02-5.65053959e+02j,
8.63179873e+01+5.00947893e+01j, 6.37594561e+02+3.70029077e+02j,
-6.37594561e+02-3.70029077e+02j, -8.63179873e+01-5.00947893e+01j,
2.11898752e-01+1.22975798e-01j, 1.56520670e+00+9.08370344e-01j,
-1.56520670e+00-9.08370344e-01j, -2.11898752e-01-1.22975798e-01j]
D = PETSc.Mat().create(PETSc.COMM_WORLD)
D.setSizes([(V.dofmap.index_map.size_local, V.dofmap.index_map.size_global), (V.dofmap.index_map.size_local, V.dofmap.index_map.size_global)])
D.setType('mpiaij')
D.setUp()
D.setValuesCSR(rows, cols, vals, addv=PETSc.InsertMode.ADD_VALUES)
D.assemblyBegin()
D.assemblyEnd()
def eps_solver(A, C, target, nev):
# This function solves A + (w^2) C = 0
E = SLEPc.EPS().create(MPI.COMM_WORLD)
C = - C
E.setOperators(A, C)
st = E.getST()
st.setType('sinvert')
E.setTarget(target)
E.setWhichEigenpairs(SLEPc.EPS.Which.TARGET_MAGNITUDE)
E.setDimensions(nev, SLEPc.DECIDE)
E.setTolerances(1e-15)
E.setFromOptions()
E.solve()
return E
def pep_solver(A, B, C, target, nev):
# This function solves A + wB + w^2 C = 0
Q = SLEPc.PEP().create(MPI.COMM_WORLD)
operators = [A, B, C]
Q.setOperators(operators)
st = Q.getST()
st.setType('sinvert')
Q.setTarget(target)
Q.setWhichEigenpairs(SLEPc.PEP.Which.TARGET_MAGNITUDE)
Q.setDimensions(nev, SLEPc.DECIDE)
Q.setTolerances(1e-15)
Q.setFromOptions()
Q.solve()
return Q
target = 200 * 2 * np.pi
E = eps_solver(A, C, target**2, 2)
vr, vi = A.getVecs()
eig = np.sqrt(E.getEigenvalue(0))
print("Eigenvalue without damping without forcing: ", eig)
E2 = pep_solver(A, B, C, target, 2)
vr, vi = A.getVecs()
eig2 = E2.getEigenpair(0, vr, vi)
print("Eigenvalue with damping: ", eig2)
D_Mat = A - D
E3 = eps_solver(D_Mat, C, target**2, 2)
vr, vi = A.getVecs()
eig3 = np.sqrt(E3.getEigenvalue(0))
print("Eigenvalue with forcing: ", eig3)
E4 = pep_solver(D_Mat, B, C, target, 2)
vr, vi = A.getVecs()
eig4 = E4.getEigenpair(0, vr, vi)
print("Eigenvalue with damping and forcing: ", eig4)
gives;
Eigenvalue without damping without forcing: (1069.2399772685112-1.373743027646997e-14j)
Eigenvalue with damping: (1139.6967684690483-0.24634561557213236j)
Eigenvalue with forcing: (1074.7488345843487+3.2431003471216204j)
Eigenvalue with damping and forcing: (1146.075830311377+3.512980597062334j)
This results makes me confused and any suggestions would be warmly welcomed. Thank you in advance!