@@ -104,7 +104,8 @@ def initialize(self, pc):
104104 div_args = (divergence ,)
105105 div_kwargs = dict ()
106106 bddcpc .setBDDCDivergenceMat (* div_args , ** div_kwargs )
107- elif tdim >= 2 and V .finat_element .formdegree == 1 :
107+
108+ elif tdim >= 3 and V .finat_element .formdegree == 1 :
108109 get_gradient = appctx .get ("get_discrete_gradient" , get_discrete_gradient )
109110 gradient = get_gradient (V )
110111 try :
@@ -130,12 +131,19 @@ def applyTranspose(self, pc, x, y):
130131 self .pc .applyTranspose (x , y )
131132
132133
133- def get_vertex_dofs (V ):
134- W = FunctionSpace (V .mesh (), restrict (V .ufl_element (), "vertex" ))
134+ def get_dof_mapping (V , W ):
135135 indices = get_restriction_indices (V , W )
136136 indices = V .dof_dset .lgmap .apply (indices )
137- vertex_dofs = PETSc .IS ().createGeneral (indices , comm = V .comm )
138- return vertex_dofs
137+ return PETSc .IS ().createGeneral (indices , comm = V .comm )
138+
139+
140+ def get_restricted_dofs (V , domain ):
141+ W = FunctionSpace (V .mesh (), restrict (V .ufl_element (), domain ))
142+ return get_dof_mapping (V , W )
143+
144+
145+ def get_low_order_dofs (V ):
146+ return get_dof_mapping (V , V .reconstruct (degree = 1 ))
139147
140148
141149def get_divergence_mat (V , mat_type = "aij" , allow_repeated = False ):
@@ -153,13 +161,40 @@ def get_divergence_mat(V, mat_type="aij", allow_repeated=False):
153161
154162
155163def get_discrete_gradient (V ):
156- degree = max (as_tuple (V .ufl_element ().degree ()))
157- variant = V .ufl_element ().variant ()
164+ from firedrake import VectorSpaceBasis
158165 Q = FunctionSpace (V .mesh (), curl_to_grad (V .ufl_element ()))
166+ degree = max (as_tuple (Q .ufl_element ().degree ()))
167+ variant = Q .ufl_element ().variant ()
159168 gradient = tabulate_exterior_derivative (Q , V )
160- if variant == 'fdm' :
161- corners = get_vertex_dofs (Q )
162- gradient .compose ('_elements_corners' , corners )
169+
170+ if variant == "fdm" :
171+ vdofs = get_restricted_dofs (Q , "vertex" )
172+ gradient .compose ('_elements_corners' , vdofs )
173+
174+ # Constant moments along edges
175+ wdofs = get_low_order_dofs (V )
176+ v0 = Function (V )
177+ with v0 .dat .vec as y :
178+ y .setValues (wdofs , numpy .ones ((wdofs .getSize (),), dtype = PETSc .RealType ))
179+
180+ # Linear moments along edges
181+ # Constant moments of gradients of H1 edge bubbles
182+ p = Function (Q ).interpolate (1 )
183+ q = Function (Q )
184+
185+ edofs = get_restricted_dofs (Q , "edge" )
186+ edofs = edofs .difference (vdofs )
187+ with q .dat .vec as qvec , p .dat .vec as pvec :
188+ qvec .setValues (edofs , pvec .getValues (edofs ))
189+
190+ v1 = Function (V )
191+ with v1 .dat .vec as y , q .dat .vec as x :
192+ gradient .mult (x , y )
193+ y .chop (1E-14 )
194+
195+ nsp = VectorSpaceBasis ([v0 , v1 ])
196+ gradient .compose ("_constraints" , nsp .nullspace ())
197+
163198 grad_args = (gradient ,)
164199 grad_kwargs = {'order' : degree }
165200 return grad_args , grad_kwargs
0 commit comments