2020from pyop2 .compilation import load
2121from pyop2 .mpi import COMM_SELF
2222from pyop2 .utils import get_petsc_dir
23+ from collections import defaultdict
2324
2425
2526__all__ = ["assemble_mixed_mass_matrix" , "intersection_finder" ]
2627
2728
29+ # TODO replace with KAIJ (we require petsc4py wrappers)
2830class BlockMatrix (object ):
29- def __init__ (self , mat , dimension ):
31+ def __init__ (self , mat , dimension , block_scale = None ):
3032 self .mat = mat
3133 self .dimension = dimension
34+ self .block_scale = block_scale
3235
3336 def mult (self , mat , x , y ):
3437 sizes = self .mat .getSizes ()
@@ -41,6 +44,8 @@ def mult(self, mat, x, y):
4144 xi = PETSc .Vec ().createWithArray (xa , size = sizes [1 ], comm = x .comm )
4245 yi = PETSc .Vec ().createWithArray (ya , size = sizes [0 ], comm = y .comm )
4346 self .mat .mult (xi , yi )
47+ if self .block_scale is not None :
48+ yi .scale (self .block_scale [i ])
4449 y .array [start ::stride ] = yi .array_r
4550
4651 def multTranspose (self , mat , x , y ):
@@ -54,6 +59,8 @@ def multTranspose(self, mat, x, y):
5459 xi = PETSc .Vec ().createWithArray (xa , size = sizes [0 ], comm = x .comm )
5560 yi = PETSc .Vec ().createWithArray (ya , size = sizes [1 ], comm = y .comm )
5661 self .mat .multTranspose (xi , yi )
62+ if self .block_scale is not None :
63+ yi .scale (self .block_scale [i ])
5764 y .array [start ::stride ] = yi .array_r
5865
5966
@@ -68,14 +75,6 @@ def assemble_mixed_mass_matrix(V_A, V_B):
6875 if len (V_A ) > 1 or len (V_B ) > 1 :
6976 raise NotImplementedError ("Sorry, only implemented for non-mixed spaces" )
7077
71- if V_A .ufl_element ().mapping () != "identity" or V_B .ufl_element ().mapping () != "identity" :
72- msg = """
73- Sorry, only implemented for affine maps for now. To do non-affine, we'd need to
74- import much more of the assembly engine of UFL/TSFC/etc to do the assembly on
75- each supermesh cell.
76- """
77- raise NotImplementedError (msg )
78-
7978 mesh_A = V_A .mesh ()
8079 mesh_B = V_B .mesh ()
8180
@@ -116,15 +115,39 @@ def likely(cell_A):
116115 def likely (cell_A ):
117116 return cell_map [cell_A ]
118117
119- assert V_A .value_size == V_B .value_size
120- orig_value_size = V_A .value_size
121- if V_A .value_size > 1 :
118+ assert V_A .block_size == V_B .block_size
119+ orig_block_size = V_A .block_size
120+
121+ # To deal with symmetry, each block of the mass matrix must be rescaled by the multiplicity
122+ if V_A .ufl_element ().mapping () == "symmetries" :
123+ symmetry = V_A .ufl_element ().symmetry ()
124+ assert V_B .ufl_element ().mapping () == "symmetries"
125+ assert V_B .ufl_element ().symmetry () == symmetry
126+
127+ multiplicity = defaultdict (int )
128+ for idx in numpy .ndindex (V_A .value_shape ):
129+ idx = symmetry .get (idx , idx )
130+ multiplicity [idx ] += 1
131+
132+ block_scale = tuple (scale for idx , scale in multiplicity .items ())
133+ else :
134+ block_scale = None
135+
136+ if V_A .block_size > 1 :
122137 V_A = firedrake .FunctionSpace (mesh_A , V_A .ufl_element ().sub_elements [0 ])
123- if V_B .value_size > 1 :
138+ if V_B .block_size > 1 :
124139 V_B = firedrake .FunctionSpace (mesh_B , V_B .ufl_element ().sub_elements [0 ])
125140
126- assert V_A .value_size == 1
127- assert V_B .value_size == 1
141+ if V_A .ufl_element ().mapping () != "identity" or V_B .ufl_element ().mapping () != "identity" :
142+ msg = """
143+ Sorry, only implemented for affine maps for now. To do non-affine, we'd need to
144+ import much more of the assembly engine of UFL/TSFC/etc to do the assembly on
145+ each supermesh cell.
146+ """
147+ raise NotImplementedError (msg )
148+
149+ assert V_A .block_size == 1
150+ assert V_B .block_size == 1
128151
129152 preallocator = PETSc .Mat ().create (comm = mesh_A ._comm )
130153 preallocator .setType (PETSc .Mat .Type .PREALLOCATOR )
@@ -155,7 +178,7 @@ def likely(cell_A):
155178 onnz = numpy .repeat (onnz , cset .cdim )
156179 preallocator .destroy ()
157180
158- assert V_A .value_size == V_B .value_size
181+ assert V_A .block_size == V_B .block_size
159182 rdim = V_B .dof_dset .cdim
160183 cdim = V_A .dof_dset .cdim
161184
@@ -445,16 +468,16 @@ def likely(cell_A):
445468 lib .restype = ctypes .c_int
446469
447470 ammm (V_A , V_B , likely , node_locations_A , node_locations_B , M_SS , ctypes .addressof (lib ), mat )
448- if orig_value_size == 1 :
471+ if orig_block_size == 1 :
449472 return mat
450473 else :
451474 (lrows , grows ), (lcols , gcols ) = mat .getSizes ()
452- lrows *= orig_value_size
453- grows *= orig_value_size
454- lcols *= orig_value_size
455- gcols *= orig_value_size
475+ lrows *= orig_block_size
476+ grows *= orig_block_size
477+ lcols *= orig_block_size
478+ gcols *= orig_block_size
456479 size = ((lrows , grows ), (lcols , gcols ))
457- context = BlockMatrix (mat , orig_value_size )
480+ context = BlockMatrix (mat , orig_block_size , block_scale = block_scale )
458481 blockmat = PETSc .Mat ().createPython (size , context = context , comm = mat .comm )
459482 blockmat .setUp ()
460483 return blockmat
0 commit comments