def create_empty(target,source): #create matrix mlist = [] nlist = [] for si1 in xrange(target.nspaces): tspace = target.get_space(si1) for ci1 in xrange(tspace.ncomp): for bi1 in xrange(tspace.npatches): m = tspace.get_localndofs(ci1,bi1) mlist.append(m) for si2 in xrange(source.nspaces): sspace = source.get_space(si2) for ci2 in xrange(sspace.ncomp): for bi2 in xrange(sspace.npatches): n = sspace.get_localndofs(ci2,bi2) nlist.append(n) M = np.sum(np.array(mlist,dtype=np.int32)) N = np.sum(np.array(nlist,dtype=np.int32)) mat = PETSc.Mat() mat.create(PETSc.COMM_WORLD) mat.setSizes(((M, None),(N,None))) mat.setType('aij') mat.setLGMap(target.get_overall_lgmap(),cmap=source.get_overall_lgmap()) mat.setUp() mat.assemblyBegin() mat.assemblyEnd() return mat
def create_mono(target,source,blocklist,kernellist): #create matrix mlist = [] nlist = [] for si1 in xrange(target.nspaces): tspace = target.get_space(si1) for ci1 in xrange(tspace.ncomp): for bi1 in xrange(tspace.npatches): m = tspace.get_localndofs(ci1,bi1) mlist.append(m) for si2 in xrange(source.nspaces): sspace = source.get_space(si2) for ci2 in xrange(sspace.ncomp): for bi2 in xrange(sspace.npatches): n = sspace.get_localndofs(ci2,bi2) nlist.append(n) M = np.sum(np.array(mlist,dtype=np.int32)) N = np.sum(np.array(nlist,dtype=np.int32)) mat = PETSc.Mat() mat.create(PETSc.COMM_WORLD) mat.setSizes(((M, None),(N,None))) mat.setType('aij') mat.setLGMap(target.get_overall_lgmap(),cmap=source.get_overall_lgmap()) #preallocate matrix #PRE-ALLOCATE IS NOT QUITE PERFECT FOR FACET INTEGRALS- IT WORKS BUT IS TOO MUCH! mlist.insert(0,0) mlist_adj = np.cumsum(mlist) dnnzarr = np.zeros(M,dtype=np.int32) onnzarr = np.zeros(M,dtype=np.int32) i = 0 #this tracks which row "block" are at #This loop order ensures that we fill an entire row in the matrix first #Assuming that fields are stored si,ci,bi, which they are! for si1 in xrange(target.nspaces): tspace = target.get_space(si1) for ci1 in xrange(tspace.ncomp): #only pre-allocate diagonal blocks, so one loop on bi for bi in xrange(tspace.npatches): #here we can assume tspace.mesh = sspace.mesh, and therefore tspace.blocks = sspace.nblocks) for si2 in xrange(source.nspaces): sspace = source.get_space(si2) for ci2 in xrange(sspace.ncomp): if ((si1,si2) in blocklist): bindex = blocklist.index((si1,si2)) interior_x,interior_y,interior_z = get_interior_flags(kernellist[bindex]) dnnz,onnz = two_form_preallocate_opt(tspace.mesh(),tspace,sspace,ci1,ci2,bi,interior_x,interior_y,interior_z) dnnz = np.ravel(dnnz) onnz = np.ravel(onnz) dnnzarr[mlist_adj[i]:mlist_adj[i+1]] = dnnzarr[mlist_adj[i]:mlist_adj[i+1]] + dnnz onnzarr[mlist_adj[i]:mlist_adj[i+1]] = onnzarr[mlist_adj[i]:mlist_adj[i+1]] + onnz i = i + 1 #increment row block mat.setPreallocationNNZ((dnnzarr,onnzarr)) mat.setOption(PETSc.Mat.Option.IGNORE_ZERO_ENTRIES, False) mat.setOption(PETSc.Mat.Option.NEW_NONZERO_ALLOCATION_ERR, True) mat.setUp() mat.zeroEntries() return mat
def create_matrix(mat_type,target,source,blocklist,kernellist): #block matrix if mat_type == 'nest' and (target.nspaces > 1 or source.nspaces > 1): #create matrix array matrices = [] for si1 in xrange(target.nspaces): matrices.append([]) for si2 in xrange(source.nspaces): if ((si1,si2) in blocklist): bindex = blocklist.index((si1,si2)) mat = create_mono(target.get_space(si1),source.get_space(si2),[(0,0),],[kernellist[bindex],]) else: mat = create_empty(target.get_space(si1),source.get_space(si2)) matrices[si1].append(mat) #do an empty assembly for si1 in xrange(target.nspaces): for si2 in xrange(source.nspaces): if ((si1,si2) in blocklist): bindex = blocklist.index((si1,si2)) fill_mono(matrices[si1][si2],target.get_space(si1),source.get_space(si2),[(0,0),],[kernellist[bindex],],zeroassembly=True) #this catches bugs in pre-allocation and the initial assembly by locking the non-zero structure matrices[si1][si2].setOption(PETSc.Mat.Option.NEW_NONZERO_LOCATION_ERR, True) matrices[si1][si2].setOption(PETSc.Mat.Option.UNUSED_NONZERO_LOCATION_ERR, True) #These are for zeroRows- the first keeps the non-zero structure when zeroing rows, the 2nd tells PETSc that the process only zeros owned rows matrices[si1][si2].setOption(PETSc.Mat.Option.KEEP_NONZERO_PATTERN, True) matrices[si1][si2].setOption(PETSc.Mat.Option.NO_OFF_PROC_ZERO_ROWS, False) #create nest mat = PETSc.Mat().createNest(matrices,comm=PETSc.COMM_WORLD) #monolithic matrix if (mat_type == 'nest' and (target.nspaces == 1 and source.nspaces == 1)) or mat_type == 'aij': #create matrix mat = create_mono(target,source,blocklist,kernellist) #do an empty assembly fill_mono(mat,target,source,blocklist,kernellist,zeroassembly=True) mat.assemble() #this catches bugs in pre-allocation and the initial assembly by locking the non-zero structure mat.setOption(PETSc.Mat.Option.NEW_NONZERO_LOCATION_ERR, True) mat.setOption(PETSc.Mat.Option.UNUSED_NONZERO_LOCATION_ERR, True) mat.setOption(PETSc.Mat.Option.NEW_NONZERO_ALLOCATION_ERR, True) #These are for zeroRows- the first keeps the non-zero structure when zeroing rows, the 2nd tells PETSc that the process only zeros owned rows mat.setOption(PETSc.Mat.Option.KEEP_NONZERO_PATTERN, True) mat.setOption(PETSc.Mat.Option.NO_OFF_PROC_ZERO_ROWS, False) return mat