def gaussian_mesh_randomizer(mesh, percentage, preserve_boundary=True): """ Randomly perturb a given mesh. Args: mesh: Input mesh. percentage: Maximum perturbation in percentage of mesh.hmin(). preserve_boundary: Whether to move the vertices on the boundary. Returns: rmesh: The perturbed mesh. """ rmesh = Mesh(mesh) deltax = (np.random.randn(rmesh.num_vertices(), rmesh.geometry().dim()) * percentage * rmesh.hmin()) if preserve_boundary: boundary_mesh = BoundaryMesh(rmesh, "exterior") boundary_vertices = boundary_mesh.entity_map(0).array() deltax[boundary_vertices] = 0.0 rmesh.coordinates()[:] = rmesh.coordinates() + deltax return rmesh
def remap_from_files(prefix, ksdg=None): """remap dofs from multiprocess FunctionSpace to single process Required parameter: prefix -- the filename prefix for the fsinfo files Optional parameter: ksdg -- the KSDGSolver that defines the single process FunctionsSpace Returns a vector of integers that can be used to remap degrees of freedom on a distributed mesh to those on the local mesh, using numpy indirection, i.e. remap = remap_from_files(ksdg, local_mesh=lmesh) local_function.vector()[:] = global_function.vector()[:][remap] This function does the same thing as dofremap, but at a different time and a different way. Unlike dofremap, remap_from_files is called after a solution is finished, not while it is in progress. The information about the degrees of freedom is retrieved from h5 files created at the time of solution. If the solution was run as an MPI group on S processes, there remap_from_files consults S+1 files: prefix_mesh.xml.gz prefixrank0_fsinfo.h5 prefixrank1_fsinfo.h5 ... prefixrank<S-1>_fsinfo.h5 The mesh file contains the total mesh for the problem, as assembled by gather_mesh. Of the fsinfo files (which contain information about the function space local to each MPI process), at least the rank0 file must exist. Its 'size' entry is read to determine the number of fsinfo files, and its degree and dim entries are used to create a FunctionSpace on this mesh (by creating a KSDGSolver with those arguments). It then goes through the fsinfo files sequentially, determining the mapping from each one's DOFs to the DOFs pof the global FunctionSpace. Note: this works only for solutions with a single rho and a single U. """ if (ksdg): mesh = ksdg.mesh else: meshfile = prefix + '_mesh.xml.gz' mesh = Mesh(meshfile) dim = mesh.geometry().dim() r0name = fsinfo_filename(prefix, 0) with h5py.File(r0name, 'r') as fsf: size = fsf['/mpi/size'].value if (dim != fsf['dim'].value): raise KSDGException("mesh dimension = " + dim + ", FunctionSpace dim = " + fsf['dim']) try: degree = fsf['degree'].value except KeyError: degree = 3 try: periodic = fsf['periodic'].value except KeyError: periodic = False if not ksdg: from .ksdgmakesolver import makeKSDGSolver # circular import ksdg = makeKSDGSolver(mesh=mesh, degree=degree, project_initial_condition=False, periodic=periodic) owneddofs = [] ltg = [] dofcoords = [] dofs = [] rho_dofs = [] U_dofs = [] cell_dofs = [] cell_indices = [] # # collect info about each processes dofs from files # for rank in range(size): rname = fsinfo_filename(prefix, rank) with h5py.File(rname, 'r') as fsf: owneddofs.append(fsf['ownership_range'].value.copy()) ltg.append(fsf['tabulate_local_to_global_dofs'].value.copy()) dofcoords.append(fsf['dofcoords'].value[0].copy()) dofs.append(fsf['dofs'].value.copy()) U_dofs.append(fsf['U_dofs'].value.copy()) rho_dofs.append(fsf['rho_dofs'].value.copy()) cell_dofs.append(fsf['cell_dofs'].value.copy()) cell_indices.append(fsf['/mesh/cell_indices'].value.copy()) transform = integerify_transform(dofcoords) spacing = transform[0] base = transform[1] # # check against global info # owneddofs = np.array(owneddofs) gdofmap = ksdg.VS.dofmap() gdofrange = gdofmap.ownership_range() if gdofrange[0] != 0: raise KSDGException("First dof %d, expected 0", gdofrange[0]) fdofmin = owneddofs[:, 0].min() fdofmax = owneddofs[:, 1].max() if (fdofmin != gdofrange[0] or fdofmax != gdofrange[1]): raise KSDGException("dof mismatch: global %d - %d, files %d - %d" % (gdofrange[0], gdofrange[1], fdofmin, fdofmax)) fdofs = np.zeros((gdofrange[1], dim + 2)) # # combine file info into one list of file dofs # for rank in range(size): fdofs[rho_dofs[rank], 1] = 0.0 fdofs[U_dofs[rank], 1] = 1.0 ncells = cell_indices[rank].shape[0] dofspercell = cell_dofs[rank].shape[1] for cell in range(ncells): fdofs[ltg[rank][cell_dofs[rank][cell]], 0] = cell_indices[rank][cell] nlocal = owneddofs[rank, 1] - owneddofs[rank, 0] fdofs[ltg[rank][:nlocal], 2:] = dofcoords[rank] # # assemble global dof list # ifdofs = np.empty_like(fdofs, dtype=int) np.rint(fdofs[:, :2], out=ifdofs[:, :2], casting='unsafe') np.rint(fdofs[:, 2:] / spacing - base, out=ifdofs[:, 2:], casting='unsafe') gdofs = local_dofs(ksdg) gdofs = gdofs[:, :-1] igdofs = np.empty_like(gdofs, dtype=int) np.rint(gdofs[:, :2], out=igdofs[:, :2], casting='unsafe') np.rint(gdofs[:, 2:] / spacing - base, out=igdofs[:, 2:], casting='unsafe') # # DOFs belonging to R subspaces don't have cells or coordinates # Rdofs = np.array([], dtype=int) for ss in subspaces(fs): if isRsubspace(ss): Rdofs = np.append(Rdofs, ss.dofmap().dofs()) logGATHER('Rdofs', Rdofs) try: doflist[Rdofs - owneddofs[0], 1] = -2.0 doflist[Rdofs - owneddofs[0], 2:-1] = -float('inf') except IndexError: logGATHER('IndexError: Rdofs-owneddofs[0]', Rdofs - owneddofs[0]) return (remap_list(ifdofs, igdofs, fdofs, gdofs))