def compute(self, get): u = get(self.valuename) if u is None: return if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"): rank = len(u.ufl_shape) shape = u.ufl_shape mesh = u.ufl_domain()._ufl_cargo dIdomain = self.dI.ufl_domain() else: rank = u.rank() shape = u.shape() mesh = u.domain().data() dIdomain = self.dI.domain() mesh_id = mesh.id() # Find mesh/domain if isinstance(self.dI.subdomain_data(), MeshFunctionSizet): mf = self.dI.subdomain_data() mf_mesh_id = mf.mesh().id() if mf_mesh_id != mesh_id: mf = duplicate_meshfunction(mf, mesh) mesh = mf.mesh() self.dI = self.dI.reconstruct(domain=mesh, subdomain_data=mf) if dIdomain is None: self.dI = self.dI.reconstruct(domain=mesh) if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"): dIdomain = self.dI.ufl_domain() else: dIdomain = self.dI.domain() assert dIdomain is not None # Calculate volume if not hasattr(self, "volume"): self.volume = assemble(Constant(1) * self.dI) assert self.volume > 0 if rank == 0: value = assemble(u * self.dI) / self.volume elif rank == 1: value = [ assemble(u[i] * self.dI) / self.volume for i in xrange(u.value_size()) ] elif rank == 2: value = [] for i in xrange(shape[0]): for j in xrange(shape[1]): value.append(assemble(u[i, j] * self.dI) / self.volume) return value
def compute(self, get): u = get(self.valuename) if u is None: return None if not hasattr(self, "u"): self.before_first_compute(get) if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"): rank = len(u.ufl_shape) else: rank = u.rank() if rank > 0: u = u.split() U = [] for i, _u in enumerate(u): U.append(_interpolate(self.us[i], _u)) #U.append(self._ft.interpolate_nonmatching_mesh(_u, self.us.function_space())) MPI.barrier(mpi_comm_world()) self.assigner.assign(self.u, U) else: _interpolate(self.u, u) MPI.barrier(mpi_comm_world()) # FIXME: This gives a PETSc-error (VecCopy). Unnecessary interpolation used instead. #self.u.assign(U) #self.u.assign(interpolate(U, self.u.function_space())) return self.u
def before_first_compute(self, get): u = get(self.valuename) if u is None: return None V = u.function_space() element = V.ufl_element() #family = element.family() #degree = element.degree() spaces = SpacePool(self.mesh) FS = spaces.get_custom_space(element.family(), element.degree(), element.value_shape()) if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"): rank = len(u.ufl_shape) else: rank = u.rank() if rank > 0: FS_scalar = spaces.get_custom_space(element.family(), element.degree(), ()) self.assigner = FunctionAssigner(FS, [FS_scalar]*FS.num_sub_spaces()) self.us = [] for i in range(FS.num_sub_spaces()): self.us.append(Function(FS_scalar)) self.u = Function(FS, name=self.name)
def get_grad_space(self, V, family="auto", degree="auto", shape="auto"): """Get gradient space of FunctionSpace V. .. warning:: This is experimental and currently only designed to work with CG-spaces. """ element = V.ufl_element() if degree == "auto": degree = element.degree() - 1 if family == "auto": family = "DG" if family in ("CG", "Lagrange") and degree == 0: family = "DG" if shape == "auto": if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"): shape = grad(Coefficient(element)).ufl_shape else: shape = grad(Coefficient(element)).shape() DV = self.get_custom_space(family, degree, shape) return DV
def _update_metadata_file(self, field_name, data, t, timestep, save_as, metadata): "Update metadata shelve file from master process." if on_master_process(): if self._metadata_cache.get(field_name) is None: savedir = self.get_savedir(field_name) metadata_filename = os.path.join(savedir, 'metadata.db') #metadata_file = shelve.open(metadata_filename) metadata_file = shelve.open(metadata_filename) self._metadata_cache[field_name] = metadata_file metadata_file = self._metadata_cache[field_name] # Store some data the first time if "type" not in metadata_file and data is not None: # Data about type and formats metadata_file["type"] = type(data).__name__ metadata_file["saveformats"] = list( set(save_as + metadata_file.get("saveformats", []))) # Data about function space if isinstance(data, Function): if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"): element = data.ufl_element() else: element = data.element() metadata_file["element"] = repr(element, ) metadata_file["element_degree"] = repr(element.degree(), ) metadata_file["element_family"] = repr(element.family(), ) metadata_file["element_value_shape"] = repr( element.value_shape(), ) # Store some data each timestep metadata_file[str(timestep)] = metadata metadata_file[str(timestep)]["t"] = t
def __init__(self, mesh, facet_ids, bcs, params, ics=dict()): # input check: mesh assert isinstance(mesh, dlfn.Mesh) self._mesh = mesh self._space_dim = self._mesh.topology().dim() assert self._space_dim in (2, 3) # input check: facet_ids assert isinstance(facet_ids, dlfn.MeshFunctionSizet) self._facet_markers = facet_ids # input check: boundary conditions self._check_boundary_conditions(bcs) self._bcs = bcs # input check: parameters from parameters import ParameterHandler assert isinstance(params, ParameterHandler) self._parameters = params # input check: initial conditions assert isinstance(ics, dict) self._ics = ics # equation coefficients tmp = self._parameters.coefficients() self._coefficients = tuple([dlfn.Constant(t) for t in tmp]) # imex coefficients self._imex = IMEXCoefficients(self._parameters.imex_type) # gravity field import gravity_field as gf if params.gravity_type not in (gf.GravityType.radial, gf.GravityType.radial_linear): self._gravity = gf.get_gravity_field(self._space_dim, params.gravity_type) else: self._gravity = gf.get_gravity_field(self._space_dim, params.gravity_type, radii=params.radii) # initialize timestep as dolfin constant self._timestep = dlfn.Constant(1.0) self._timestep.assign(self._parameters.timestep) # runtime flags self._rebuild_matrices = True # helpful constants self._one = dlfn.Constant(1.0) self._omega = dlfn.Constant(1.0) self._rotation_vector = None if self._space_dim == 2: self._null_vector = dlfn.Constant((0.0, 0.0)) elif self._space_dim == 3: self._rotation_vector = dlfn.Constant((0.0, 0.0, 1.0)) self._null_vector = dlfn.Constant((0.0, 0.0, 0.0)) else: raise ValueError() print "The system's version of FEniCS is", dlfn.dolfin_version(), "."
def _init_measure(measure="default", cell_domains=None, facet_domains=None, indicator=None): assert cell_domains is None or facet_domains is None, "You can't specify both cell_domains or facet_domains" if cell_domains is not None: assert isinstance(cell_domains, (MeshFunctionSizet, MeshFunctionInt)) if facet_domains is not None: assert isinstance(facet_domains, (MeshFunctionSizet, MeshFunctionInt)) if (cell_domains and indicator is not None): if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"): dI = Measure("cell")(subdomain_data=cell_domains)(indicator) else: dI = Measure("cell")[cell_domains](indicator) elif (facet_domains and indicator is not None): if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"): dI = Measure("exterior_facet")( subdomain_data=facet_domains)(indicator) else: dI = Measure("exterior_facet")[facet_domains](indicator) elif measure == "default": if indicator is not None: cbc_warning( "Indicator specified, but no domains. Will dompute average over entire domain." ) dI = dx() elif isinstance(measure, Measure): dI = measure else: raise TypeError( "Unable to create a domain measure from provided domains or measure." ) return dI
def compute(self, get): u = get(self.valuename) if isinstance(u, Function): if not hasattr(self, "use_project"): self.before_first_compute(get) if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"): rank = len(u.ufl_shape) else: rank = u.rank() if rank == 0: self.f.vector().zero() self.f.vector().axpy(1.0, u.vector()) self.f.vector().abs() return self.f elif rank >= 1: if self.use_project: b = assemble(sqrt(inner(u, u)) * self.v * dx(None)) self.projection.solve(self.f.vector(), b) else: self.assigner.assign(self.subfuncs, u) self.f.vector().zero() for i in xrange(u.function_space().num_sub_spaces()): vec = self.subfuncs[i].vector() vec.apply('') self.f.vector().axpy(1.0, vec * vec) try: sqrt_in_place(self.f.vector()) except: r = self.f.vector().local_range() self.f.vector()[r[0]:r[1]] = np.sqrt( self.f.vector()[r[0]:r[1]]) self.f.vector().apply('') return self.f elif isinstance(u, Iterable) and all( isinstance(_u, Number) for _u in u): return np.sqrt(sum(_u**2 for _u in u)) elif isinstance(u, Number): return abs(u) else: # Don't know how to handle object cbc_warning( "Don't know how to calculate magnitude of object of type %s. Returning object." % type(u)) return u
def compute(self, get): u = get(self.valuename) if u is None: return None if not isinstance(u, Function): cbc_warning("Do not understand how to handle datatype %s" % str(type(u))) return None #if not hasattr(self, "restriction_map"): if not hasattr(self, "keys"): V = u.function_space() element = V.ufl_element() family = element.family() degree = element.degree() if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"): rank = len(u.ufl_shape) else: rank = u.rank() if rank == 0: FS = FunctionSpace(self.submesh, family, degree) elif rank == 1: FS = VectorFunctionSpace(self.submesh, family, degree) elif rank == 2: FS = TensorFunctionSpace(self.submesh, family, degree, symmetry={}) self.u = Function(FS) #self.restriction_map = restriction_map(V, FS) rmap = restriction_map(V, FS) self.keys = np.array(rmap.keys(), dtype=np.intc) self.values = np.array(rmap.values(), dtype=np.intc) self.temp_array = np.zeros(len(self.keys), dtype=np.float_) # The simple __getitem__, __setitem__ has been removed in dolfin 1.5.0. # The new cbcpost-method get_set_vector should be compatible with 1.4.0 and 1.5.0. #self.u.vector()[self.keys] = u.vector()[self.values] get_set_vector(self.u.vector(), self.keys, u.vector(), self.values, self.temp_array) return self.u
def get_set_vector(setvector, set_indices, getvector, get_indices, temp_array=None): """Equivalent of setvector[set_indices] = getvector[get_indices] for global indices (MPI-blocking). Pass temp_array to avoid initiation of array on call. """ #if dolfin_version() == "1.4.0": # setvector[set_indices] = getvector[get_indices] # return if not hasattr(get_set_vector, "cppmodule"): code = """ void get_set_vector(std::shared_ptr<GenericVector> set_vector, const Array<dolfin::la_index> & set_indices, std::shared_ptr<const GenericVector> get_vector, const Array<dolfin::la_index> & get_indices, const Array<double> & temp_array) { std::size_t N = set_indices.size(); // Perform a const_cast (unable to pass non-const Array through from python layer) Array<double> & temp_array2 = const_cast<Array<double> &>(temp_array); // Get and set using the non-const temp_array2 get_vector->get(temp_array2.data(), N, get_indices.data()); set_vector->set(temp_array2.data(), N, set_indices.data()); // Apply vector (MPI-part) set_vector->apply("insert"); } """ # Very minor change required for dolfin 1.4.0 #if dolfin_version() == "1.4.0": if LooseVersion(dolfin_version()) <= LooseVersion("1.4.0"): code = code.replace("get_vector->get", "get_vector->get_local") cbc_log(20, "Compiling get_set_vector.cppmodule") get_set_vector.cppmodule = compile_extension_module(code) assert len(set_indices) == len(get_indices) if temp_array is None: temp_array = zeros(len(set_indices), dtype=float_) assert len(temp_array) == len(set_indices) get_set_vector.cppmodule.get_set_vector(setvector, set_indices, getvector, get_indices, temp_array)
def _set_solver(self, operator, op_name=None): """ Set the solver of an operator """ if type(operator) is df.PETScMatrix: if df.dolfin_version() <= '1.6.0': solver = df.PETScLUSolver( 'mumps' if df.has_lu_solver_method('mumps') else 'default') else: solver = df.PETScLUSolver( self.mpi_comm, 'mumps' if df.has_lu_solver_method('mumps') else 'default') solver.set_operator(operator) solver.parameters['reuse_factorization'] = True if op_name == 'K': solver.parameters['symmetric'] = True else: import scipy.sparse.linalg as spsla solver = spsla.splu(operator.tocsc(copy=True)) return solver
def mesh_to_boundarymesh_dofmap(boundary, V, Vb, _should_own="cdof"): "Find the mapping from dofs on full mesh FS to dofs on boundarymesh FS" from dolfin import dolfin_version, MPI, mpi_comm_world #if dolfin_version() != '1.4.0' and MPI.size(mpi_comm_world()) > 1: # raise RuntimeError("mesh_to_boundarymesh_dofmap is currently not supported in parallel in version %s" %(dolfin_version())) assert V.ufl_element().family() == Vb.ufl_element().family() assert V.ufl_element().degree() == Vb.ufl_element().degree() assert _should_own in ["cdof", "bdof"] # Currently only CG1 and DG0 spaces are supported assert V.ufl_element().family() in ["Lagrange", "Discontinuous Lagrange"] if V.ufl_element().family() == "Discontinuous Lagrange": assert V.ufl_element().degree() == 0 else: assert V.ufl_element().degree() == 1 D = boundary.topology().dim() mesh = V.mesh() V_dm = V.dofmap() Vb_dm = Vb.dofmap() dofmap_to_boundary = {} # Extract maps from boundary to mesh vertex_map = boundary.entity_map(0) cell_map = boundary.entity_map(D) for i in xrange(len(cell_map)): boundary_cell = Cell(boundary, i) mesh_facet = Facet(mesh, cell_map[i]) mesh_cell_index = mesh_facet.entities(D + 1)[0] mesh_cell = Cell(mesh, mesh_cell_index) cell_dofs = V_dm.cell_dofs(mesh_cell_index) boundary_dofs = Vb_dm.cell_dofs(i) if V_dm.num_entity_dofs(0) > 0: for v_idx in boundary_cell.entities(0): mesh_v_idx = vertex_map[int(v_idx)] mesh_list_idx = where( mesh_cell.entities(0) == mesh_v_idx)[0][0] boundary_list_idx = where( boundary_cell.entities(0) == v_idx)[0][0] bdofs = boundary_dofs[Vb_dm.tabulate_entity_dofs( 0, boundary_list_idx)] cdofs = cell_dofs[V_dm.tabulate_entity_dofs(0, mesh_list_idx)] for bdof, cdof in zip(bdofs, cdofs): #if dolfin_version() in ["1.4.0+", "1.5.0", "1.6.0"]: if LooseVersion(dolfin_version()) > LooseVersion("1.4.0"): bdof = Vb_dm.local_to_global_index(bdof) cdof = V_dm.local_to_global_index(cdof) if _should_own == "cdof" and not (V_dm.ownership_range( )[0] <= cdof < V_dm.ownership_range()[1]): continue elif _should_own == "bdof" and not (Vb_dm.ownership_range( )[0] <= bdof < Vb_dm.ownership_range()[1]): continue else: dofmap_to_boundary[bdof] = cdof if V_dm.num_entity_dofs(D + 1) > 0 and V_dm.num_entity_dofs(0) == 0: bdofs = boundary_dofs[Vb_dm.tabulate_entity_dofs(D, 0)] cdofs = cell_dofs[V_dm.tabulate_entity_dofs(D + 1, 0)] for bdof, cdof in zip(bdofs, cdofs): #if dolfin_version() in ["1.4.0+", "1.5.0"]: if LooseVersion(dolfin_version()) > LooseVersion("1.4.0"): bdof = Vb_dm.local_to_global_index(bdof) cdof = V_dm.local_to_global_index(cdof) dofmap_to_boundary[bdof] = cdof return dofmap_to_boundary
def before_first_compute(self, get): u = get(self.valuename) if isinstance(u, Function): if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"): rank = len(u.ufl_shape) else: rank = u.rank() if rank == 0: self.f = Function(u.function_space()) elif rank >= 1: # Assume all subpaces are equal V = u.function_space().extract_sub_space([0]).collapse() mesh = V.mesh() el = V.ufl_element() self.f = Function(V) # Find out if we can operate directly on vectors, or if we have to use a projection # We can operate on vectors if all sub-dofmaps are ordered the same way # For simplicity, this is only tested for CG- or DG0-spaces # (this might always be true for these spaces, but better to be safe than sorry ) self.use_project = True if el.family() == "Lagrange" or (el.family() == "Discontinuous Lagrange" and el.degree() == 0): #dm = u.function_space().dofmap() dm0 = V.dofmap() self.use_project = False for i in xrange(u.function_space().num_sub_spaces()): Vi = u.function_space().extract_sub_space( [i]).collapse() dmi = Vi.dofmap() try: # For 1.6.0+ and newer diff = Vi.tabulate_dof_coordinates( ) - V.tabulate_dof_coordinates() except: # For 1.6.0 and older diff = dmi.tabulate_all_coordinates( mesh) - dm0.tabulate_all_coordinates(mesh) if len(diff) > 0: max_diff = max(abs(diff)) else: max_diff = 0.0 max_diff = MPI.max(mpi_comm_world(), max_diff) if max_diff > 1e-12: self.use_project = True break self.assigner = FunctionAssigner( [V] * u.function_space().num_sub_spaces(), u.function_space()) self.subfuncs = [ Function(V) for _ in range(u.function_space().num_sub_spaces()) ] # IF we have to use a projection, build projection matrix only once if self.use_project: self.v = TestFunction(V) M = assemble(inner(self.v, TrialFunction(V)) * dx) self.projection = KrylovSolver("cg", "default") self.projection.set_operator(M) elif isinstance(u, Iterable) and all( isinstance(_u, Number) for _u in u): pass elif isinstance(u, Number): pass else: # Don't know how to handle object cbc_warning( "Don't know how to calculate magnitude of object of type %s." % type(u))
def restriction_map(V, Vb, _all_coords=None, _all_coordsb=None): "Return a map between dofs in Vb to dofs in V. Vb's mesh should be a submesh of V's Mesh." if V.ufl_element().family( ) == "Discontinuous Lagrange" and V.ufl_element().degree() > 0: raise RuntimeError( "This function does not work for DG-spaces of degree >0 \ (several dofs associated with same point in same subspace)." ) if V.ufl_element().family() != "Lagrange": cbc_warning("This function is only tested for CG-spaces.") assert V.ufl_element().family() == Vb.ufl_element().family( ), "ufl elements differ in the two spaces" assert V.ufl_element().degree() == Vb.ufl_element().degree( ), "ufl elements differ in the two spaces" assert V.ufl_element().cell() == Vb.ufl_element().cell( ), "ufl elements differ in the two spaces" D = V.mesh().geometry().dim() # Recursively call this function if V has sub-spaces if V.num_sub_spaces() > 0: mapping = {} if MPI.size(mpi_comm_world()) == 1: if _all_coords is None: try: # For 1.6.0+ and newer all_coords = V.tabulate_dof_coordinates().reshape( V.dim(), D) all_coordsb = Vb.tabulate_dof_coordinates().reshape( Vb.dim(), D) except: # For 1.6.0 and older all_coords = V.dofmap().tabulate_all_coordinates( V.mesh()).reshape(V.dim(), D) all_coordsb = Vb.dofmap().tabulate_all_coordinates( Vb.mesh()).reshape(Vb.dim(), D) else: all_coords = _all_coords all_coordsb = _all_coordsb else: all_coords = None all_coordsb = None for i in range(V.num_sub_spaces()): mapping.update( restriction_map(V.sub(i), Vb.sub(i), all_coords, all_coordsb)) return mapping dm = V.dofmap() dmb = Vb.dofmap() N = len(dm.dofs()) Nb = len(dmb.dofs()) dofs = dm.dofs() # Extract coordinates of dofs if dm.is_view(): if _all_coords is not None: coords = _all_coords[V.dofmap().dofs()] else: try: # For 1.6.0+ and newer coords = V.collapse().tabulate_dof_coordinates().reshape(N, D) except: # For 1.6.0 and older coords = V.collapse().dofmap().tabulate_all_coordinates( V.mesh()).reshape(N, D) if _all_coordsb is not None: coordsb = _all_coordsb[Vb.dofmap().dofs()] else: try: # For 1.6.0+ and newer coordsb = Vb.collapse().tabulate_dof_coordinates().reshape( Nb, D) except: # For 1.6.0 and older coordsb = Vb.collapse().dofmap().tabulate_all_coordinates( Vb.mesh()).reshape(Nb, D) else: if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"): # For 1.6.0+ and newer coords = V.tabulate_dof_coordinates().reshape(N, D) coordsb = Vb.tabulate_dof_coordinates().reshape(Nb, D) else: # For 1.6.0 and older coords = V.dofmap().tabulate_all_coordinates(V.mesh()).reshape( N, D) coordsb = Vb.dofmap().tabulate_all_coordinates(Vb.mesh()).reshape( Nb, D) # Build KDTree to compute distances from coordinates in base kdtree = KDTree(coords) eps = 1e-12 mapping = {} request_dofs = np.array([]) distances, indices = kdtree.query(coordsb) for i, subdof in enumerate(dmb.dofs()): # Find closest dof in base #d, idx = kdtree.query(coordsb[i]) d, idx = distances[i], indices[i] if d < eps: # Dof found on this process, add to map dof = dofs[idx] assert subdof not in mapping mapping[subdof] = dof else: # Search for this dof on other processes add_dofs = np.hstack(([subdof], coordsb[i])) request_dofs = np.append(request_dofs, add_dofs) del distances del indices # Scatter all dofs not found on current process to all processes all_request_dofs = [None] * MPI.size(mpi_comm_world()) for j in xrange(MPI.size(mpi_comm_world())): all_request_dofs[j] = broadcast(request_dofs, j) # Re-order all requested dofs # Remove items coming from this process all_request_dofs[MPI.rank(mpi_comm_world())] = [] all_request_dofs = np.hstack(all_request_dofs) all_request_dofs = all_request_dofs.reshape( len(all_request_dofs) / (D + 1), D + 1) all_request_dofs = dict( zip(all_request_dofs[:, 0], all_request_dofs[:, 1:])) # Search this process for all dofs not found on same process as subdof for subdof, coordsbi in all_request_dofs.items(): subdof = int(subdof) # Find closest dof in base d, idx = kdtree.query(coordsbi) if d < eps: # Dof found on this process, add to map dof = dofs[idx] assert subdof not in mapping mapping[subdof] = dof return mapping
import dolfin as dlfn import mshr from dolfin import inner, grad, div, dot import numpy as np #============================================================================== # TODO: try to use MUMPS or UMFPACK instead of LUSolver # TODO: more diagnostic values # TODO: write output of sol00 sol0 for restart # TODO: implement restart #============================================================================== #dlfn.parameters["form_compiler"]["cpp_optimize"] = True #comm = dlfn.mpi_comm_world() #mpi_comm = dlfn.mpi_comm_world() #mpi_rank = dlfn.MPI.rank(mpi_comm) #print mpi_rank print "The system's version of FEniCS is ", dlfn.dolfin_version(), "." #============================================================================== # run time parameters t_end = 150. n_steps = 50000 output_frequency = 50 checkpoint_frequency = 1000 rms_frequency = 5 aspect_ratio = 0.35 print_time_stepping_coefficients = False use_assembler_method = True non_zero_initial_temperature = True #============================================================================== # refinements n_initial_refinements = 0 #==============================================================================
def _update_hdf5_file(self, field_name, saveformat, data, timestep, t): """Update hdf5 file with new data. This creates a hashed dataset within the dataset to save FunctionSpace information only once, and for all subsequent savings only the vector is saved and links are created to the FunctionSpace information. This ensures that the saving is fully compatible with restart and replay on an arbitrary number of processes. """ assert saveformat == "hdf5" fullname, metadata = self._get_datafile_name(field_name, saveformat, timestep) # Create "good enough" hash. This is done to avoid data corruption when restarted from # different number of processes, different distribution or different function space local_hash = sha1() local_hash.update( str(data.function_space().mesh().num_cells()).encode()) local_hash.update(str(data.function_space().ufl_element()).encode()) local_hash.update(str(data.function_space().dim()).encode()) local_hash.update(str(MPI.size(mpi_comm_world())).encode()) # Global hash (same on all processes), 10 digits long global_hash = MPI.sum(mpi_comm_world(), int(local_hash.hexdigest(), 16)) global_hash = str(int(global_hash % 1e10)).zfill(10) #key = (field_name, saveformat) #datafile = self._datafile_cache.get(key) #if datafile is None: # datafile = HDF5File(mpi_comm_world(), fullname, 'w') # self._datafile_cache[key] = datafile # Open HDF5File if not os.path.isfile(fullname): datafile = HDF5File(mpi_comm_world(), fullname, 'w') else: datafile = HDF5File(mpi_comm_world(), fullname, 'a') # Write to hash-dataset if not yet done if not datafile.has_dataset(global_hash) or not datafile.has_dataset( global_hash + "/" + field_name): datafile.write(data, str(global_hash) + "/" + field_name) if not datafile.has_dataset("Mesh"): datafile.write(data.function_space().mesh(), "Mesh") # Write vector to file # TODO: Link vector when function has been written to hash datafile.write(data.vector(), field_name + str(timestep) + "/vector") # HDF5File.close is broken in 1.4 if dolfin_version() == "1.4.0+": datafile.close() del datafile # Link information about function space from hash-dataset hdf5_link(fullname, str(global_hash) + "/" + field_name + "/x_cell_dofs", field_name + str(timestep) + "/x_cell_dofs") hdf5_link(fullname, str(global_hash) + "/" + field_name + "/cell_dofs", field_name + str(timestep) + "/cell_dofs") hdf5_link(fullname, str(global_hash) + "/" + field_name + "/cells", field_name + str(timestep) + "/cells") return metadata
def create_submesh(mesh, markers, marker): "This function allows for a SubMesh-equivalent to be created in parallel" # Build mesh submesh = Mesh() mesh_editor = MeshEditor() mesh_editor.open(submesh, mesh.ufl_cell().cellname(), mesh.ufl_cell().topological_dimension(), mesh.ufl_cell().geometric_dimension()) # Return empty mesh if no matching markers if MPI.sum(mpi_comm_world(), int(marker in markers.array())) == 0: cbc_warning( "Unable to find matching markers in meshfunction. Submesh is empty." ) mesh_editor.close() return submesh base_cell_indices = np.where(markers.array() == marker)[0] base_cells = mesh.cells()[base_cell_indices] base_vertex_indices = np.unique(base_cells.flatten()) base_global_vertex_indices = sorted( [mesh.topology().global_indices(0)[vi] for vi in base_vertex_indices]) gi = mesh.topology().global_indices(0) shared_local_indices = set(base_vertex_indices).intersection( set(mesh.topology().shared_entities(0).keys())) shared_global_indices = [gi[vi] for vi in shared_local_indices] unshared_global_indices = list( set(base_global_vertex_indices) - set(shared_global_indices)) unshared_vertices_dist = distribution(len(unshared_global_indices)) # Number unshared vertices on separate process idx = sum(unshared_vertices_dist[:MPI.rank(mpi_comm_world())]) base_to_sub_global_indices = {} for gi in unshared_global_indices: base_to_sub_global_indices[gi] = idx idx += 1 # Gather all shared process on process 0 and assign global index all_shared_global_indices = gather(shared_global_indices, on_process=0, flatten=True) all_shared_global_indices = np.unique(all_shared_global_indices) shared_base_to_sub_global_indices = {} idx = int( MPI.max(mpi_comm_world(), float(max(base_to_sub_global_indices.values() + [-1e16]))) + 1) if MPI.rank(mpi_comm_world()) == 0: for gi in all_shared_global_indices: shared_base_to_sub_global_indices[int(gi)] = idx idx += 1 # Broadcast global numbering of all shared vertices shared_base_to_sub_global_indices = dict( zip(broadcast(shared_base_to_sub_global_indices.keys(), 0), broadcast(shared_base_to_sub_global_indices.values(), 0))) # Join shared and unshared numbering in one dict base_to_sub_global_indices = dict( base_to_sub_global_indices.items() + shared_base_to_sub_global_indices.items()) # Create mapping of local indices base_to_sub_local_indices = dict( zip(base_vertex_indices, range(len(base_vertex_indices)))) # Define sub-cells sub_cells = [None] * len(base_cells) for i, c in enumerate(base_cells): sub_cells[i] = [base_to_sub_local_indices[j] for j in c] # Store vertices as sub_vertices[local_index] = (global_index, coordinates) sub_vertices = {} for base_local, sub_local in base_to_sub_local_indices.items(): sub_vertices[sub_local] = (base_to_sub_global_indices[ mesh.topology().global_indices(0)[base_local]], mesh.coordinates()[base_local]) ## Done with base mesh # Distribute meshdata on (if any) empty processes sub_cells, sub_vertices = distribute_meshdata(sub_cells, sub_vertices) global_cell_distribution = distribution(len(sub_cells)) #global_vertex_distribution = distribution(len(sub_vertices)) global_num_cells = MPI.sum(mpi_comm_world(), len(sub_cells)) global_num_vertices = sum(unshared_vertices_dist) + MPI.sum( mpi_comm_world(), len(all_shared_global_indices)) mesh_editor.init_vertices(len(sub_vertices)) #mesh_editor.init_cells(len(sub_cells)) mesh_editor.init_cells_global(len(sub_cells), global_num_cells) global_index_start = sum( global_cell_distribution[:MPI.rank(mesh.mpi_comm())]) for index, cell in enumerate(sub_cells): if LooseVersion(dolfin_version()) >= LooseVersion("1.6.0"): mesh_editor.add_cell(index, *cell) else: mesh_editor.add_cell(int(index), global_index_start + index, np.array(cell, dtype=np.uintp)) for local_index, (global_index, coordinates) in sub_vertices.items(): #print coordinates mesh_editor.add_vertex_global(int(local_index), int(global_index), coordinates) mesh_editor.close() submesh.topology().init(0, len(sub_vertices), global_num_vertices) submesh.topology().init(mesh.ufl_cell().topological_dimension(), len(sub_cells), global_num_cells) # FIXME: Set up shared entities # What damage does this do? submesh.topology().shared_entities(0)[0] = [] # The code below sets up shared vertices, but lacks shared facets. # It is considered incomplete, and therefore commented out ''' #submesh.topology().shared_entities(0)[0] = [] from dolfin import compile_extension_module cpp_code = """ void set_shared_entities(Mesh& mesh, std::size_t idx, const Array<std::size_t>& other_processes) { std::set<unsigned int> set_other_processes; for (std::size_t i=0; i<other_processes.size(); i++) { set_other_processes.insert(other_processes[i]); //std::cout << idx << " --> " << other_processes[i] << std::endl; } //std::cout << idx << " --> " << set_other_processes[0] << std::endl; mesh.topology().shared_entities(0)[idx] = set_other_processes; } """ set_shared_entities = compile_extension_module(cpp_code).set_shared_entities base_se = mesh.topology().shared_entities(0) se = submesh.topology().shared_entities(0) for li in shared_local_indices: arr = np.array(base_se[li], dtype=np.uintp) sub_li = base_to_sub_local_indices[li] set_shared_entities(submesh, base_to_sub_local_indices[li], arr) ''' return submesh