@@ -160,43 +160,46 @@ cannot always be sure that the linear solver at hand is correctly utilising the
160160To directly eliminate the nullspace we introduce a class :code: `FixAtPointBC ` which
161161implements a boundary condition that fixes a field at a single point. ::
162162
163- import firedrake.utils as firedrake_utils
164-
165- class FixAtPointBC(firedrake.DirichletBC):
166- r'''A special BC object for pinning a function at a point.
167-
168- :arg V: the :class:`.FunctionSpace` on which the boundary condition should be applied.
169- :arg g: the boundary condition value.
170- :arg bc_point: the point at which to pin the function.
171- The location of the finite element DOF nearest to bc_point is actually used.
172- '''
173- def __init__(self, V, g, bc_point):
174- super(FixAtPointBC, self).__init__(V, g, bc_point)
175- if isinstance(bc_point, tuple):
176- bc_point = as_vector(bc_point)
177- self.bc_point = bc_point
178-
179- @firedrake_utils.cached_property
180- def nodes(self):
181- V = self.function_space()
182- x = firedrake.SpatialCoordinate(V.mesh())
183- xdist = x - self.bc_point
184-
185- test = firedrake.TestFunction(V)
186- trial = firedrake.TrialFunction(V)
187- xphi = firedrake.assemble(ufl.inner(xdist * test, xdist * trial) * ufl.dx, diagonal=True)
188- phi = firedrake.assemble(ufl.inner(test, trial) * ufl.dx, diagonal=True)
189- with xphi.dat.vec as xu, phi.dat.vec as u:
190- xu.pointwiseDivide(xu, u)
191- min_index, min_value = xu.min() # Find the index of the DOF closest to bc_point
192-
193- nodes = V.dof_dset.lgmap.applyInverse([min_index])
194- nodes = nodes[nodes >= 0]
195- return nodes
196-
163+ import functools
164+
165+ class FixAtPointBC(DirichletBC):
166+ r'''A special BC object for pinning a function at a point.
167+
168+ :arg V: the :class:`.FunctionSpace` on which the boundary condition should be applied.
169+ :arg g: the boundary condition value.
170+ :arg bc_point: the point at which to pin the function.
171+ The location of the finite element DOF nearest to bc_point is actually used.
172+ '''
173+ def __init__(self, V, g, bc_point):
174+ super().__init__(V, g, bc_point)
175+
176+ @functools.cached_property
177+ def nodes(self):
178+ V = self.function_space()
179+ if V.mesh().ufl_coordinate_element().degree() != 1:
180+ # Ensure a P1 mesh
181+ coordinates = V.mesh().coordinates
182+ P1 = coordinates.function_space().reconstruct(degree=1)
183+ P1_mesh = Mesh(Function(P1).interpolate(coordinates))
184+ V = V.reconstruct(mesh=P1_mesh)
185+
186+ point = [tuple(self.sub_domain)]
187+ vom = VertexOnlyMesh(V.mesh(), point)
188+ P0 = FunctionSpace(vom, "DG", 0)
189+ Fvom = Cofunction(P0.dual()).assign(1)
190+
191+ # Take the basis function with the largest abs value at bc_point
192+ v = TestFunction(V)
193+ F = assemble(Interpolate(inner(v, v), Fvom))
194+ with F.dat.vec as Fvec:
195+ max_index, _ = Fvec.max()
196+ nodes = V.dof_dset.lgmap.applyInverse([max_index])
197+ nodes = nodes[nodes >= 0]
198+ return nodes
199+
197200We use this to fix the pressure and auxiliary temperature at the origin::
198201
199- aux_bcs = [FixAtPointBC(Z.sub(1), 0, as_vector([ 0, 0] )),
202+ aux_bcs = [FixAtPointBC(Z.sub(1), 0, ( 0, 0)),
200203 FixAtPointBC(Z.sub(2), 0, as_vector([0, 0]))]
201204
202205:code: `FixAtPointBC ` takes three arguments: the function space to fix, the value with which it
0 commit comments