I have a variational formulation that uses the Lagrange multiplier method. I need to use a real element for this multiplier. I’m a bit confused by real elements being in FEniCSx but still not implemented: calling
lagrange_elem = basix.ufl.element("Lagrange", domain.ufl_cell().cellname(), 1)
real_elem = basix.ufl.real_element(domain.ufl_cell().cellname(), (1,))
mixed_elem = basix.ufl.mixed_element([lagrange_elem, real_elem])
is fine, but
V = fem.functionspace(domain, mixed_elem)
is not for some reason and raises NotImplementedError
. I’m aware of this issue but it’s contradicted by this one so I’m a bit confused.
So I decided to try my luck with legacy Dolfin. My problem now is I can’t find how to generate a circular mesh with holes in legacy Dolfin. It was easy in 1.3 with CSG, or in Dolfinx with Gmsh, but I can’t find anything in the 1.9 documentation about that.
How can I create the mesh without relying on an external xdmf file? I need to loop on different meshes that I generate on the fly and the performance will be hit too hard if I need to handle file reading and writing.
My problem is as follows, with \eta a scalar function, \lambda a real number and i\in\{1,2\}:
The differential equation is (\Delta-1)\eta=0 and the conditions are
Maybe there is a different way to solve this?
Here is my Dolfinx code:
import basix.ufl
from mpi4py import MPI
import numpy as np
import ufl
import basix
import dolfinx
from dolfinx.fem.petsc import LinearProblem
tan_theta_1 = 1
tan_theta_2 = 1
interchannel_distance = 4
channel_radius = 1
domain_radius = 20
### MESH ###
import gmsh
gmsh.initialize()
# Geometry
ext_boundary = gmsh.model.occ.addCircle(0, 0, 0, domain_radius)
ext_boundary_loop = gmsh.model.occ.addCurveLoop([ext_boundary])
channel1 = gmsh.model.occ.addCircle(-interchannel_distance/2, 0, 0, channel_radius)
channel1_loop = gmsh.model.occ.addCurveLoop([channel1])
channel2 = gmsh.model.occ.addCircle(interchannel_distance/2, 0, 0, channel_radius)
channel2_loop = gmsh.model.occ.addCurveLoop([channel2])
membrane = gmsh.model.occ.addPlaneSurface([ext_boundary_loop, channel1_loop, channel2_loop])
gmsh.model.occ.synchronize()
gdim = 2
gmsh.model.addPhysicalGroup(gdim, [membrane], 1)
# Mesh generation
gmsh.option.setNumber("Mesh.CharacteristicLengthMin", 0.05)
gmsh.option.setNumber("Mesh.CharacteristicLengthMax", 2)
gmsh.option.setNumber("Mesh.MeshSizeFromCurvature", 100)
gmsh.model.mesh.generate(gdim)
# DOLFINx interface
from dolfinx.io import gmshio
from dolfinx.fem.petsc import LinearProblem
from mpi4py import MPI
gmsh_model_rank = 0
mesh_comm = MPI.COMM_WORLD
domain, cell_markers, facet_markers = gmshio.model_to_mesh(gmsh.model, mesh_comm, gmsh_model_rank, gdim=gdim)
### Boundaries ###
from dolfinx.mesh import locate_entities, meshtags
# Markers
def is_on_ext_boundary(x) -> bool:
return np.isclose(x[0]**2 + x[1]**2, domain_radius**2)
def is_on_channel1_boundary(x) -> bool:
return np.isclose((x[0] + interchannel_distance/2)**2 + x[1]**2, channel_radius**2)
def is_on_channel2_boundary(x) -> bool:
return np.isclose((x[0] - interchannel_distance/2)**2 + x[1]**2, channel_radius**2)
def is_on_boundary(x) -> bool:
return np.logical_or(is_on_ext_boundary(x), is_on_channel1_boundary(x), is_on_channel2_boundary(x))
boundaries = list(enumerate([is_on_ext_boundary, is_on_channel1_boundary, is_on_channel2_boundary]))
facet_indices, facet_markers = [], []
fdim = domain.topology.dim - 1
for (marker, locator) in boundaries:
facets = locate_entities(domain, fdim, locator)
facet_indices.append(facets)
facet_markers.append(np.full_like(facets, marker))
facet_indices = np.hstack(facet_indices).astype(np.int32)
facet_markers = np.hstack(facet_markers).astype(np.int32)
sorted_facets = np.argsort(facet_indices)
facet_tag = meshtags(domain, fdim, facet_indices[sorted_facets], facet_markers[sorted_facets])
# Debugging
from dolfinx.io import XDMFFile
domain.topology.create_connectivity(domain.topology.dim-1, domain.topology.dim)
with XDMFFile(domain.comm, "facet_tags.xdmf", "w", encoding=XDMFFile.Encoding.ASCII) as xdmf:
xdmf.write_mesh(domain)
xdmf.write_meshtags(facet_tag, domain.geometry)
### Variational formulation ###
# Function space
from dolfinx import fem
V = fem.functionspace(domain, ("Lagrange", 1))
# Build function space with Lagrange multiplier
lagrange_elem = basix.ufl.element("Lagrange", domain.ufl_cell().cellname(), 1)
real_elem = basix.ufl.real_element(domain.ufl_cell().cellname(), (1,))
lagrange_V = fem.functionspace(domain, lagrange_elem)
real_V = fem.functionspace(domain, real_elem) # NotImplementedError
mixed_V = fem.functionspace(domain, lagrange_V * real_V)