@@ -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,41 @@ 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
165+
158166 Q = FunctionSpace (V .mesh (), curl_to_grad (V .ufl_element ()))
167+ degree = max (as_tuple (Q .ufl_element ().degree ()))
168+ variant = Q .ufl_element ().variant ()
159169 gradient = tabulate_exterior_derivative (Q , V )
160- if variant == 'fdm' :
161- corners = get_vertex_dofs (Q )
162- gradient .compose ('_elements_corners' , corners )
170+
171+ if variant == "fdm" :
172+ vdofs = get_restricted_dofs (Q , "vertex" )
173+ gradient .compose ('_elements_corners' , vdofs )
174+
175+ # Constant moments along edges
176+ wdofs = get_low_order_dofs (V )
177+ v0 = Function (V )
178+ with v0 .dat .vec as y :
179+ y .setValues (wdofs , numpy .ones ((wdofs .getSizes ()[0 ],), dtype = PETSc .RealType ))
180+
181+ # Linear moments along edges
182+ # Constant moments of gradients of H1 edge bubbles
183+ p = Function (Q ).interpolate (1 )
184+ q = Function (Q )
185+
186+ edofs = get_restricted_dofs (Q , "edge" )
187+ edofs = edofs .difference (vdofs )
188+ with q .dat .vec as qvec , p .dat .vec as pvec :
189+ qvec .setValues (edofs , pvec .getValues (edofs ))
190+
191+ v1 = Function (V )
192+ with v1 .dat .vec as y , q .dat .vec as x :
193+ gradient .mult (x , y )
194+ y .chop (1E-14 )
195+
196+ nsp = VectorSpaceBasis ([v0 , v1 ])
197+ gradient .compose ("_constraints" , nsp .nullspace ())
198+
163199 grad_args = (gradient ,)
164200 grad_kwargs = {'order' : degree }
165201 return grad_args , grad_kwargs
0 commit comments