Here’s the code
import numpy as np
from time import process_time
import ufl
from dolfinx import plot, fem
from dolfinx.fem import Function, FunctionSpace, assemble_scalar, form, locate_dofs_geometrical, dirichletbc, form
from dolfinx.fem.petsc import LinearProblem
from dolfinx.io import XDMFFile
from dolfinx.mesh import create_rectangle, CellType
from ufl import dx, grad, inner, div, conj, real, imag, Dx
from mpi4py import MPI
from petsc4py import PETSc
if np.issubdtype(PETSc.ScalarType, np.complexfloating):
imaginaryUnit = PETSc.ScalarType(0 + 1j)
# Parameters
dt = 1/50;
hbar = 0.03;
inlet_velocity = np.array([1.0, 0.0]);
T = 0.5;
numsteps = T/dt
# Mesh Discretization
dimensions = np.array([10,4]);
nx = 250;
ny = 100;
domain = create_rectangle(MPI.COMM_WORLD, [np.array([0, 0]), dimensions],
[nx, ny], CellType.triangle)
tdim = domain.topology.dim;
# Schrodinger: definition of the functional space
deg = 2; # linear = 1, parabolic = 2 Lagrangian FE are used
V = FunctionSpace(domain, ("CG", deg))
Psi1 = Function(V)
Psi2 = Function(V)
Psi1.name = "WaveFunction - 1"
Psi2.name = "WaveFunction - 2"
PsiTilde1 = Function(V)
PsiTilde2 = Function(V)
psi1old = Function(V)
psi2old = Function(V)
u = ufl.TrialFunction(V)
v = ufl.TestFunction(V)
# Schrodinger: defining the inlet boundary condition
dofs_inlet = locate_dofs_geometrical(V, lambda x: np.isclose(x[0],0))
inletPlaneWave = Function(V)
kvec = inlet_velocity/hbar
inletPlaneWave.interpolate(lambda x: np.exp(imaginaryUnit * (kvec[0]*x[0] + kvec[1]*x[1])))
bc_Inlet = dirichletbc(inletPlaneWave, dofs_inlet)
# Schrodinger: initialization
def initial_condition1(x):
return 1 + 0j + 0 * x[0] + 1 * x[1]
def initial_condition2(x, c = 0.01 ):
return c + 0j + 0 * x[0] + 1 * x[1]
psi1old.interpolate(initial_condition1)
psi2old.interpolate(initial_condition2)
theta = 0.5 # Crank-Nicolson is used - theta method is implemented
a = inner(u, v) * dx - imaginaryUnit * dt * hbar / 2 * (1-theta) * inner(grad(u), grad(v)) * dx
L1 = inner(psi1old, v) * dx + imaginaryUnit * dt * hbar / 2 * theta * inner(grad(psi1old), grad(v)) * dx
L2 = inner(psi2old, v) * dx + imaginaryUnit * dt * hbar / 2 * theta * inner(grad(psi2old), grad(v)) * dx
schrodinger_bilinear = form(a)
schrodinger_RHS1 = form(L1)
schrodinger_RHS2 = form(L2)
schrodinger_A = fem.petsc.assemble_matrix(schrodinger_bilinear, bcs = [bc_Inlet])
schrodinger_A.assemble()
schrodinger_L1 = fem.petsc.create_vector(schrodinger_RHS1)
schrodinger_L2 = fem.petsc.create_vector(schrodinger_RHS2)
schrodinger_solve = PETSc.KSP().create(domain.comm)
schrodinger_solve.setOperators(schrodinger_A)
schrodinger_solve.setType(PETSc.KSP.Type.CG)
# Defining a function to compute velocity field from wave function
def wave_derivative_i(u1, u2, i):
return hbar * (- imaginaryUnit * ( grad(u1)[i] * conj(u1) + grad(u2)[i] * conj(u2) ) )
uTildex = real(wave_derivative_i(PsiTilde1, PsiTilde2, 0))
uTildey = real(wave_derivative_i(PsiTilde1, PsiTilde2, 1))
sourceTerm = grad(uTildex)[0] + grad(uTildey)[1]
ConversionSpace = FunctionSpace(domain, ("DG", 0))
ConversionSpace = FunctionSpace(domain, ("DG", 0))
tmpExpression = fem.Expression(sourceTerm, ConversionSpace.element.interpolation_points)
# other option
tmpExpression = fem.Expression(uTildex, ConversionSpace.element.interpolation_points)
tmpFunction = Function(ConversionSpace)
tmpFunction.interpolate(tmpExpression)