Hi all,
I’m solving a nonlinear problem in a 3D mesh, the result of which is a 3D vector field:
import numpy as np
import ufl
import gmsh
from dolfinx import fem, nls
from dolfinx.io import gmshio
from petsc4py.PETSc import ScalarType
from mpi4py import MPI
model = gmsh.model; geom = gmsh.model.geo
mesh_comm = MPI.COMM_WORLD
def generateSphere3D(RR):
"""Create a Gmsh model of a sphere."""
gmsh.initialize()
gmsh.option.setNumber("General.Terminal", 0)
model.add("model_acorn")
sphere = model.occ.addSphere(0, 0, 0, RR, tag=1)
model.occ.synchronize()
model.add_physical_group(dim=3, tags=[sphere])
geom.synchronize()
model.mesh.generate(3)
return gmshio.model_to_mesh(model, mesh_comm, 0, gdim=3)
domain, _, _ = generateSphere3D(1)
#### define the space of funtions and the functions that we will use
element = ufl.VectorElement("CG", domain.ufl_cell(), 1, 3)
V = fem.FunctionSpace(domain, element) # space of vector functions with 3 components
m = fem.Function(V, name='m') # 3-dimensional vector OP
dm = ufl.TrialFunction(V) # variation of the OP
phi = ufl.TestFunction(V) # test function
#### derive the weak formulation directly from the energy functional
def ET(u):
# just a simple example
a = -1; c = 1
return (a*ufl.dot(u,u)/2. + c*ufl.dot(u,u)**2/4.)*ufl.dx
F = ET(m)
dF = ufl.derivative(F, m, phi) # variation of the energy
JF = ufl.derivative(dF, m, dm) # Jacobian of the energy
#### define and solve the problem
problem = fem.petsc.NonlinearProblem(dF, m, J=JF)
def solveProblem(prob):
solver = nls.petsc.NewtonSolver(mesh_comm, prob)
solver.convergence_criterion = "incremental"
solver.rtol = 1e-6
solver.max_it = 100
solver.report = True
minit = [1.,0,0] # initial guess for the unknown function
minit = ufl.as_vector([fem.Constant(domain, ScalarType(minit[0])),
fem.Constant(domain, ScalarType(minit[1])),
fem.Constant(domain, ScalarType(minit[2]))])
expr = fem.Expression(minit, V.element.interpolation_points())
m.interpolate(expr)
solver.nonzero_initial_guess = True
n, converged = solver.solve(m)
assert(converged)
solveProblem(problem)
Now, for visualisation purposes, I want to plot the 3D-vector solution for the y=0 slice (2D) of the original system. Building on this link, I tried this:
def generateCircle2D(RR, ms=0.1):
"""Create a Gmsh model of a circle."""
gmsh.initialize()
gmsh.option.setNumber("General.Terminal", 0)
model.add("y=0 projection")
p = [
geom.addPoint(0, 0, RR, ms),
geom.addPoint(0, 0, 0, ms),
geom.addPoint(RR, 0, 0, ms),
geom.addPoint(0, 0, -RR, ms),
geom.addPoint(-RR, 0, 0, ms)
]
l = [
geom.addCircleArc(p[0], p[1], p[2]),
geom.addCircleArc(p[2], p[1], p[3]),
geom.addCircleArc(p[3], p[1], p[4]),
geom.addCircleArc(p[4], p[1], p[0])
]
cl = geom.addCurveLoop([l[j] for j in range(len(l))])
s = geom.addPlaneSurface([cl])
model.occ.synchronize()
model.add_physical_group(2, [s])
geom.synchronize()
model.mesh.generate(2)
return gmshio.model_to_mesh(model, mesh_comm, 0, gdim=3)
domain2D, _, _ = generateCircle2D(1)
element2D = ufl.VectorElement("CG", domain2D.ufl_cell(), 1, 3)
V2D = fem.FunctionSpace(domain2D, element2D)
class m_slice(fem.Expression):
def eval(self, value, x):
value[0] = m(x[0], 0, x[2]) # slice of 3D system: y = 0 plane
def value_shape(self):
return ()
m_sl = fem.Function.interpolate(m_slice, V2D)
However, I get the error message:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Input In [5], in <cell line: 35>()
32 def value_shape(self):
33 return ()
---> 35 m_sl = fem.Function.interpolate(m_slice, V2D)
File /usr/local/dolfinx-real/lib/python3.10/dist-packages/dolfinx/fem/function.py:333, in Function.interpolate(self, u, cells)
330 self._cpp_object.interpolate(expr._cpp_object, cells)
332 if cells is None:
--> 333 mesh = self.function_space.mesh
334 map = mesh.topology.index_map(mesh.topology.dim)
335 cells = np.arange(map.size_local + map.num_ghosts, dtype=np.int32)
AttributeError: type object 'm_slice' has no attribute 'function_space'
The idea is then to generate a stream plot of the projected solution:
from scipy.interpolate import griddata
def plotStreamlines(mm, res=100):
"""Plots the OP streamlines and magnitude in Cartesian coordinates."""
m_array = mm.x.array
m_array = [ [ m_array[i], m_array[i+1], m_array[i+2] ] for i in range(0, len(m_array), 3) ]
m_array = np.array(m_array).T
x_array = V2D.tabulate_dof_coordinates()
x_array = np.array(x_array).T
x_grid, z_grid = np.meshgrid(np.linspace(x_array[0].min(),x_array[0].max(),res),
np.linspace(x_array[2].min(),x_array[2].max(),res))
x_vector_interp = griddata((x_array[0], x_array[2]), m_array[0], (x_grid, z_grid), method='cubic')
z_vector_interp = griddata((x_array[0], x_array[2]), m_array[2], (x_grid, z_grid), method='cubic')
plt.streamplot(x_grid, z_grid, x_vector_interp, z_vector_interp)
plt.show()
plotStreamlines(m_sl)
Could anyone please help me? Thanks in advance!