Complex Calculate using complex solutions of variational formulation

Hi Fenics community,
I am using Fenicsx 0.5 version which installed in November 2022. I get a complex solution function “uh” by variational formulation. It can be printed as follows:

print(‘uh.real=’, np.real(uh.x.array))
print(“uh.imag=”, 1j * np.imag(uh.x.array))

And get:

uh.real= [-0.5831319 -0.60897291 -0.75570672 … -0.44941454 -0.84838181 -0.92178618]
uh.imag= [-0.-0.34321542j 0.+0.23538291j 0.+0.0579351j … 0.+1.0360514j 0.+0.73615986j -0.-0.54553593j]

Then, I use “uh” for further calculation.

marker = mesh.meshtags(msh2, 1, bod2.indices, 2)
ds = ufl.Measure(‘ds’, subdomain_data=marker, domain=msh2) # get boundary

f1 = ufl.algebra.Power((x[0]*1j+x[1]) / r, int(order1[i]))

it = fem.assemble_scalar(fem.form(uh * f1 * ds(2))) / (r * k) # kind 1
it = fem.assemble_scalar(fem.form(np.real(uh)* f1 * ds(2))) / (r * k) # kind 2
it = fem.assemble_scalar(fem.form(np.imag(uh)* f1 * ds(2))) / (r * k) # kind 3
it = fem.assemble_scalar(fem.form((1j * np.imag(uh)) * f1 * ds(2))) / (r * k) # kind 4
it = fem.assemble_scalar(fem.form((np.real(uh) + 1j * np.imag(uh)) * f1 * ds(2))) / (r * k) # kind 5

The final figure of kind 1, 2 and 5 look the same. The kind 3 and 4 look the same. But kind 1 and 3 are completely different. How could I calculate “uh” as a complex form, not just use the real part or imagine part of “uh”?
Thank you

Please make a minimal reproducible example. This means a small code using simple meshes (say a unit cube or unit square) that people can copy-paste and run on their own system to reproduce the error.

I made a simple meshes.The solution should consistent with u =(sin(2piy)/(2pi)+jy)sin(pix)
The main code is following

import numpy as np
import math
import ufl
from dolfinx import fem, mesh, plot
from ufl import ds, dx, grad, inner, sin
from mpi4py import MPI
from petsc4py.PETSc import ScalarType

msh = mesh.create_rectangle(comm=MPI.COMM_WORLD,
points=((0.0, 0.0), (2.0, 1.0)), n=(32, 16),
cell_type=mesh.CellType.triangle,)
V = fem.FunctionSpace(msh, (“Lagrange”, 1))
facets = mesh.locate_entities_boundary(msh, dim=1,
marker=lambda x: np.logical_or(np.isclose(x[0], 0.0),
np.isclose(x[0], 2.0)))
dofs = fem.locate_dofs_topological(V=V, entity_dim=1, entities=facets)
bc = fem.dirichletbc(value=ScalarType(0), dofs=dofs, V=V)
u = ufl.TrialFunction(V)
v = ufl.TestFunction(V)
x = ufl.SpatialCoordinate(msh)
pi = math.pi
x0 = pi * x[0]
x1 = sin(x0)
y1 = sin(2 * pi * x[1])
f = pi * x1 * (2.5 * y1 + 1j * pi * x[1])
g = (1 + 1j) * sin(pi * x[1])
a = inner(grad(u), grad(v)) * dx
L = inner(f, v) * dx + inner(g, v) * ds

problem = fem.petsc.LinearProblem(a, L, bcs=[bc], petsc_options={“ksp_type”: “preonly”, “pc_type”: >“lu”})
uh = problem.solve()

Here are my problems:
1.In the analytical solution, u(y=0)=0, u(y=1)=jsin(pix). But the figure in pyvista is not consistent.The figure is as follow:

2.I try to understand “ds” by:

print(fem.assemble(fem.Constant(msh,1) * ds))
But I get:
RuntimeError: Unsupported dtype
I know this way from the answer of this web

