Bug introduced in 13.0 or earlier and fixed in 13.1.0
I have been using the finite element tools in Mathematica version 13.0 to solve eigenvalue problems in physics (Schrödinger equation) using NDEigensystem, but I am having some issues with non-negligible imaginary parts of the eigenvalues.
These appear to be related to the stiffness matrix not being self-adjoint even when the underlying operator is. However, I noted that when I use the inactive form of the differential operators, the stiffness matrix is self-adjoint and the imaginary values vanish. When I use the active form, the stiffness matrix is not self-adjoint and the eigenvalues have a significant imaginary part.
I would like to point out that I am using coefficients that are spatially dependent and that it does seem that the difference between the stiffness matrix S and its conjugate transpose gets larger the more abrupt the change in the coefficients is. Perhaps it is related to how the coefficients are discretized in the two cases (just a thought)?
The reason that I want to understand what's going on is that I seem unable to run NDEigensystem using the inactive form (as provided by the (...)PDETerm functions) when I have a coupled system of equations. I simply get an error message
which does not make sense to me since the problem is certainly both stationary and linear.
Therefore, I would either like to understand how the outputs of the inactive form and active form can be made consistent, or to resolve the issue related to the above error message so that I can use the inactive form. By refining the mesh appropriately, the issue can be resolved to some degree, but not fully and I am not particularly happy with this partial solution.
I have also tried to compile the system step by step using the MM Finite Element programming guide which DOES give a self-adjoint stiffness matrix in the coupled (vector) case (when applying the boundary conditions in an appropriate way). This could be a solution, but I'd much prefer to use NDEigensystem if the issue can be resolved.
I have provided some minimal examples below that I believe capture the problems I am having:
Scalar case This example illustrates the difference between the active and inactive form. Note that the operator is made self-adjoint by the coefficient b being imaginary and the fact that we symmetrize the convection term by adding the conservative convection term as well.
(*Minimal example 1, 1D scalar*) ClearAll["Global'*"]; Needs["NDSolve`FEM`"]; (*Defined a smoothened step-like function to vary parameters in space*) sh[x_, x0_, p1_, p2_, s_] := 1/2*(Tanh[-(x - x0)/s] + 1)*p1 + 1/2*(Tanh[(x - x0)/s] + 1)*p2 mesh = ToElementMesh[Line[{{0}, {1}}], MaxCellMeasure -> 0.001]; c = 1.; b = I; a = 1.; r = 10.; x0 = 0.5; sig = 0.001; diffterm = DiffusionPDETerm[{u[x], {x}}, {sh[x, x0, c, c*r, sig]}]; convecterm = ConvectionPDETerm[{u[x], {x}}, {sh[x, x0, b, b*r, sig]}]; consconvecterm = ConservativeConvectionPDETerm[{u[ x], {x}}, -{sh[x, x0, b, b*r, sig]}]; (*minus sign to account for sign convention of the \ Conservative term*) reacterm = ReactionPDETerm[{u[x], {x}}, sh[x, x0, a, a*r, sig]]; Plot[sh[x, x0, a, a*r, sig], {x, 0, 1}] eq = (diffterm + convecterm + consconvecterm + reacterm); bc = DirichletCondition[{u[x] == 0.}, True]; method = Method -> {"PDEDiscretization" -> {"FiniteElement", "InterpolationOrder" -> { u -> 2}}, "Eigensystem" -> {"Arnoldi", "Shift" -> 0}, "VectorNormalization" -> Function[{values, vectors, stiffness, damping}, s = stiffness; norm = vectors/ Diagonal[ vectors . damping . ConjugateTranspose[vectors]]^(1/2)]}; solution = NDEigensystem[{eq, bc}, u[x], {x} ∈ mesh, 10, method]; solution[[1]] {MatrixPlot[Re[s - ConjugateTranspose@s], PlotLegends -> True], MatrixPlot[Im[s - ConjugateTranspose@s], PlotLegends -> True]} HermitianMatrixQ@s solution = NDEigensystem[{Activate@eq, bc}, u[x], {x} ∈ mesh, 10, method]; solution[[1]] {MatrixPlot[Re[s - ConjugateTranspose@s], PlotLegends -> True], MatrixPlot[Im[s - ConjugateTranspose@s], PlotLegends -> True]} HermitianMatrixQ@s
Vector case (coupled equations) This example shows that the Active form gives similar problems to the 1D scalar case, but now the inactive form throws the aforementioned error message when trying to run NDEigensystem.
(*Minimal example 2, 1D vector*) ClearAll["Global'*"]; \ Needs["NDSolve`FEM`"]; (*Defined a smoothened step-like function to vary parameters in space*) sh[x_, x0_, p1_, p2_, s_] := 1/2*(Tanh[-(x - x0)/s] + 1)*p1 + 1/2*(Tanh[(x - x0)/s] + 1)*p2 mesh = ToElementMesh[Line[{{0}, {1}}], MaxCellMeasure -> 0.001]; c = 1.; b = I; a = 1.; r = 10.; x0 = 0.5; stp = 0.001; u = {u1[x], u2[x]}; diffterm = DiffusionPDETerm[{u, {x}}, {{{sh[x, x0, c, c*r, stp]}, {0}}, {{0}, {sh[x, x0, c, c*r, stp]}}}]; convecterm = ConvectionPDETerm[{u, {x}}, {{{0}, {sh[x, x0, b, b*r, stp]}}, {{sh[ x, x0, b, b*r, stp]}, {0}}}]; reacterm = ReactionPDETerm[{u, {x}}, {{sh[x, x0, a, a*r, stp], 0}, {0, sh[x, x0, a, a*r, stp]}}]; consconvecterm = ConservativeConvectionPDETerm[{u, {x}}, -{{{0}, {sh[x, x0, b, b*r, stp]}}, {{sh[x, x0, b, b*r, stp]}, {0}}}]; eq = (diffterm + convecterm + reacterm + consconvecterm); bc = DirichletCondition[{u1[x] == 0., u2[x] == 0.}, True]; method = Method -> {"PDEDiscretization" -> {"FiniteElement", "InterpolationOrder" -> { u1 -> 2, u2 -> 2}}, "Eigensystem" -> {"Arnoldi", "Shift" -> 0}, "VectorNormalization" -> Function[{values, vectors, stiffness, damping}, s = stiffness; norm = vectors/ Diagonal[ vectors . damping . ConjugateTranspose[vectors]]^(1/2)]}; solution = NDEigensystem[{eq, bc}, u, {x} ∈ mesh, 10, method]; solution = NDEigensystem[{Activate@eq, bc}, u, {x} ∈ mesh, 10, method]; solution[[1]] {MatrixPlot@Re[s], MatrixPlot@Im[s], MatrixPlot[Re[s - ConjugateTranspose@s], PlotLegends -> True], MatrixPlot[Im[s - ConjugateTranspose@s], PlotLegends -> True]} HermitianMatrixQ@s