def reset_regions(self): """ Reset the list of regions associated with the domain. """ self.regions = OneTypeList(Region) self._region_stack = [] self._bnf = create_bnf(self._region_stack)
def from_conf(conf, variables, regions, materials, integrals, user=None, verbose=True): objs = OneTypeList(Equation) conf = copy(conf) ii = 0 for name, desc in conf.iteritems(): if verbose: output('equation "%s":' % name) output(desc) eq = Equation.from_desc(name, desc, variables, regions, materials, integrals, user=user) objs.append(eq) ii += 1 obj = Equations(objs) return obj
def from_conf(conf): objs = OneTypeList(Function) for key, fc in conf.iteritems(): fun = Function(name=fc.name, function=fc.function, is_constant=False, extra_args={}) objs.append(fun) obj = Functions(objs) return obj
def from_conf(conf): objs = OneTypeList(Function) for key, fc in conf.iteritems(): fun = Function(name = fc.name, function = fc.function, is_constant = False, extra_args = {}) objs.append(fun) obj = Functions(objs) return obj
def from_file_hdf5(filename, var_names): """TODO: do not read entire file, provide data on demand.""" io = HDF5MeshIO(filename) ts = TimeStepper(*io.read_time_stepper()) steps = nm.arange(ts.n_step, dtype=nm.int32) ths = io.read_variables_time_history(var_names, ts) objs = OneTypeList(History) for name, th in ths.iteritems(): hist = History(name, steps=steps, times=ts.times, th=th) objs.append(hist) obj = Histories(objs, dt=ts.dt, name=' '.join(var_names)) return obj
def from_conf(conf, functions, wanted=None): """ Construct Materials instance from configuration. """ if wanted is None: wanted = conf.keys() objs = OneTypeList(Material) for key, mc in conf.iteritems(): if key not in wanted: continue mat = Material.from_conf(mc, functions) objs.append(mat) obj = Materials(objs) return obj
def from_conf(conf, functions, wanted=None): """ Construct Materials instance from configuration. """ if wanted is None: wanted = list(conf.keys()) objs = OneTypeList(Material) for key, mc in six.iteritems(conf): if key not in wanted: continue mat = Material.from_conf(mc, functions) objs.append(mat) obj = Materials(objs) return obj
def from_conf(conf): objs = OneTypeList(Integral) for desc in conf.itervalues(): if hasattr(desc, 'vals'): aux = Integral(desc.name, coors=desc.vals, weights=desc.weights) else: aux = Integral(desc.name, order=desc.order) objs.append(aux) obj = Integrals(objs) return obj
def semideep_copy(self): """Copy materials, while external data (e.g. region) remain shared.""" others = copy(self) others.update(OneTypeList(Material)) for mat in self: other = mat.copy(name=mat.name) other.reset() others.append(other) return others
def from_file_hdf5( filename, var_names ): """TODO: do not read entire file, provide data on demand.""" io = HDF5MeshIO( filename ) ts = TimeStepper( *io.read_time_stepper() ) steps = nm.arange( ts.n_step, dtype = nm.int32 ) ths = io.read_variables_time_history( var_names, ts ) objs = OneTypeList( History ) for name, th in six.iteritems(ths): hist = History( name, steps = steps, times = ts.times, th = th ) objs.append( hist ) obj = Histories( objs, dt = ts.dt, name = ' '.join( var_names ) ) return obj
def read_spline_box_hdf5(filename): if not pt.isHDF5File(filename): raise ValueError, 'not a HDF5 file! (%s)' % filename fd = pt.openFile(filename, mode='r') boxes = fd.listNodes('/box') n_box = len(boxes) dim = len(fd.listNodes(boxes[0].ax)) sp_boxes = SplineBoxes(dim=dim, n_box=n_box, n_vertex=0, spbs=OneTypeList(SplineBox)) for box in boxes: spb = SplineBox() sp_boxes.spbs.append(spb) spb.ib = int(box._v_name) spb.cpi = nm.asarray(box.cpi.read()) - 1 spb.gpi = nm.asarray(box.gpi.read()) - 1 spb.cxyz = nm.asarray(box.cxyz.read()).transpose() spb.cxyz0 = spb.cxyz.copy() spb.ax = [] for axi in fd.listNodes(box.ax): spb.ax.append(nm.asarray(axi.bsc.read())) sp_boxes.n_vertex = max(sp_boxes.n_vertex, nm.amax(spb.gpi) + 1) print nm.amin(spb.gpi), nm.amax(spb.gpi) ## # Fix cpi by rebuilding :). off = 0 n0, n1, n2 = spb.cpi.shape aux = nm.arange(n0 * n1).reshape(n1, n0).transpose() for ii in xrange(n2): spb.cpi[:, :, ii] = aux + off off += n0 * n1 fd.close() for perm in cycle([n_box] * 2): if perm[0] == perm[1]: continue gpi1 = sp_boxes.spbs[perm[0]].gpi gpi2 = sp_boxes.spbs[perm[1]].gpi assert_(len(nm.intersect1d(gpi1, gpi2)) == 0) return sp_boxes
class Domain(Struct): """ Domain is divided into groups, whose purpose is to have homogeneous data shapes.""" def __init__(self, name, mesh, verbose=False): """Create a Domain. Parameters ---------- name : str Object name. mesh : Mesh A mesh defining the domain. """ geom_els = {} for ig, desc in enumerate(mesh.descs): gel = GeometryElement(desc) # Create geometry elements of dimension - 1. gel.create_surface_facet() geom_els[desc] = gel interps = {} for gel in geom_els.itervalues(): key = gel.get_interpolation_name() gel.interp = interps.setdefault(key, fea.Interpolant(key, gel)) gel = gel.surface_facet if gel is not None: key = gel.get_interpolation_name() gel.interp = interps.setdefault(key, fea.Interpolant(key, gel)) Struct.__init__(self, name=name, mesh=mesh, geom_els=geom_els, geom_interps=interps) self.mat_ids_to_i_gs = {} for ig, mat_id in enumerate(mesh.mat_ids): self.mat_ids_to_i_gs[mat_id[0]] = ig self.setup_groups() self.fix_element_orientation() self.setup_facets(verbose=verbose) self.reset_regions() self.clear_surface_groups() def setup_groups(self): n_nod, dim = self.mesh.coors.shape self.shape = Struct(n_gr=len(self.mesh.conns), n_el=0, n_nod=n_nod, dim=dim) self.groups = {} for ii in range(self.shape.n_gr): gel = self.geom_els[self.mesh.descs[ii]] # Shortcut. conn = self.mesh.conns[ii] vertices = nm.unique(conn) n_vertex = vertices.shape[0] n_el, n_ep = conn.shape n_edge = gel.n_edge n_edge_total = n_edge * n_el if gel.dim == 3: n_face = gel.n_face n_face_total = n_face * n_el else: n_face = n_face_total = 0 shape = Struct(n_vertex=n_vertex, n_el=n_el, n_ep=n_ep, n_edge=n_edge, n_edge_total=n_edge_total, n_face=n_face, n_face_total=n_face_total, dim=self.mesh.dims[ii]) self.groups[ii] = Struct(ig=ii, vertices=vertices, conn=conn, gel=gel, shape=shape) self.shape.n_el += n_el ## # c: 22.11.2007, r: 25.03.2008 def iter_groups(self, igs=None): if igs is None: for ig in xrange(self.shape.n_gr): # sorted by ig. yield self.groups[ig] else: for ig in igs: yield ig, self.groups[ig] ## # c: 25.03.2008, r: 25.03.2008 def get_cell_offsets(self): offs = {} off = 0 for group in self.iter_groups(): ig = group.ig offs[ig] = off off += group.shape.n_el return offs def get_mesh_coors(self, actual=False): """ Return the coordinates of the underlying mesh vertices. """ if actual and hasattr(self.mesh, 'coors_act'): return self.mesh.coors_act else: return self.mesh.coors def get_mesh_bounding_box(self): """ Return the bounding box of the underlying mesh. Returns ------- bbox : ndarray (2, dim) The bounding box with min. values in the first row and max. values in the second row. """ return self.mesh.get_bounding_box() def get_diameter(self): """ Return the diameter of the domain. Notes ----- The diameter corresponds to the Friedrichs constant. """ bbox = self.get_mesh_bounding_box() return (bbox[1, :] - bbox[0, :]).max() def get_conns(self): """ Return the element connectivity groups of the underlying mesh. """ return self.mesh.conns def fix_element_orientation(self): """ Ensure element nodes ordering giving positive element volume. The groups with elements of lower dimension than the space dimension are skipped. """ from extmods.mesh import orient_elements coors = self.mesh.coors for ii, group in self.groups.iteritems(): if group.shape.dim < self.shape.dim: continue ori, conn = group.gel.orientation, group.conn itry = 0 while itry < 2: flag = -nm.ones(conn.shape[0], dtype=nm.int32) # Changes orientation if it is wrong according to swap*! # Changes are indicated by positive flag. orient_elements(flag, conn, coors, ori.roots, ori.vecs, ori.swap_from, ori.swap_to) if nm.alltrue(flag == 0): if itry > 0: output('...corrected') itry = -1 break output( 'warning: bad element orientation, trying to correct...') itry += 1 if itry == 2 and flag[0] != -1: raise RuntimeError('elements cannot be oriented! (%d, %s)' % (ii, self.mesh.descs[ii])) elif flag[0] == -1: output('warning: element orienation not checked') def has_faces(self): return sum([group.shape.n_face for group in self.iter_groups()]) > 0 def setup_facets(self, create_edges=True, create_faces=True, verbose=False): """ Setup the edges and faces (in 3D) of domain elements. """ kinds = ['edges', 'faces'] is_face = self.has_faces() create = [create_edges, create_faces and is_face] for ii, kind in enumerate(kinds): if create[ii]: if verbose: output('setting up domain %s...' % kind) tt = time.clock() obj = Facets.from_domain(self, kind) obj.sort_and_orient() obj.setup_unique() obj.setup_neighbours() # 'ed' or 'fa' setattr(self, kind[:2], obj) if verbose: output('...done in %.2f s' % (time.clock() - tt)) if not is_face: self.fa = None def get_facets(self, force_faces=False): """ Return edge and face descriptions. """ if force_faces and not self.fa: return self.ed, self.ed else: return self.ed, self.fa def reset_regions(self): """Reset the list of regions associated with the domain.""" self.regions = OneTypeList(Region) self._region_stack = [] self._bnf = create_bnf(self._region_stack) def create_region(self, name, select, flags=None, check_parents=True, functions=None, add_to_regions=True): """Region factory constructor. Append the new region to self.regions list.""" if flags is None: flags = {} if check_parents: parents = get_parents(select) for p in parents: if p not in [region.name for region in self.regions]: msg = 'parent region %s of %s not found!' % (p, name) raise ValueError(msg) stack = self._region_stack try: self._bnf.parseString(select) except ParseException: print 'parsing failed:', select raise region = visit_stack( stack, region_op, region_leaf(self, self.regions, select, functions)) region.name = name forbid = flags.get('forbid', None) if forbid: fb = re.compile('^group +\d+(\s+\d+)*$').match(forbid) if fb: groups = forbid[5:].strip().split() forbid = [int(ii) for ii in groups] else: raise ValueError('bad forbid! (%s)' % forbid) forbidden_igs = [self.mat_ids_to_i_gs[mat_id] for mat_id in forbid] region.delete_groups(forbidden_igs) region.switch_cells(flags.get('can_cells', True)) region.complete_description(self.ed, self.fa) if add_to_regions: self.regions.append(region) return region def create_regions(self, region_defs, functions=None): output('creating regions...') tt = time.clock() self.reset_regions() ## # Sort region definitions by dependencies. graph, name_to_sort_name = get_dependency_graph(region_defs) sorted_regions = sort_by_dependency(graph) ## print sorted_regions ## # Define regions. for name in sorted_regions: sort_name = name_to_sort_name[name] rdef = region_defs[sort_name] region = self.create_region(name, rdef.select, flags=rdef, check_parents=False, functions=functions) output(' ', region.name) output('...done in %.2f s' % (time.clock() - tt)) return self.regions ## # 31.07.2007, c def get_element_diameters(self, ig, cells, vg, mode, square=True): group = self.groups[ig] diameters = nm.empty((len(cells), 1, 1, 1), dtype=nm.float64) if vg is None: diameters.fill(1.0) else: vg.get_element_diameters(diameters, group.gel.edges, self.get_mesh_coors().copy(), group.conn, cells, mode) if square: out = diameters.squeeze() else: out = nm.sqrt(diameters.squeeze()) return out ## # 29.08.2007, re-c from 00.01.18, r: 26.03.2008 def surface_faces(self): if not self.fa: print "no faces defined!" raise ValueError fa = self.fa flag = fa.mark_surface_facets() surf_faces = [] itri = nm.where(flag == 3)[0] if itri.size: surf_faces.append(fa.facets[itri, :3]) itet = nm.where(flag == 4)[0] if itet.size: surf_faces.append(fa.facets[itet, :4]) isurf = nm.where(flag >= 1)[0] if isurf.size: lst = fa.indices[isurf] return lst, surf_faces def clear_surface_groups(self): """ Remove surface group data. """ self.surface_groups = {} def create_surface_group(self, region): """ Create a new surface group corresponding to `region` if it does not exist yet. Notes ----- Surface groups define surface facet connectivity that is needed for :class:`sfepy.fem.mappings.SurfaceMapping`. """ for ig in region.igs: groups = self.surface_groups.setdefault(ig, {}) if region.name not in groups: region.select_cells_of_surface(reset=False) group = self.groups[ig] gel_faces = group.gel.get_surface_entities() name = 'surface_group_%s_%d' % (region.name, ig) surface_group = FESurface(name, region, gel_faces, group.conn, ig) groups[region.name] = surface_group def refine(self): """ Uniformly refine the domain mesh. Returns ------- domain : Domain instance The new domain with the refined mesh. Notes ----- Works only for meshes with single element type! Does not preserve node groups! """ names = set() for group in self.groups.itervalues(): names.add(group.gel.name) if len(names) != 1: msg = 'refine() works only for meshes with single element type!' raise NotImplementedError(msg) el_type = names.pop() if el_type == '2_3': mesh = refine_2_3(self.mesh, self.ed) elif el_type == '2_4': mesh = refine_2_4(self.mesh, self.ed) elif el_type == '3_4': mesh = refine_3_4(self.mesh, self.ed) elif el_type == '3_8': mesh = refine_3_8(self.mesh, self.ed, self.fa) else: msg = 'unsupported element type! (%s)' % el_type raise NotImplementedError(msg) domain = Domain(self.name + '_r', mesh) return domain
class Domain( Struct ): """ Domain is divided into groups, whose purpose is to have homogeneous data shapes.""" def __init__(self, name, mesh, verbose=False): """Create a Domain. Parameters ---------- name : str Object name. mesh : Mesh A mesh defining the domain. """ geom_els = {} for ig, desc in enumerate(mesh.descs): gel = GeometryElement(desc) # Create geometry elements of dimension - 1. gel.create_surface_facet() geom_els[desc] = gel interps = {} for gel in geom_els.itervalues(): key = gel.get_interpolation_name() gel.interp = interps.setdefault(key, fea.Interpolant(key, gel)) gel = gel.surface_facet if gel is not None: key = gel.get_interpolation_name() gel.interp = interps.setdefault(key, fea.Interpolant(key, gel)) Struct.__init__(self, name = name, mesh = mesh, geom_els = geom_els, geom_interps = interps) self.mat_ids_to_i_gs = {} for ig, mat_id in enumerate( mesh.mat_ids ): self.mat_ids_to_i_gs[mat_id[0]] = ig self.setup_groups() self.fix_element_orientation() self.setup_facets(verbose=verbose) self.reset_regions() self.clear_surface_groups() def setup_groups( self ): n_nod, dim = self.mesh.coors.shape self.shape = Struct( n_gr = len( self.mesh.conns ), n_el = 0, n_nod = n_nod, dim = dim ) self.groups = {} for ii in range( self.shape.n_gr ): gel = self.geom_els[self.mesh.descs[ii]] # Shortcut. conn = self.mesh.conns[ii] vertices = nm.unique( conn ) n_vertex = vertices.shape[0] n_el, n_ep = conn.shape n_edge = gel.n_edge n_edge_total = n_edge * n_el if gel.dim == 3: n_face = gel.n_face n_face_total = n_face * n_el else: n_face = n_face_total = 0 shape = Struct(n_vertex=n_vertex, n_el=n_el, n_ep=n_ep, n_edge=n_edge, n_edge_total=n_edge_total, n_face=n_face, n_face_total=n_face_total, dim=self.mesh.dims[ii]) self.groups[ii] = Struct( ig = ii, vertices = vertices, conn = conn, gel = gel, shape = shape ) self.shape.n_el += n_el ## # c: 22.11.2007, r: 25.03.2008 def iter_groups( self, igs = None ): if igs is None: for ig in xrange( self.shape.n_gr ): # sorted by ig. yield self.groups[ig] else: for ig in igs: yield ig, self.groups[ig] ## # c: 25.03.2008, r: 25.03.2008 def get_cell_offsets( self ): offs = {} off = 0 for group in self.iter_groups(): ig = group.ig offs[ig] = off off += group.shape.n_el return offs def get_mesh_coors(self, actual=False): """ Return the coordinates of the underlying mesh vertices. """ if actual and hasattr(self.mesh, 'coors_act'): return self.mesh.coors_act else: return self.mesh.coors def get_mesh_bounding_box(self): """ Return the bounding box of the underlying mesh. Returns ------- bbox : ndarray (2, dim) The bounding box with min. values in the first row and max. values in the second row. """ return self.mesh.get_bounding_box() def get_diameter(self): """ Return the diameter of the domain. Notes ----- The diameter corresponds to the Friedrichs constant. """ bbox = self.get_mesh_bounding_box() return (bbox[1,:] - bbox[0,:]).max() def get_conns(self): """ Return the element connectivity groups of the underlying mesh. """ return self.mesh.conns def fix_element_orientation(self): """ Ensure element nodes ordering giving positive element volume. The groups with elements of lower dimension than the space dimension are skipped. """ from extmods.mesh import orient_elements coors = self.mesh.coors for ii, group in self.groups.iteritems(): if group.shape.dim < self.shape.dim: continue ori, conn = group.gel.orientation, group.conn itry = 0 while itry < 2: flag = -nm.ones(conn.shape[0], dtype=nm.int32) # Changes orientation if it is wrong according to swap*! # Changes are indicated by positive flag. orient_elements(flag, conn, coors, ori.roots, ori.vecs, ori.swap_from, ori.swap_to) if nm.alltrue( flag == 0 ): if itry > 0: output('...corrected') itry = -1 break output('warning: bad element orientation, trying to correct...') itry += 1 if itry == 2 and flag[0] != -1: raise RuntimeError('elements cannot be oriented! (%d, %s)' % (ii, self.mesh.descs[ii])) elif flag[0] == -1: output('warning: element orienation not checked') def has_faces( self ): return sum( [group.shape.n_face for group in self.iter_groups()] ) > 0 def setup_facets(self, create_edges=True, create_faces=True, verbose=False): """ Setup the edges and faces (in 3D) of domain elements. """ kinds = ['edges', 'faces'] is_face = self.has_faces() create = [create_edges, create_faces and is_face] for ii, kind in enumerate(kinds): if create[ii]: if verbose: output('setting up domain %s...' % kind) tt = time.clock() obj = Facets.from_domain(self, kind) obj.sort_and_orient() obj.setup_unique() obj.setup_neighbours() # 'ed' or 'fa' setattr(self, kind[:2], obj) if verbose: output('...done in %.2f s' % (time.clock() - tt)) if not is_face: self.fa = None def get_facets(self, force_faces=False): """ Return edge and face descriptions. """ if force_faces and not self.fa: return self.ed, self.ed else: return self.ed, self.fa def reset_regions(self): """Reset the list of regions associated with the domain.""" self.regions = OneTypeList(Region) self._region_stack = [] self._bnf = create_bnf(self._region_stack) def create_region(self, name, select, flags=None, check_parents=True, functions=None, add_to_regions=True): """Region factory constructor. Append the new region to self.regions list.""" if flags is None: flags = {} if check_parents: parents = get_parents(select) for p in parents: if p not in [region.name for region in self.regions]: msg = 'parent region %s of %s not found!' % (p, name) raise ValueError(msg) stack = self._region_stack try: self._bnf.parseString(select) except ParseException: print 'parsing failed:', select raise region = visit_stack(stack, region_op, region_leaf(self, self.regions, select, functions)) region.name = name forbid = flags.get('forbid', None) if forbid: fb = re.compile('^group +\d+(\s+\d+)*$').match(forbid) if fb: groups = forbid[5:].strip().split() forbid = [int( ii ) for ii in groups] else: raise ValueError('bad forbid! (%s)' % forbid) forbidden_igs = [self.mat_ids_to_i_gs[mat_id] for mat_id in forbid] region.delete_groups(forbidden_igs) region.switch_cells(flags.get('can_cells', True)) region.complete_description(self.ed, self.fa) if add_to_regions: self.regions.append(region) return region def create_regions(self, region_defs, functions=None): output( 'creating regions...' ) tt = time.clock() self.reset_regions() ## # Sort region definitions by dependencies. graph, name_to_sort_name = get_dependency_graph(region_defs) sorted_regions = sort_by_dependency(graph) ## print sorted_regions ## # Define regions. for name in sorted_regions: sort_name = name_to_sort_name[name] rdef = region_defs[sort_name] region = self.create_region(name, rdef.select, flags=rdef, check_parents=False, functions=functions) output(' ', region.name) output( '...done in %.2f s' % (time.clock() - tt) ) return self.regions ## # 31.07.2007, c def get_element_diameters( self, ig, cells, vg, mode, square = True ): group = self.groups[ig] diameters = nm.empty( (len( cells ), 1, 1, 1), dtype = nm.float64 ) if vg is None: diameters.fill( 1.0 ) else: vg.get_element_diameters( diameters, group.gel.edges, self.get_mesh_coors().copy(), group.conn, cells, mode ) if square: out = diameters.squeeze() else: out = nm.sqrt( diameters.squeeze() ) return out ## # 29.08.2007, re-c from 00.01.18, r: 26.03.2008 def surface_faces( self ): if not self.fa: print "no faces defined!" raise ValueError fa = self.fa flag = fa.mark_surface_facets() surf_faces = [] itri = nm.where(flag == 3)[0] if itri.size: surf_faces.append( fa.facets[itri,:3] ) itet = nm.where(flag == 4)[0] if itet.size: surf_faces.append( fa.facets[itet,:4] ) isurf = nm.where( flag >= 1 )[0] if isurf.size: lst = fa.indices[isurf] return lst, surf_faces def clear_surface_groups(self): """ Remove surface group data. """ self.surface_groups = {} def create_surface_group(self, region): """ Create a new surface group corresponding to `region` if it does not exist yet. Notes ----- Surface groups define surface facet connectivity that is needed for :class:`sfepy.fem.mappings.SurfaceMapping`. """ for ig in region.igs: groups = self.surface_groups.setdefault(ig, {}) if region.name not in groups: region.select_cells_of_surface(reset=False) group = self.groups[ig] gel_faces = group.gel.get_surface_entities() name = 'surface_group_%s_%d' % (region.name, ig) surface_group = FESurface(name, region, gel_faces, group.conn, ig) groups[region.name] = surface_group def refine(self): """ Uniformly refine the domain mesh. Returns ------- domain : Domain instance The new domain with the refined mesh. Notes ----- Works only for meshes with single element type! Does not preserve node groups! """ names = set() for group in self.groups.itervalues(): names.add(group.gel.name) if len(names) != 1: msg = 'refine() works only for meshes with single element type!' raise NotImplementedError(msg) el_type = names.pop() if el_type == '2_3': mesh = refine_2_3(self.mesh, self.ed) elif el_type == '2_4': mesh = refine_2_4(self.mesh, self.ed) elif el_type == '3_4': mesh = refine_3_4(self.mesh, self.ed) elif el_type == '3_8': mesh = refine_3_8(self.mesh, self.ed, self.fa) else: msg = 'unsupported element type! (%s)' % el_type raise NotImplementedError(msg) domain = Domain(self.name + '_r', mesh) return domain
class Domain(Struct): def __init__(self, name, mesh=None, nurbs=None, bmesh=None, regions=None, verbose=False): Struct.__init__(self, name=name, mesh=mesh, nurbs=nurbs, bmesh=bmesh, regions=regions, verbose=verbose) def get_centroids(self, dim): """ Return the coordinates of centroids of mesh entities with dimension `dim`. """ return self.cmesh.get_centroids(dim) def has_faces(self): return self.shape.tdim == 3 def reset_regions(self): """ Reset the list of regions associated with the domain. """ self.regions = OneTypeList(Region) self._region_stack = [] self._bnf = create_bnf(self._region_stack) def create_region(self, name, select, kind='cell', parent=None, check_parents=True, functions=None, add_to_regions=True, allow_empty=False): """ Region factory constructor. Append the new region to self.regions list. """ if check_parents: parents = get_parents(select) for p in parents: if p not in [region.name for region in self.regions]: msg = 'parent region %s of %s not found!' % (p, name) raise ValueError(msg) stack = self._region_stack try: self._bnf.parseString(select) except ParseException: print 'parsing failed:', select raise region = visit_stack(stack, region_op, region_leaf(self, self.regions, select, functions)) region.name = name region.definition = select region.set_kind(kind) region.finalize(allow_empty=allow_empty) region.parent = parent region.update_shape() if add_to_regions: self.regions.append(region) return region def create_regions(self, region_defs, functions=None): output('creating regions...') tt = time.clock() self.reset_regions() ## # Sort region definitions by dependencies. graph, name_to_sort_name = get_dependency_graph(region_defs) sorted_regions = sort_by_dependency(graph) ## # Define regions. for name in sorted_regions: sort_name = name_to_sort_name[name] rdef = region_defs[sort_name] region = self.create_region(name, rdef.select, kind=rdef.get('kind', 'cell'), parent=rdef.get('parent', None), check_parents=False, functions=functions) output(' ', region.name) output('...done in %.2f s' % (time.clock() - tt)) return self.regions def save_regions(self, filename, region_names=None): """ Save regions as individual meshes. Parameters ---------- filename : str The output filename. region_names : list, optional If given, only the listed regions are saved. """ import os if region_names is None: region_names = self.regions.get_names() trunk, suffix = os.path.splitext(filename) output('saving regions...') for name in region_names: region = self.regions[name] output(name) aux = self.mesh.from_region(region, self.mesh) aux.write('%s_%s%s' % (trunk, region.name, suffix), io='auto') output('...done') def save_regions_as_groups(self, filename, region_names=None): """ Save regions in a single mesh but mark them by using different element/node group numbers. If regions overlap, the result is undetermined, with exception of the whole domain region, which is marked by group id 0. Region masks are also saved as scalar point data for output formats that support this. Parameters ---------- filename : str The output filename. region_names : list, optional If given, only the listed regions are saved. """ output('saving regions as groups...') aux = self.mesh.copy() n_ig = c_ig = 0 n_nod = self.shape.n_nod # The whole domain region should go first. names = (region_names if region_names is not None else self.regions.get_names()) for name in names: region = self.regions[name] if region.vertices.shape[0] == n_nod: names.remove(region.name) names = [region.name] + names break out = {} for name in names: region = self.regions[name] output(region.name) aux.cmesh.vertex_groups[region.vertices] = n_ig n_ig += 1 mask = nm.zeros((n_nod, 1), dtype=nm.float64) mask[region.vertices] = 1.0 out[name] = Struct(name='region', mode='vertex', data=mask, var_name=name, dofs=None) if region.has_cells(): ii = region.get_cells() aux.cmesh.cell_groups[ii] = c_ig c_ig += 1 aux.write(filename, io='auto', out=out) output('...done')
class Domain(Struct): """ Domain is divided into groups, whose purpose is to have homogeneous data shapes. """ def __init__(self, name, mesh, verbose=False): """Create a Domain. Parameters ---------- name : str Object name. mesh : Mesh A mesh defining the domain. """ geom_els = {} for ig, desc in enumerate(mesh.descs): gel = GeometryElement(desc) # Create geometry elements of dimension - 1. gel.create_surface_facet() geom_els[desc] = gel interps = {} for gel in geom_els.itervalues(): key = gel.get_interpolation_name() gel.interp = interps.setdefault(key, fea.Interpolant(key, gel)) gel = gel.surface_facet if gel is not None: key = gel.get_interpolation_name() gel.interp = interps.setdefault(key, fea.Interpolant(key, gel)) Struct.__init__(self, name=name, mesh=mesh, geom_els=geom_els, geom_interps=interps) self.mat_ids_to_i_gs = {} for ig, mat_id in enumerate(mesh.mat_ids): self.mat_ids_to_i_gs[mat_id[0]] = ig self.setup_groups() self.fix_element_orientation() self.reset_regions() self.clear_surface_groups() from sfepy.fem.geometry_element import create_geometry_elements from sfepy.fem.extmods.cmesh import CMesh self.cmesh = CMesh.from_mesh(mesh) gels = create_geometry_elements() self.cmesh.set_local_entities(gels) self.cmesh.setup_entities() self.shape.tdim = self.cmesh.tdim def setup_groups(self): n_nod, dim = self.mesh.coors.shape self.shape = Struct(n_gr=len(self.mesh.conns), n_el=0, n_nod=n_nod, dim=dim) self.groups = {} for ii in range(self.shape.n_gr): gel = self.geom_els[self.mesh.descs[ii]] # Shortcut. conn = self.mesh.conns[ii] vertices = nm.unique(conn) n_vertex = vertices.shape[0] n_el, n_ep = conn.shape n_edge = gel.n_edge n_edge_total = n_edge * n_el if gel.dim == 3: n_face = gel.n_face n_face_total = n_face * n_el else: n_face = n_face_total = 0 shape = Struct(n_vertex=n_vertex, n_el=n_el, n_ep=n_ep, n_edge=n_edge, n_edge_total=n_edge_total, n_face=n_face, n_face_total=n_face_total, dim=self.mesh.dims[ii]) self.groups[ii] = Struct(ig=ii, vertices=vertices, conn=conn, gel=gel, shape=shape) self.shape.n_el += n_el def iter_groups(self, igs=None): if igs is None: for ig in xrange(self.shape.n_gr): # sorted by ig. yield self.groups[ig] else: for ig in igs: yield ig, self.groups[ig] def get_cell_offsets(self): offs = {} off = 0 for group in self.iter_groups(): ig = group.ig offs[ig] = off off += group.shape.n_el return offs def get_mesh_coors(self, actual=False): """ Return the coordinates of the underlying mesh vertices. """ if actual and hasattr(self.mesh, 'coors_act'): return self.mesh.coors_act else: return self.mesh.coors def get_centroids(self, dim): """ Return the coordinates of centroids of mesh entities with dimension `dim`. """ return self.cmesh.get_centroids(dim) def get_mesh_bounding_box(self): """ Return the bounding box of the underlying mesh. Returns ------- bbox : ndarray (2, dim) The bounding box with min. values in the first row and max. values in the second row. """ return self.mesh.get_bounding_box() def get_diameter(self): """ Return the diameter of the domain. Notes ----- The diameter corresponds to the Friedrichs constant. """ bbox = self.get_mesh_bounding_box() return (bbox[1, :] - bbox[0, :]).max() def get_conns(self): """ Return the element connectivity groups of the underlying mesh. """ return self.mesh.conns def fix_element_orientation(self): """ Ensure element nodes ordering giving positive element volume. The groups with elements of lower dimension than the space dimension are skipped. """ from extmods.cmesh import orient_elements coors = self.mesh.coors for ii, group in self.groups.iteritems(): if group.shape.dim < self.shape.dim: continue ori, conn = group.gel.orientation, group.conn itry = 0 while itry < 2: flag = -nm.ones(conn.shape[0], dtype=nm.int32) # Changes orientation if it is wrong according to swap*! # Changes are indicated by positive flag. orient_elements(flag, conn, coors, ori.roots, ori.vecs, ori.swap_from, ori.swap_to) if nm.alltrue(flag == 0): if itry > 0: output('...corrected') itry = -1 break output( 'warning: bad element orientation, trying to correct...') itry += 1 if itry == 2 and flag[0] != -1: raise RuntimeError('elements cannot be oriented! (%d, %s)' % (ii, self.mesh.descs[ii])) elif flag[0] == -1: output('warning: element orienation not checked') def has_faces(self): return sum([group.shape.n_face for group in self.iter_groups()]) > 0 def reset_regions(self): """ Reset the list of regions associated with the domain. """ self.regions = OneTypeList(Region) self._region_stack = [] self._bnf = create_bnf(self._region_stack) def create_region(self, name, select, kind='cell', parent=None, check_parents=True, functions=None, add_to_regions=True): """ Region factory constructor. Append the new region to self.regions list. """ if check_parents: parents = get_parents(select) for p in parents: if p not in [region.name for region in self.regions]: msg = 'parent region %s of %s not found!' % (p, name) raise ValueError(msg) stack = self._region_stack try: self._bnf.parseString(select) except ParseException: print 'parsing failed:', select raise region = visit_stack( stack, region_op, region_leaf(self, self.regions, select, functions)) region.name = name region.definition = select region.set_kind(kind) region.parent = parent region.update_shape() if add_to_regions: self.regions.append(region) return region def create_regions(self, region_defs, functions=None): output('creating regions...') tt = time.clock() self.reset_regions() ## # Sort region definitions by dependencies. graph, name_to_sort_name = get_dependency_graph(region_defs) sorted_regions = sort_by_dependency(graph) ## # Define regions. for name in sorted_regions: sort_name = name_to_sort_name[name] rdef = region_defs[sort_name] region = self.create_region(name, rdef.select, kind=rdef.get('kind', 'cell'), parent=rdef.get('parent', None), check_parents=False, functions=functions) output(' ', region.name) output('...done in %.2f s' % (time.clock() - tt)) return self.regions def save_regions(self, filename, region_names=None): """ Save regions as individual meshes. Parameters ---------- filename : str The output filename. region_names : list, optional If given, only the listed regions are saved. """ import os if region_names is None: region_names = self.regions.get_names() trunk, suffix = os.path.splitext(filename) output('saving regions...') for name in region_names: region = self.regions[name] output(name) aux = self.mesh.from_region(region, self.mesh) aux.write('%s_%s%s' % (trunk, region.name, suffix), io='auto') output('...done') def save_regions_as_groups(self, filename, region_names=None): """ Save regions in a single mesh but mark them by using different element/node group numbers. If regions overlap, the result is undetermined, with exception of the whole domain region, which is marked by group id 0. Region masks are also saved as scalar point data for output formats that support this. Parameters ---------- filename : str The output filename. region_names : list, optional If given, only the listed regions are saved. """ output('saving regions as groups...') aux = self.mesh.copy() n_ig = c_ig = 0 n_nod = self.shape.n_nod # The whole domain region should go first. names = (region_names if region_names is not None else self.regions.get_names()) for name in names: region = self.regions[name] if region.vertices.shape[0] == n_nod: names.remove(region.name) names = [region.name] + names break out = {} for name in names: region = self.regions[name] output(region.name) aux.ngroups[region.vertices] = n_ig n_ig += 1 mask = nm.zeros((n_nod, 1), dtype=nm.float64) mask[region.vertices] = 1.0 out[name] = Struct(name='region', mode='vertex', data=mask, var_name=name, dofs=None) if region.has_cells(): for ig in region.igs: ii = region.get_cells(ig) aux.mat_ids[ig][ii] = c_ig c_ig += 1 aux.write(filename, io='auto', out=out) output('...done') def get_element_diameters(self, ig, cells, vg, mode, square=True): group = self.groups[ig] diameters = nm.empty((len(cells), 1, 1, 1), dtype=nm.float64) if vg is None: diameters.fill(1.0) else: vg.get_element_diameters(diameters, group.gel.edges, self.get_mesh_coors().copy(), group.conn, cells.astype(nm.int32), mode) if square: out = diameters.squeeze() else: out = nm.sqrt(diameters.squeeze()) return out def clear_surface_groups(self): """ Remove surface group data. """ self.surface_groups = {} def create_surface_group(self, region): """ Create a new surface group corresponding to `region` if it does not exist yet. Notes ----- Surface groups define surface facet connectivity that is needed for :class:`sfepy.fem.mappings.SurfaceMapping`. """ for ig in region.igs: groups = self.surface_groups.setdefault(ig, {}) if region.name not in groups: group = self.groups[ig] gel_faces = group.gel.get_surface_entities() name = 'surface_group_%s_%d' % (region.name, ig) surface_group = FESurface(name, region, gel_faces, group.conn, ig) groups[region.name] = surface_group def refine(self): """ Uniformly refine the domain mesh. Returns ------- domain : Domain instance The new domain with the refined mesh. Notes ----- Works only for meshes with single element type! Does not preserve node groups! """ names = set() for group in self.groups.itervalues(): names.add(group.gel.name) if len(names) != 1: msg = 'refine() works only for meshes with single element type!' raise NotImplementedError(msg) el_type = names.pop() if el_type == '2_3': mesh = refine_2_3(self.mesh, self.cmesh) elif el_type == '2_4': mesh = refine_2_4(self.mesh, self.cmesh) elif el_type == '3_4': mesh = refine_3_4(self.mesh, self.cmesh) elif el_type == '3_8': mesh = refine_3_8(self.mesh, self.cmesh) else: msg = 'unsupported element type! (%s)' % el_type raise NotImplementedError(msg) domain = Domain(self.name + '_r', mesh) return domain
class Domain(Struct): def __init__(self, name, mesh=None, nurbs=None, bmesh=None, regions=None, verbose=False): Struct.__init__(self, name=name, mesh=mesh, nurbs=nurbs, bmesh=bmesh, regions=regions, verbose=verbose) def get_centroids(self, dim): """ Return the coordinates of centroids of mesh entities with dimension `dim`. """ return self.cmesh.get_centroids(dim) def has_faces(self): return self.shape.tdim == 3 def reset_regions(self): """ Reset the list of regions associated with the domain. """ self.regions = OneTypeList(Region) self._region_stack = [] self._bnf = create_bnf(self._region_stack) def create_region(self, name, select, kind='cell', parent=None, check_parents=True, functions=None, add_to_regions=True, allow_empty=False): """ Region factory constructor. Append the new region to self.regions list. """ if check_parents: parents = get_parents(select) for p in parents: if p not in [region.name for region in self.regions]: msg = 'parent region %s of %s not found!' % (p, name) raise ValueError(msg) stack = self._region_stack try: self._bnf.parseString(select) except ParseException: print('parsing failed:', select) raise region = visit_stack( stack, region_op, region_leaf(self, self.regions, select, functions)) region.name = name region.definition = select region.set_kind(kind) region.finalize(allow_empty=allow_empty) region.parent = parent region.update_shape() if add_to_regions: self.regions.append(region) return region def create_regions(self, region_defs, functions=None): output('creating regions...') tt = time.clock() self.reset_regions() ## # Sort region definitions by dependencies. graph, name_to_sort_name = get_dependency_graph(region_defs) sorted_regions = sort_by_dependency(graph) ## # Define regions. for name in sorted_regions: sort_name = name_to_sort_name[name] rdef = region_defs[sort_name] region = self.create_region(name, rdef.select, kind=rdef.get('kind', 'cell'), parent=rdef.get('parent', None), check_parents=False, functions=functions) output(' ', region.name) output('...done in %.2f s' % (time.clock() - tt)) return self.regions def save_regions(self, filename, region_names=None): """ Save regions as individual meshes. Parameters ---------- filename : str The output filename. region_names : list, optional If given, only the listed regions are saved. """ import os if region_names is None: region_names = self.regions.get_names() trunk, suffix = os.path.splitext(filename) output('saving regions...') for name in region_names: region = self.regions[name] output(name) aux = self.mesh.from_region(region, self.mesh) aux.write('%s_%s%s' % (trunk, region.name, suffix), io='auto') output('...done') def save_regions_as_groups(self, filename, region_names=None): """ Save regions in a single mesh but mark them by using different element/node group numbers. If regions overlap, the result is undetermined, with exception of the whole domain region, which is marked by group id 0. Region masks are also saved as scalar point data for output formats that support this. Parameters ---------- filename : str The output filename. region_names : list, optional If given, only the listed regions are saved. """ output('saving regions as groups...') aux = self.mesh.copy() n_ig = c_ig = 0 n_nod = self.shape.n_nod # The whole domain region should go first. names = (region_names if region_names is not None else self.regions.get_names()) for name in names: region = self.regions[name] if region.vertices.shape[0] == n_nod: names.remove(region.name) names = [region.name] + names break out = {} for name in names: region = self.regions[name] output(region.name) aux.cmesh.vertex_groups[region.vertices] = n_ig n_ig += 1 mask = nm.zeros((n_nod, 1), dtype=nm.float64) mask[region.vertices] = 1.0 out[name] = Struct(name='region', mode='vertex', data=mask, var_name=name, dofs=None) if region.has_cells(): ii = region.get_cells() aux.cmesh.cell_groups[ii] = c_ig c_ig += 1 aux.write(filename, io='auto', out=out) output('...done')
def create_evaluable(expression, fields, materials, variables, integrals, regions=None, ebcs=None, epbcs=None, lcbcs=None, ts=None, functions=None, auto_init=False, mode='eval', extra_args=None, verbose=True, kwargs=None): """ Create evaluable object (equations and corresponding variables) from the `expression` string. Parameters ---------- expression : str The expression to evaluate. fields : dict The dictionary of fields used in `variables`. materials : Materials instance The materials used in the expression. variables : Variables instance The variables used in the expression. integrals : Integrals instance The integrals to be used. regions : Region instance or list of Region instances The region(s) to be used. If not given, the regions defined within the fields domain are used. ebcs : Conditions instance, optional The essential (Dirichlet) boundary conditions for 'weak' mode. epbcs : Conditions instance, optional The periodic boundary conditions for 'weak' mode. lcbcs : Conditions instance, optional The linear combination boundary conditions for 'weak' mode. ts : TimeStepper instance, optional The time stepper. functions : Functions instance, optional The user functions for boundary conditions, materials etc. auto_init : bool Set values of all variables to all zeros. mode : one of 'eval', 'el_avg', 'qp', 'weak' The evaluation mode - 'weak' means the finite element assembling, 'qp' requests the values in quadrature points, 'el_avg' element averages and 'eval' means integration over each term region. extra_args : dict, optional Extra arguments to be passed to terms in the expression. verbose : bool If False, reduce verbosity. kwargs : dict, optional The variables (dictionary of (variable name) : (Variable instance)) to be used in the expression. Returns ------- equation : Equation instance The equation that is ready to be evaluated. variables : Variables instance The variables used in the equation. """ if kwargs is None: kwargs = {} if regions is not None: if isinstance(regions, Region): regions = [regions] regions = OneTypeList(Region, regions) else: regions = fields[fields.keys()[0]].domain.regions # Create temporary variables. aux_vars = Variables(variables) if extra_args is None: extra_args = kwargs else: extra_args = copy(extra_args) extra_args.update(kwargs) if ts is not None: extra_args.update({'ts' : ts}) equations = Equations.from_conf({'tmp' : expression}, aux_vars, regions, materials, integrals, user=extra_args, verbose=verbose) equations.collect_conn_info() # The true variables used in the expression. variables = equations.variables if auto_init: for var in variables: var.init_data(step=0) if mode == 'weak': equations.time_update(ts, ebcs, epbcs, lcbcs, functions, verbose=verbose) else: setup_extra_data(equations.conn_info) return equations, variables
class Domain(Struct): """ Domain is divided into groups, whose purpose is to have homogeneous data shapes. """ def __init__(self, name, mesh, verbose=False): """Create a Domain. Parameters ---------- name : str Object name. mesh : Mesh A mesh defining the domain. """ geom_els = {} for ig, desc in enumerate(mesh.descs): gel = GeometryElement(desc) # Create geometry elements of dimension - 1. gel.create_surface_facet() geom_els[desc] = gel interps = {} for gel in geom_els.itervalues(): key = gel.get_interpolation_name() gel.interp = interps.setdefault(key, fea.Interpolant(key, gel)) gel = gel.surface_facet if gel is not None: key = gel.get_interpolation_name() gel.interp = interps.setdefault(key, fea.Interpolant(key, gel)) Struct.__init__(self, name=name, mesh=mesh, geom_els=geom_els, geom_interps=interps) self.mat_ids_to_i_gs = {} for ig, mat_id in enumerate(mesh.mat_ids): self.mat_ids_to_i_gs[mat_id[0]] = ig self.setup_groups() self.fix_element_orientation() self.reset_regions() self.clear_surface_groups() from sfepy.fem.geometry_element import create_geometry_elements from sfepy.fem.extmods.cmesh import CMesh self.cmesh = CMesh.from_mesh(mesh) gels = create_geometry_elements() self.cmesh.set_local_entities(gels) self.cmesh.setup_entities() self.shape.tdim = self.cmesh.tdim def setup_groups(self): n_nod, dim = self.mesh.coors.shape self.shape = Struct(n_gr=len(self.mesh.conns), n_el=0, n_nod=n_nod, dim=dim) self.groups = {} for ii in range(self.shape.n_gr): gel = self.geom_els[self.mesh.descs[ii]] # Shortcut. conn = self.mesh.conns[ii] vertices = nm.unique(conn) n_vertex = vertices.shape[0] n_el, n_ep = conn.shape n_edge = gel.n_edge n_edge_total = n_edge * n_el if gel.dim == 3: n_face = gel.n_face n_face_total = n_face * n_el else: n_face = n_face_total = 0 shape = Struct(n_vertex=n_vertex, n_el=n_el, n_ep=n_ep, n_edge=n_edge, n_edge_total=n_edge_total, n_face=n_face, n_face_total=n_face_total, dim=self.mesh.dims[ii]) self.groups[ii] = Struct(ig=ii, vertices=vertices, conn=conn, gel=gel, shape=shape) self.shape.n_el += n_el def iter_groups(self, igs=None): if igs is None: for ig in xrange(self.shape.n_gr): # sorted by ig. yield self.groups[ig] else: for ig in igs: yield ig, self.groups[ig] def get_cell_offsets(self): offs = {} off = 0 for group in self.iter_groups(): ig = group.ig offs[ig] = off off += group.shape.n_el return offs def get_mesh_coors(self, actual=False): """ Return the coordinates of the underlying mesh vertices. """ if actual and hasattr(self.mesh, 'coors_act'): return self.mesh.coors_act else: return self.mesh.coors def get_centroids(self, dim): """ Return the coordinates of centroids of mesh entities with dimension `dim`. """ return self.cmesh.get_centroids(dim) def get_mesh_bounding_box(self): """ Return the bounding box of the underlying mesh. Returns ------- bbox : ndarray (2, dim) The bounding box with min. values in the first row and max. values in the second row. """ return self.mesh.get_bounding_box() def get_diameter(self): """ Return the diameter of the domain. Notes ----- The diameter corresponds to the Friedrichs constant. """ bbox = self.get_mesh_bounding_box() return (bbox[1,:] - bbox[0,:]).max() def get_conns(self): """ Return the element connectivity groups of the underlying mesh. """ return self.mesh.conns def fix_element_orientation(self): """ Ensure element nodes ordering giving positive element volume. The groups with elements of lower dimension than the space dimension are skipped. """ from extmods.cmesh import orient_elements coors = self.mesh.coors for ii, group in self.groups.iteritems(): if group.shape.dim < self.shape.dim: continue ori, conn = group.gel.orientation, group.conn itry = 0 while itry < 2: flag = -nm.ones(conn.shape[0], dtype=nm.int32) # Changes orientation if it is wrong according to swap*! # Changes are indicated by positive flag. orient_elements(flag, conn, coors, ori.roots, ori.vecs, ori.swap_from, ori.swap_to) if nm.alltrue(flag == 0): if itry > 0: output('...corrected') itry = -1 break output('warning: bad element orientation, trying to correct...') itry += 1 if itry == 2 and flag[0] != -1: raise RuntimeError('elements cannot be oriented! (%d, %s)' % (ii, self.mesh.descs[ii])) elif flag[0] == -1: output('warning: element orienation not checked') def has_faces(self): return sum([group.shape.n_face for group in self.iter_groups()]) > 0 def reset_regions(self): """ Reset the list of regions associated with the domain. """ self.regions = OneTypeList(Region) self._region_stack = [] self._bnf = create_bnf(self._region_stack) def create_region(self, name, select, kind='cell', parent=None, check_parents=True, functions=None, add_to_regions=True): """ Region factory constructor. Append the new region to self.regions list. """ if check_parents: parents = get_parents(select) for p in parents: if p not in [region.name for region in self.regions]: msg = 'parent region %s of %s not found!' % (p, name) raise ValueError(msg) stack = self._region_stack try: self._bnf.parseString(select) except ParseException: print 'parsing failed:', select raise region = visit_stack(stack, region_op, region_leaf(self, self.regions, select, functions)) region.name = name region.definition = select region.set_kind(kind) region.parent = parent region.update_shape() if add_to_regions: self.regions.append(region) return region def create_regions(self, region_defs, functions=None): output('creating regions...') tt = time.clock() self.reset_regions() ## # Sort region definitions by dependencies. graph, name_to_sort_name = get_dependency_graph(region_defs) sorted_regions = sort_by_dependency(graph) ## # Define regions. for name in sorted_regions: sort_name = name_to_sort_name[name] rdef = region_defs[sort_name] region = self.create_region(name, rdef.select, kind=rdef.get('kind', 'cell'), parent=rdef.get('parent', None), check_parents=False, functions=functions) output(' ', region.name) output('...done in %.2f s' % (time.clock() - tt)) return self.regions def save_regions(self, filename, region_names=None): """ Save regions as individual meshes. Parameters ---------- filename : str The output filename. region_names : list, optional If given, only the listed regions are saved. """ import os if region_names is None: region_names = self.regions.get_names() trunk, suffix = os.path.splitext(filename) output('saving regions...') for name in region_names: region = self.regions[name] output(name) aux = self.mesh.from_region(region, self.mesh) aux.write('%s_%s%s' % (trunk, region.name, suffix), io='auto') output('...done') def save_regions_as_groups(self, filename, region_names=None): """ Save regions in a single mesh but mark them by using different element/node group numbers. If regions overlap, the result is undetermined, with exception of the whole domain region, which is marked by group id 0. Region masks are also saved as scalar point data for output formats that support this. Parameters ---------- filename : str The output filename. region_names : list, optional If given, only the listed regions are saved. """ output('saving regions as groups...') aux = self.mesh.copy() n_ig = c_ig = 0 n_nod = self.shape.n_nod # The whole domain region should go first. names = (region_names if region_names is not None else self.regions.get_names()) for name in names: region = self.regions[name] if region.vertices.shape[0] == n_nod: names.remove(region.name) names = [region.name] + names break out = {} for name in names: region = self.regions[name] output(region.name) aux.ngroups[region.vertices] = n_ig n_ig += 1 mask = nm.zeros((n_nod, 1), dtype=nm.float64) mask[region.vertices] = 1.0 out[name] = Struct(name='region', mode='vertex', data=mask, var_name=name, dofs=None) if region.has_cells(): for ig in region.igs: ii = region.get_cells(ig) aux.mat_ids[ig][ii] = c_ig c_ig += 1 aux.write(filename, io='auto', out=out) output('...done') def get_element_diameters(self, ig, cells, vg, mode, square=True): group = self.groups[ig] diameters = nm.empty((len(cells), 1, 1, 1), dtype=nm.float64) if vg is None: diameters.fill(1.0) else: vg.get_element_diameters(diameters, group.gel.edges, self.get_mesh_coors().copy(), group.conn, cells.astype(nm.int32), mode) if square: out = diameters.squeeze() else: out = nm.sqrt(diameters.squeeze()) return out def clear_surface_groups(self): """ Remove surface group data. """ self.surface_groups = {} def create_surface_group(self, region): """ Create a new surface group corresponding to `region` if it does not exist yet. Notes ----- Surface groups define surface facet connectivity that is needed for :class:`sfepy.fem.mappings.SurfaceMapping`. """ for ig in region.igs: groups = self.surface_groups.setdefault(ig, {}) if region.name not in groups: group = self.groups[ig] gel_faces = group.gel.get_surface_entities() name = 'surface_group_%s_%d' % (region.name, ig) surface_group = FESurface(name, region, gel_faces, group.conn, ig) groups[region.name] = surface_group def refine(self): """ Uniformly refine the domain mesh. Returns ------- domain : Domain instance The new domain with the refined mesh. Notes ----- Works only for meshes with single element type! Does not preserve node groups! """ names = set() for group in self.groups.itervalues(): names.add(group.gel.name) if len(names) != 1: msg = 'refine() works only for meshes with single element type!' raise NotImplementedError(msg) el_type = names.pop() if el_type == '2_3': mesh = refine_2_3(self.mesh, self.cmesh) elif el_type == '2_4': mesh = refine_2_4(self.mesh, self.cmesh) elif el_type == '3_4': mesh = refine_3_4(self.mesh, self.cmesh) elif el_type == '3_8': mesh = refine_3_8(self.mesh, self.cmesh) else: msg = 'unsupported element type! (%s)' % el_type raise NotImplementedError(msg) domain = Domain(self.name + '_r', mesh) return domain
def extract_time_history(filename, extract, verbose=True): """Extract time history of a variable from a multi-time-step results file. Parameters ---------- filename : str The name of file to extract from. extract : str The description of what to extract in a string of comma-separated description items. A description item consists of: name of the variable to extract, mode ('e' for elements, 'n' for nodes), ids of the nodes or elements (given by the mode). Example: 'u n 10 15, p e 0' means variable 'u' in nodes 10, 15 and variable 'p' in element 0. verbose : bool Verbosity control. Returns ------- ths : dict The time histories in a dict with variable names as keys. If a nodal variable is requested in elements, its value is a dict of histories in the element nodes. ts : TimeStepper instance The time stepping information. """ output('extracting selected data...', verbose=verbose) output('selection:', extract, verbose=verbose) ## # Parse extractions. pes = OneTypeList(Struct) for chunk in extract.split(','): aux = chunk.strip().split() pes.append(Struct(var = aux[0], mode = aux[1], indx = map(int, aux[2:]), igs = None)) ## # Verify array limits, set igs for element data, shift indx. mesh = Mesh.from_file(filename) n_el, n_els, offs = mesh.n_el, mesh.n_els, mesh.el_offsets for pe in pes: if pe.mode == 'n': for ii in pe.indx: if (ii < 0) or (ii >= mesh.n_nod): raise ValueError('node index 0 <= %d < %d!' % (ii, mesh.n_nod)) if pe.mode == 'e': pe.igs = [] for ii, ie in enumerate(pe.indx[:]): if (ie < 0) or (ie >= n_el): raise ValueError('element index 0 <= %d < %d!' % (ie, n_el)) ig = (ie < n_els).argmax() pe.igs.append(ig) pe.indx[ii] = ie - offs[ig] ## print pes ## # Extract data. # Assumes only one element group (ignores igs)! io = MeshIO.any_from_filename(filename) ths = {} for pe in pes: mode, nname = io.read_data_header(pe.var) output(mode, nname, verbose=verbose) if ((pe.mode == 'n' and mode == 'vertex') or (pe.mode == 'e' and mode == 'cell')): th = io.read_time_history(nname, pe.indx) elif pe.mode == 'e' and mode == 'vertex': conn = mesh.conns[0] th = {} for iel in pe.indx: ips = conn[iel] th[iel] = io.read_time_history(nname, ips) else: raise ValueError('cannot extract cell data %s in nodes!' % pe.var) ths[pe.var] = th output('...done', verbose=verbose) ts = TimeStepper(*io.read_time_stepper()) return ths, ts