print(assemble(Constant(1)*dx))

I have tried “print(assemble(Constant(1)*ds))”. But “assemble” and “Constant” are not defined.
So, how can I do?
Thank you.

There are multiple issues here.

  1. fem.assemble is not a command in DOLFINx. it would be fem.assemble_scalar.
  2. The input value to your constant is an integer, it should be: fem.Constant(msh, ScakarType(1)).

For the rest of your problem, have you tried using ufl to derive the boundary conditions and sourcing term?
I’ve for instance done this in Poisson problem with Neumann boundary condition - #6 by dokken

Thank you for your help.
I have tried

print(fem.assemble_scalar(fem.Constant(msh, ScalarType(1))*ds))
but got
‘’’
Traceback (most recent call last):
File “/usr/lib/python3.10/code.py”, line 90, in runcode
exec(code, self.locals)
File “”, line 1, in
File “/home/jtr/pycharm-2022.2.4/plugins/python/helpers/pydev/_pydev_bundle/pydev_umd.py”, line 198, in runfile
pydev_imports.execfile(filename, global_vars, local_vars) # execute the script
File “/home/jtr/pycharm-2022.2.4/plugins/python/helpers/pydev/_pydev_imps/_pydev_execfile.py”, line 18, in execfile
exec(compile(contents+“\n”, file, ‘exec’), glob, loc)
File “/home/jtr/fenicsdemo/fenicsx/my project/text4.py”, line 32, in
print(fem.assemble_scalar(fem.Constant(msh, ScalarType(1))*ds))
File “/usr/lib/petscdir/petsc-complex/lib/python3/dist-packages/dolfinx/fem/assemble.py”, line 125, in assemble_scalar
constants = constants or _pack_constants(M)
TypeError: pack_constants(): incompatible function arguments. The following argument types are supported:
1. (form: dolfinx::fem::Form) → numpy.ndarray[numpy.float64]
2. (e: dolfinx::fem::Expression) → numpy.ndarray[numpy.float64]
3. (form: dolfinx::fem::Form) → numpy.ndarray[numpy.float32]
4. (e: dolfinx::fem::Expression) → numpy.ndarray[numpy.float32]
5. (form: dolfinx::fem::Form<std::complex >) → numpy.ndarray[numpy.complex128]
6. (e: dolfinx::fem::Expression<std::complex >) → numpy.ndarray[numpy.complex128]
7. (form: dolfinx::fem::Form<std::complex >) → numpy.ndarray[numpy.complex64]
8. (e: dolfinx::fem::Expression<std::complex >) → numpy.ndarray[numpy.complex64]
Invoked with: Form([Integral(Constant(Mesh(VectorElement(FiniteElement(‘Lagrange’, triangle, 1), dim=2), 0), (), 0), ‘exterior_facet’, Mesh(VectorElement(FiniteElement(‘Lagrange’, triangle, 1), dim=2), 0), ‘everywhere’, {}, None)])
Did you forget to #include <pybind11/stl.h>? Or <pybind11/complex.h>,
<pybind11/functional.h>, <pybind11/chrono.h>, etc. Some automatic
conversions are optional and require extra headers to be included
when compiling your pybind11 module.
‘’’
So, ScalarType(1) may is not the correct type.
Then I will look at the website you recommend.
Thank you.

Try

print(fem.assemble_scalar(fem.form(fem.Constant(msh, ScalarType(1))*ds)))

It runs.And the following code get the same answer.

marker = mesh.meshtags(msh, 1, facets, 1)
ds = ufl.Measure(‘ds’, subdomain_data=marker, domain=msh)
answer = fem.assemble_scalar(fem.form(1 * ds))

And here is my new problem:
How could I get the value of “uh” on boundary?
And even on different part of boundaries which marked with number?
I want to polt a "uh-x"figure.
Thank you.

I would use paraview for this task (plot over line). You can Also see:
https://jsdokken.com/dolfinx-tutorial/chapter1/membrane_code.html#plotting-the-solution-over-a-line

I know it is vary easy to plot over line by paraview. But I want to plot “uh” on a circle. The outer boundary is a circle. I want to plot “uh-degree”.

Then use the same kind of scripts as I provided in

Where the points on a line is replaced by points on a circle.

I have tried the demo in

Implementation — FEniCSx tutorial

And I find there are some problem in “implementation” chapter
1.In Defining the variational problem, the code should be

a = ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx
L = ufl.inner(p, v) * ufl.dx

2.In Plotting the solution over a line,when the debuger run into

plotter.show()

Then going to “connected”.
I’m not sure whether there is any wrong. I just comment out these lines and go on.

If you are solving a complex valued problem, you need to use inner. This is covered in: Complex Calculate using complex solutions of variational formulation - #11 by MushishiYako

I don’t understand what you are trying to say here.
To make it easier for people to help you, I would strongly suggest making a minimal reproducible example.

Thank you for your reply.
About problem 2, I use pycharm to run my code. When I debug the demo code, I use step over to run line by line. And at the time that I run this line “plotter.show()” of these lines:

if not pyvista.OFF_SCREEN:
    plotter.show()
else:
    plotter.screenshot("deflection.png")

Debug do not respond. All variables disappear and just “connected” left.
Perhaps pycharm causes the problem, and a similar situation occurr when I jumped out of the for loop:

for i, point in enumerate(points.T):
    if len(colliding_cells.links(i))>0:
        points_on_proc.append(point)
        cells.append(colliding_cells.links(i)[0])

I try to use ‘Interpolation of a ufl-expression’ in complex type. The code is as follow:

p = (sin(2 * pi * x[1]) / (2 * pi) + 1j * x[1]) * sin(pi * x[0])
Q = fem.FunctionSpace(msh, ("Lagrange", 5))
expr = fem.Expression(p, Q.element.interpolation_points())  #TypeError: float() argument must be a string or a real number, not 'complex'
orin = fem.Function(Q)
orin.interpolate(expr) 

So, how could I use complex format directly? Or, I need to divide complex numbers into real and imaginary parts to interpolation?

Please provide a full reproducible example, as alot of crucial definitions are missing from your snippet

OK, here is my full code:

import numpy as np
import math
import ufl
from dolfinx import fem, mesh
from ufl import ds, dx, grad, inner, sin
from mpi4py import MPI
from petsc4py.PETSc import ScalarType

msh = mesh.create_rectangle(comm=MPI.COMM_WORLD,
                            points=((0.0, 0.0), (2.0, 1.0)), n=(32, 16),
                            cell_type=mesh.CellType.triangle,)
V = fem.FunctionSpace(msh, ("Lagrange", 1))
facets = mesh.locate_entities_boundary(msh, dim=1,
                                       marker=lambda x: np.logical_or(np.isclose(x[0], 0.0),
                                                                      np.isclose(x[0], 2.0)))
dofs = fem.locate_dofs_topological(V=V, entity_dim=1, entities=facets)
bc = fem.dirichletbc(value=ScalarType(0), dofs=dofs, V=V)
u = ufl.TrialFunction(V)
v = ufl.TestFunction(V)
x = ufl.SpatialCoordinate(msh)
pi = math.pi
x1 = sin(pi * x[0])
y1 = sin(2 * pi * x[1])
f = pi * x1 * (2.5 * y1 + 1j * pi * x[1])
g = (1 + 1j) * sin(pi * x[1])
a = inner(grad(u), grad(v)) * dx
L = inner(f, v) * dx + inner(g, v) * ds

print(fem.assemble_scalar(fem.form(fem.Constant(msh, ScalarType(1))*ds)))

problem = fem.petsc.LinearProblem(a, L, bcs=[bc], petsc_options={"ksp_type": "preonly", "pc_type": "lu"})
uh = problem.solve()

#Interpolation
p = (sin(2 * pi * x[1]) / (2 * pi) + 1j * x[1]) * sin(pi * x[0])
Q = fem.FunctionSpace(msh, ("Lagrange", 5))
expr = fem.Expression(p, Q.element.interpolation_points()) #TypeError: float() argument must be a string or a real number, not 'complex'
orin = fem.Function(Q)
orin.interpolate(expr)

The error perhaps is form the complex format “p” in “fem.Expression()”. So my problem is:
How could I use complex format directly? Or, I need to divide complex numbers into real and imaginary parts to interpolation?

Back to my initial problem about complex. The solution should consistent with

u =(sin(2 * pi * y)/(2 * pi)+j * y) * sin(pi * x).

And the boundaries are:

u(x=0)=0; u(x=3)=0; u(y=0)=0; u(y=1)=j * sin(pi * x).

So, the solution of “uh * ds” should be

“j * sin(pi * x) * ds”(from 0 to 2) = j * 2/pi

Approximately equal to

j * 0.636619772.

but the answer which I get is

“(-0.2018599781544065+0.6365176098632956j)”.

I don’t know how to calculate the real part.
My code is as follow:

import numpy as np
import math
import ufl
from dolfinx import fem, mesh, plot
from ufl import ds, dx, grad, inner, sin
from mpi4py import MPI
from petsc4py.PETSc import ScalarType

msh = mesh.create_rectangle(comm=MPI.COMM_WORLD,
                            points=((0.0, 0.0), (3.0, 1.0)), n=(300, 100),
                            cell_type=mesh.CellType.triangle,)
V = fem.FunctionSpace(msh, ("Lagrange", 1))
facets = mesh.locate_entities_boundary(msh, dim=1,
                                       marker=lambda x: np.logical_or(np.logical_or(np.isclose(x[0], 0.0),
                                                                      np.isclose(x[0], 3.0)),
                                                                      np.isclose(x[1], 0.0)))
dofs = fem.locate_dofs_topological(V=V, entity_dim=1, entities=facets)
bc = fem.dirichletbc(value=ScalarType(0), dofs=dofs, V=V)
u = ufl.TrialFunction(V)
v = ufl.TestFunction(V)
x = ufl.SpatialCoordinate(msh)
pi = math.pi
x1 = sin(pi * x[0])
y1 = sin(2 * pi * x[1])
f = pi * x1 * (2.5 * y1 + 1j * pi * x[1])
g = 1j * sin(pi * x[0])
a = inner(grad(u), grad(v)) * dx
L = inner(f, v) * dx + inner(g, v) * ds

print(fem.assemble_scalar(fem.form(fem.Constant(msh, ScalarType(1))*ds)))

problem = fem.petsc.LinearProblem(a, L, bcs=[bc], petsc_options={"ksp_type": "preonly", "pc_type": "lu"})
uh = problem.solve()

marker = mesh.meshtags(msh, 1, facets, 1)
ds = ufl.Measure('ds', subdomain_data=marker, domain=msh)

answer = fem.assemble_scalar(fem.form(uh * ds))
print(answer) # answer = -2 + j * 2/pi

Change this to:

p = (sin(2 * pi * x[1]) / (2 * pi) + fem.Constant(msh, ScalarType(1j)) * x[1]) * sin(pi * x[0])

Yes , it work successfully and just like what I want. Thank you for your help.
And I still need your help.Here are my problem:

  1. About the boundary defined. When I build boundary without u(y = 0) = 0, the solution was a little different at y = 1 , but totally different at y = 0 , especially the real part. The figure are as follow, first one is without u(y = 0) = 0. The second one is with u(y = 0 ) = 0.The black parts are just because my grid is too dense.

    the real part of the numerical solution without u(y = 0) = 0

    the imaginary part of the numerical solution without u(y = 0) = 0

    the real part of the numerical solution with u(y = 0) = 0

    the imaginary part of the numerical solution with u(y = 0) = 0

The difference confuse me. I want you to tell me why. The main code is as follow:

import numpy as np
import math
import ufl
from dolfinx import fem, mesh, plot
from ufl import ds, dx, grad, inner, sin
from mpi4py import MPI
from petsc4py.PETSc import ScalarType

msh = mesh.create_rectangle(comm=MPI.COMM_WORLD,
                            points=((0.0, 0.0), (3.0, 1.0)), n=(300, 100),
                            cell_type=mesh.CellType.triangle,)
V = fem.FunctionSpace(msh, ("Lagrange", 1))
facets = mesh.locate_entities_boundary(msh, dim=1,
                                       marker=lambda x: np.logical_or(np.isclose(x[0], 0.0),
                                                                      np.isclose(x[0], 3.0))) # without u(y = 0) = 0
# facets = mesh.locate_entities_boundary(msh, dim=1,
#                                        marker=lambda x: np.logical_or(np.logical_or(np.isclose(x[0], 0.0),
#                                                                       np.isclose(x[0], 3.0)),
#                                                                       np.isclose(x[1], 0.0))) # with u(y = 0) = 0
dofs = fem.locate_dofs_topological(V=V, entity_dim=1, entities=facets)
bc = fem.dirichletbc(value=ScalarType(0), dofs=dofs, V=V)
u = ufl.TrialFunction(V)
v = ufl.TestFunction(V)
x = ufl.SpatialCoordinate(msh)
pi = math.pi
x1 = sin(pi * x[0])
y1 = sin(2 * pi * x[1])
f = pi * x1 * (2.5 * y1 + 1j * pi * x[1])
g = (1 + 1j) * sin(pi * x[0])
a = inner(grad(u), grad(v)) * dx
L = inner(f, v) * dx + inner(g, v) * ds

problem = fem.petsc.LinearProblem(a, L, bcs=[bc], petsc_options={"ksp_type": "preonly", "pc_type": "lu"})
uh = problem.solve()

And the pyvista part

# uh.real
try:
    import pyvista
    cells, types, x = plot.create_vtk_mesh(V)
    grid = pyvista.UnstructuredGrid(cells, types, x)
    grid.point_data["ur"] = uh.x.array.real
    grid.set_active_scalars("ur")
    plotter = pyvista.Plotter()
    plotter.add_mesh(grid, show_edges=True)
    warped = grid.warp_by_scalar()
    plotter.add_mesh(warped)
    plotter.show_grid()
    plotter.show()
except ModuleNotFoundError:
    print("'pyvista' is required to visualise the solution")
    print("Install 'pyvista' with pip: 'python3 -m pip install pyvista'")
# uh.imag
try:
    import pyvista
    cells, types, x = plot.create_vtk_mesh(V)
    grid = pyvista.UnstructuredGrid(cells, types, x)
    grid.point_data["ui"] = uh.x.array.imag
    grid.set_active_scalars("ui")
    plotter = pyvista.Plotter()
    plotter.add_mesh(grid, show_edges=True)
    warped = grid.warp_by_scalar()
    plotter.add_mesh(warped)
    plotter.show_grid()
    plotter.show()
except ModuleNotFoundError:
    print("'pyvista' is required to visualise the solution")
    print("Install 'pyvista' with pip: 'python3 -m pip install pyvista'")

2.About my recent and initial problem----the answer of “uh * ds”
The answer should be j * 2 / pi , approximately equal to j * 0.636619772
Only with the boundary condition u(y = 0) = 0, I could get

(-2.3239149089002746e-05+0.6365176098632956j)

It’s very close to the analytical solution. That’s sounds a good news for me.

By the way, I want to know how to type formula in the topic and reply.

I’m not sure what you expect here. By changing where you are enforcing u=0, you also change where you are applying

and the combination og these two boundary conditions are fundamentally changing the solution of your PDE.

To write mathematics, use normal Latex syntax ($ -encapsulation).

Please look at various tutorials explaining how to compare with manufactured solutions
https://jsdokken.com/dolfinx-tutorial/chapter1/complex_mode.html
https://jsdokken.com/dolfinx-tutorial/chapter3/robin_neumann_dirichlet.html
https://jsdokken.com/dolfinx-tutorial/chapter4/convergence.html