def __init__(self,coords=None,elems=None,prop=None,eltype=None): """Initialize a new Mesh.""" self.coords = self.elems = self.prop = self.eltype = None if coords is None: # Create an empty Mesh object #print "EMPTY MESH" return if elems is None: if hasattr(coords,'toMesh'): # initialize from a single object coords,elems = coords.toMesh() elif type(coords) is tuple: # SHOULD WE KEEP THIS ??? coords,elems = coords try: self.coords = Coords(coords) if self.coords.ndim != 2: raise ValueError,"\nExpected 2D coordinate array, got %s" % self.coords.ndim self.elems = Connectivity(elems) if self.elems.size > 0 and ( self.elems.max() >= self.coords.shape[0] or self.elems.min() < 0): raise ValueError,"\nInvalid connectivity data: some node number(s) not in coords array" except: raise self.setProp(prop) if eltype is None: self.eltype = defaultEltype(self.nplex()) else: self.eltype = eltype
def readSmeshFacetsBlock(fil, nfacets, nbmark): """Read a tetgen .smesh facets bock. Returns a tuple of dictionaries with plexitudes as keys: - elems: for each plexitude a Connectivity array - nrs: for each plexitude a list of element numbers in corresponding elems """ elems = {} nrs = {} for i in range(nfacets): line = fil.readline() line = line.strip() if len(line) > 0: data = fromstring(line, sep=' ', dtype=int32) nplex = data[0] if nplex > 0: e = data[1:1 + nplex] # bmark currently not read addElem(elems, nrs, e, i, nplex) else: raise ValueError, "Invalid data line:\n%s" % line for np in elems: if np == 3: eltype = 'tri3' elif np == 4: eltype = 'quad4' else: eltype = None elems[np] = Connectivity(elems[np], eltype=eltype) nrs[np] = array(nrs[np]) return elems, nrs
def readElemsBlock(fil, nelems, nplex, nattr): """Read a tetgen elems block. Returns a tuple with: - elems: Connectivity of type 'tet4' or 'tet10' - nrs: the element numbers - attr: the element attributes The last can be None. """ ndata = 1 + nplex + nattr data = fromfile(fil, sep=' ', dtype=int32, count=ndata * nelems).reshape(nelems, ndata) nrs = data[:, 0] elems = data[:, 1:1 + nplex] if nattr > 0: attr = data[:, 1 + nplex:] else: attr = None if nplex == 4: eltype = 'tet4' elif nplex == 10: eltype = 'tet10' else: raise ValueError, "Unknown tetgen .ele plexitude %s" % nplex return Connectivity(elems, eltype=eltype), nrs, attr
def getEntities(self, level, reduce=False): """Return the type and connectivity table of some element entities. The full list of entities with increasing dimensionality 0,1,2,3 is:: ['points', 'edges', 'faces', 'cells' ] If level is negative, the dimensionality returned is relative to the highest dimensionality (.i.e., that of the element). If it is positive, it is taken absolute. Thus, for a 3D element type, getEntities(-1) returns the faces, while for a 2D element type, it returns the edges. For both types however, getLowerEntities(+1) returns the edges. The return value is a dict where the keys are element types and the values are connectivity tables. If reduce == False: there will be only one connectivity table and it may include degenerate elements. If reduce == True, an attempt is made to reduce the degenerate elements. The returned dict may then have multiple entries. If the requested entity level is outside the range 0..ndim, the return value is None. """ if level < 0: level = self.ndim + level if level < 0 or level > self.ndim: return Connectivity() if level == 0: return Connectivity(arange(self.nplex()).reshape((-1, 1)), eltype='point') elif level == self.ndim: return Connectivity(arange(self.nplex()).reshape((1, -1)), eltype=self) elif level == 1: return self.edges elif level == 2: return self.faces
def _sanitize(ent): # input is Connectivity or (eltype,table) # output is Connectivity if isinstance(ent, Connectivity): if hasattr(ent, 'eltype'): return ent else: raise ValueError, "Conectivity should have an element type" else: return Connectivity(ent[1], eltype=ent[0])
def readFacesBlock(fil, nelems, nbmark): """Read a tetgen faces block. Returns a a tuple with: - elems: Connectivity of type 'tri3' - nrs: face numbers - bmrk: face boundary marker The last can be None. """ ndata = 1 + 3 + nbmark data = fromfile(fil, sep=' ', dtype=int32, count=ndata * nelems).reshape(nelems, ndata) nrs = data[:, 0] elems = data[:, 1:4] if nbmark == 1: bmark = data[:, -1] else: bmark = None return Connectivity(elems, eltype='tri3'), nrs, bmark
def __init__(self, config=None, force_net_build=False, verbose=False, debug=False, host=None, db_name=None, user=None, password=None): """Connects to the BNA database kwargs: config -- path to the config file, if not given use the default config.yaml force_net_build -- force a rebuild of the network even if an existing one is found verbose -- output useful messages debug -- set to debug mode host -- hostname or address (overrides the config file if given) db -- name of database on server (overrides the config file if given) user -- username to connect to database (overrides the config file if given) password -- password to connect to database (overrides the config file if given) return: pyBNA object """ Destinations.__init__(self) Connectivity.__init__(self) Core.__init__(self) Conf.__init__(self) self.verbose = verbose self.debug = debug self.module_dir = os.path.dirname(os.path.abspath(__file__)) if config is None: config = os.path.join(self.module_dir, "config.yaml") self.config = self.parse_config(yaml.safe_load(open(config))) self.config["bna"]["connectivity"]["max_detour"] = float( 100 + self.config["bna"]["connectivity"]["max_detour"]) / 100 self.db_connectivity_table = self.config["bna"]["connectivity"][ "table"] self.net_config = self.config["bna"]["network"] # km/mi if "units" in self.config: if self.config.units == "mi": self.km = False elif self.config.units == "km": self.km = True else: raise ValueError("Invalid units \"{}\" in config".format( self.config.units)) else: self.km = False if self.verbose: print("") print("---------------pyBNA---------------") print(" Create and test BNA scenarios") print("-----------------------------------") print("") # set up db connection print("Connecting to database") if host is None: host = self.config["db"]["host"] if db_name is None: db_name = self.config["db"]["dbname"] if user is None: user = self.config["db"]["user"] if password is None: password = self.config["db"]["password"] db_connection_string = " ".join([ "dbname=" + db_name, "user="******"host=" + host, "password="******"DB connection: %s" % db_connection_string) DBUtils.__init__(self, db_connection_string, self.verbose, self.debug) # srid if "srid" in self.config: self.srid = self.config["srid"] elif not self.debug: self.srid = self.get_srid(self.config.bna.blocks.table) # destinations self.destinations = dict() self.destination_blocks = set() if not self.debug: pass # self.set_destinations() self.sql_subs = self.make_bna_substitutions(self.config) if force_net_build: print("Building network tables in database") self.build_network() elif self.debug: pass elif not self.check_network(): print("Network tables not found in database...building") self.build_network() elif self.verbose: print("Network tables found in database")
import materials import cantilevers import matplotlib.pyplot as plt from scipy.interpolate import InterpolatedUnivariateSpline from gaussian import Gaussian from laminate_fem import LaminateFEM from connectivity import Connectivity import scipy.sparse as sparse """ """ material = materials.PiezoMumpsMaterial() cantilever = cantilevers.InitialCantileverFixedTip() la = laminate_analysis.LaminateAnalysis(cantilever, material, True) fem = LaminateFEM(cantilever, material, True, 0.01) connectivity = Connectivity(fem.mesh) gaussian = Gaussian(fem, fem.cantilever, 0.1) index = 100 # the index of the pseudo-density to vary. ps = np.arange(0.02, 1, 0.01) g3sums = np.empty_like(ps) netas = np.empty_like(ps) dg3sums = np.empty_like(ps) dnetas = np.empty_like(ps) print('Number of points: %d' % len(ps)) pnew = la.fem.density.copy() mdofmat = la.mdofmat tdofmat = la.tdofmat for i, p in enumerate(ps):
class Mesh(Geometry): """A mesh is a discrete geometrical model defined by nodes and elements. In the Mesh geometrical data model, coordinates of all points are gathered in a single twodimensional array 'coords' with shape (ncoords,3) and the individual geometrical elements are described by indices into the 'elems' array. This model has some advantages over the Formex data model, where all points of all element are stored by their coordinates: - compacter storage, because coordinates of coinciding points do not need to be repeated, - faster connectivity related algorithms. The downside is that geometry generating algorithms are far more complex and possibly slower. In pyFormex we therefore mostly use the Formex data model when creating geometry, but when we come to the point of exporting the geometry to file (and to other programs), a Mesh data model may be more adequate. The Mesh data model has at least the following attributes: - coords: (ncoords,3) shaped Coords array, - elems: (nelems,nplex) shaped array of int32 indices into coords. All values should be in the range 0 <= value < ncoords. - prop: array of element property numbers, default None. - eltype: string designing the element type, default None. If eltype is None, a default eltype is deived from the plexitude. A Mesh can be initialized by its attributes (coords,elems,prop,eltype) or by a single geometric object that provides a toMesh() method. """ def __init__(self,coords=None,elems=None,prop=None,eltype=None): """Initialize a new Mesh.""" self.coords = self.elems = self.prop = self.eltype = None if coords is None: # Create an empty Mesh object #print "EMPTY MESH" return if elems is None: if hasattr(coords,'toMesh'): # initialize from a single object coords,elems = coords.toMesh() elif type(coords) is tuple: # SHOULD WE KEEP THIS ??? coords,elems = coords try: self.coords = Coords(coords) if self.coords.ndim != 2: raise ValueError,"\nExpected 2D coordinate array, got %s" % self.coords.ndim self.elems = Connectivity(elems) if self.elems.size > 0 and ( self.elems.max() >= self.coords.shape[0] or self.elems.min() < 0): raise ValueError,"\nInvalid connectivity data: some node number(s) not in coords array" except: raise self.setProp(prop) if eltype is None: self.eltype = defaultEltype(self.nplex()) else: self.eltype = eltype def setCoords(self,coords): """Replace the current coords with new ones. Returns a Mesh exactly like the current except for the position of the coordinates. """ if isinstance(coords,Coords) and coords.shape == self.coords.shape: return Mesh(coords,self.elems,self.prop,self.eltype) else: raise ValueError,"Invalid reinitialization of Mesh coords" def setProp(self,prop=None): """Create or destroy the property array for the Mesh. A property array is a rank-1 integer array with dimension equal to the number of elements in the Mesh. You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored. If a value None is given, the properties are removed from the Mesh. """ if prop is None: self.prop = None else: prop = array(prop).astype(Int) self.prop = resize(prop,(self.nelems(),)) return self def getProp(self): """Return the properties as a numpy array (ndarray)""" return self.prop def maxProp(self): """Return the highest property value used, or None""" if self.prop is None: return None else: return self.prop.max() def propSet(self): """Return a list with unique property values.""" if self.prop is None: return None else: return unique(self.prop) def copy(self): """Return a copy using the same data arrays""" # SHOULD THIS RETURN A DEEP COPY? return Mesh(self.coords,self.elems,self.prop,self.eltype) def toFormex(self): """Convert a Mesh to a Formex. The Formex inherits the element property numbers and eltype from the Mesh. Node property numbers however can not be translated to the Formex data model. """ return Formex(self.coords[self.elems],self.prop,self.eltype) def ndim(self): return 3 def nelems(self): return self.elems.shape[0] def nplex(self): return self.elems.shape[1] def ncoords(self): return self.coords.shape[0] nnodes = ncoords npoints = ncoords def shape(self): return self.elems.shape def nedges(self): """Return the number of edges. This returns the number of rows that would be in getEdges(), without actually constructing the edges. The edges are not fused! """ try: el = getattr(elements,self.eltype.capitalize()) return self.nelems() * len(el.edges) except: return 0 def centroids(self): """Return the centroids of all elements of the Mesh. The centroid of an element is the point whose coordinates are the mean values of all points of the element. The return value is a Coords object with nelems points. """ return self.coords[self.elems].mean(axis=1) def getCoords(self): """Get the coords data.""" return self.coords def getElems(self): """Get the elems data.""" return self.elems def getLowerEntitiesSelector(self,level=-1,unique=False): """Get the entities of a lower dimensionality. If the element type is defined in the :mod:`elements` module, this returns a Connectivity table with the entities of a lower dimensionality. The full list of entities with increasing dimensionality 0,1,2,3 is:: ['points', 'edges', 'faces', 'cells' ] If level is negative, the dimensionality returned is relative to that of the caller. If it is positive, it is taken absolute. Thus, for a Mesh with a 3D element type, getLowerEntities(-1) returns the faces, while for a 2D element type, it returns the edges. For bothe meshes however, getLowerEntities(+1) returns the edges. By default, all entities for all elements are returned and common entities will appear multiple times. Specifying unique=True will return only the unique ones. The return value may be an empty table, if the element type does not have the requested entities (e.g. the 'point' type). If the eltype is not defined, or the requested entity level is outside the range 0..3, the return value is None. """ try: el = getattr(elements,self.eltype.capitalize()) except: return None if level < 0: level = el.ndim + level if level < 0 or level > 3: return None attr = ['points', 'edges', 'faces', 'cells'][level] return array(getattr(el,attr)) def getLowerEntities(self,level=-1,unique=False): """Get the entities of a lower dimensionality. If the element type is defined in the :mod:`elements` module, this returns a Connectivity table with the entities of a lower dimensionality. The full list of entities with increasing dimensionality 0,1,2,3 is:: ['points', 'edges', 'faces', 'cells' ] If level is negative, the dimensionality returned is relative to that of the caller. If it is positive, it is taken absolute. Thus, for a Mesh with a 3D element type, getLowerEntities(-1) returns the faces, while for a 2D element type, it returns the edges. For bothe meshes however, getLowerEntities(+1) returns the edges. By default, all entities for all elements are returned and common entities will appear multiple times. Specifying unique=True will return only the unique ones. The return value may be an empty table, if the element type does not have the requested entities (e.g. the 'point' type). If the eltype is not defined, or the requested entity level is outside the range 0..3, the return value is None. """ sel = self.getLowerEntitiesSelector(level) ent = self.elems.selectNodes(sel) if unique: ent = ent.removeDoubles() return ent def getEdges(self,unique=False): """Return the edges of the elements. This is a convenient function to create a table with the element edges. It is equivalent to ```self.getLowerEntities(1,unique)``` """ return self.getLowerEntities(1,unique) def getFaces(self,unique=False): """Return the faces of the elements. This is a convenient function to create a table with the element faces. It is equivalent to ```self.getLowerEntities(2,unique)``` """ return self.getLowerEntities(2,unique) # ?? DOES THIS WORK FOR *ANY* MESH ?? def getAngles(self, angle_spec=Deg): """Returns the angles in Deg or Rad between the edges of a mesh. The returned angles are shaped as (nelems, n1faces, n1vertices), where n1faces are the number of faces in 1 element and the number of vertices in 1 face. """ mf=self.getCoords()[self.getFaces()] el = getattr(elements,self.eltype.capitalize()) v = mf - roll(mf,-1,axis=1) v=normalize(v) v1=-roll(v,+1,axis=1) angfac= arccos( dotpr(v, v1) )*180./math.pi return angfac.reshape(self.nelems(),len(el.faces), len(el.faces[0])) def getBorder(self): """Return the border of the Mesh. This returns a Connectivity table with the border of the Mesh. The border entities are of a lower jierarchical level than the mesh itself. This Connectivity can be used together with the Mesh coords to construct a Mesh of the border geometry. See also getBorderMesh. """ sel = self.getLowerEntitiesSelector(-1) hi,lo = self.elems.insertLevel(sel) hiinv = hi.inverse() ncon = (hiinv>=0).sum(axis=1) brd = (ncon<=1) brd = lo[brd] return brd def getBorderMesh(self): """Return a Mesh with the border elements. Returns a Mesh representing the border of the Mesh. The new Mesh is of the next lower hierarchical level. """ return Mesh(self.coords,self.getBorder()) def report(self): bb = self.bbox() return """ Shape: %s nodes, %s elems, plexitude %s BBox: %s, %s Size: %s """ % (self.ncoords(),self.nelems(),self.nplex(),bb[1],bb[0],bb[1]-bb[0]) def __str__(self): return self.report() + "Coords:\n" + self.coords.__str__() + "Elems:\n" + self.elems.__str__() def fuse(self,**kargs): """Fuse the nodes of a Meshes. All nodes that are within the tolerance limits of each other are merged into a single node. The merging operation can be tuned by specifying extra arguments that will be passed to :meth:`Coords:fuse`. """ coords,index = self.coords.fuse(**kargs) return Mesh(coords,index[self.elems],self.prop,self.eltype) def compact(self): """Remove unconnected nodes and renumber the mesh. Beware! This function changes the object in place. """ nodes = unique1d(self.elems) if nodes.size == 0: self.__init__([],[]) elif nodes.shape[0] < self.ncoords() or nodes[-1] >= nodes.size: coords = self.coords[nodes] if nodes[-1] >= nodes.size: elems = inverseUniqueIndex(nodes)[self.elems] else: elems = self.elems self.__init__(coords,elems,prop=self.prop,eltype=self.eltype) return self def select(self,selected): """Return a mesh with selected elements from the original. - `selected`: an object that can be used as an index in the `elems` array, e.g. a list of element numbers. Returns a Mesh with only the selected elements. The returned mesh is not compacted. """ if len(self.elems) == 0: return self prop = self.prop if prop is not None: prop = prop[selected] elems = self.elems[selected] return Mesh(self.coords,elems,prop,self.eltype) def meanNodes(self,nodsel): """Create nodes from the existing nodes of a mesh. `nodsel` is a local node selector as in :meth:`selectNodes` Returns the mean coordinates of the points in the selector. """ elems = self.elems.selectNodes(nodsel) return self.coords[elems].mean(axis=1) def addNodes(self,newcoords,eltype=None): """Add new nodes to elements. `newcoords` is an `(nelems,nnod,3)` array of coordinates. Each element thus gets exactly `nnod` extra points and the result is a Mesh with plexitude self.nplex() + nnod. """ newnodes = arange(newcoords.shape[0]).reshape(self.elems.shape[0],-1) + self.coords.shape[0] elems = Connectivity(concatenate([self.elems,newnodes],axis=-1)) coords = Coords.concatenate([self.coords,newcoords]) return Mesh(coords,elems,self.prop,eltype) def addMeanNodes(self,nodsel,eltype=None): """Add new nodes to elements by averaging existing ones. `nodsel` is a local node selector as in :meth:`selectNodes` Returns a Mesh where the mean coordinates of the points in the selector are added to each element, thus increasing the plexitude by the length of the items in the selector. The new element type should be set to correct value. """ newcoords = self.meanNodes(nodsel) return self.addNodes(newcoords,eltype) def selectNodes(self,nodsel,eltype): """Return a mesh with subsets of the original nodes. `nodsel` is an object that can be converted to a 1-dim or 2-dim array. Examples are a tuple of local node numbers, or a list of such tuples all having the same length. Each row of `nodsel` holds a list of local node numbers that should be retained in the new connectivity table. """ elems = self.elems.selectNodes(nodsel) prop = self.prop if prop is not None: prop = column_stack([prop]*len(nodsel)).reshape(-1) return Mesh(self.coords,elems,prop=prop,eltype=eltype) def withProp(self,val): """Return a Mesh which holds only the elements with property val. val is either a single integer, or a list/array of integers. The return value is a Mesh holding all the elements that have the property val, resp. one of the values in val. The returned Mesh inherits the matching properties. If the Mesh has no properties, a copy with all elements is returned. """ if self.prop is None: return Mesh(self.coords,self.elems,eltype=self.eltype) elif type(val) == int: return Mesh(self.coords,self.elems[self.prop==val],val,self.eltype) else: t = zeros(self.prop.shape,dtype=bool) for v in asarray(val).flat: t += (self.prop == v) return Mesh(self.coords,self.elems[t],self.prop[t],self.eltype) def splitProp(self): """Partition aMesh according to its prop values. Returns a dict with the prop values as keys and the corresponding partitions as values. Each value is a Mesh instance. It the Mesh has no props, an empty dict is returned. """ if self.prop is None: return {} else: return dict([(p,self.withProp(p)) for p in self.propSet()]) def convert(self,totype): fromtype = self.eltype strategy = _conversions_[fromtype].get(totype,None) while not type(strategy) is list: # This allows for aliases in the conversion database strategy = _conversions_[fromtype].get(strategy,None) if strategy is None: raise ValueError,"Don't know how to convert %s -> %s" % (fromtype,totype) # 'r' and 'v' steps can only be the first and only step steptype,stepdata = strategy[0] if steptype == 'r': # Randomly convert elements to one of the types in list return self.convertRandom(stepdata) elif steptype == 'v': return self.convert(stepdata).convert(totype) # Execute a strategy mesh = self totype = totype.split('-')[0] for step in strategy: #print "STEP: %s" % str(step) steptype,stepdata = step if steptype == 'm': mesh = mesh.addMeanNodes(stepdata,totype) elif steptype == 's': mesh = mesh.selectNodes(stepdata,totype) else: raise ValueError,"Unknown conversion step type '%s'" % steptype return mesh def splitRandom(self,n): """Split a mesh in n parts, distributing the elements randomly.""" sel = random.randint(0,n,(self.nelems())) return [ self.select(sel==i) for i in range(n) if i in sel ] def convertRandom(self,choices): """Convert choosing randomly between choices""" ml = self.splitRandom(len(choices)) ml = [ m.convert(c) for m,c in zip(ml,choices) ] prop = self.prop if prop is not None: prop = concatenate([m.prop for m in ml]) elems = concatenate([m.elems for m in ml],axis=0) eltype = set([m.eltype for m in ml]) if len(eltype) > 1: raise RuntimeError,"Invalid choices for random conversions" eltype = eltype.pop() return Mesh(self.coords,elems,prop,eltype) def reduceDegenerate(self,eltype=None): """Reduce degenerate elements to lower plexitude elements. This will try to reduce the degenerate elements of the mesh to elements of a lower plexitude. If a target element type is given, only the matching recuce scheme is tried. Else, all the target element types for which a reduce scheme from the Mesh eltype is available, will be tried. The result is a list of Meshes of which the last one contains the elements that could not be reduced and may be empty. Property numbers propagate to the children. """ strategies = _reductions_.get(self.eltype,{}) if eltype is not None: s = strategies.get(eltype,[]) if s: strategies = {eltype:s} else: strategies = {} if not strategies: return [self] m = self ML = [] for eltype in strategies: #print "REDUCE TO %s" % eltype elems = [] prop = [] for conditions,selector in strategies[eltype]: e = m.elems cond = array(conditions) #print "TRYING",cond #print e w = (e[:,cond[:,0]] == e[:,cond[:,1]]).all(axis=1) #print "Matching elems: %s" % where(w)[0] sel = where(w)[0] if len(sel) > 0: elems.append(e[sel][:,selector]) if m.prop is not None: prop.append(m.prop[sel]) # remove the reduced elems from m m = m.select(~w) if m.nelems() == 0: break if elems: elems = concatenate(elems) if prop: prop = concatenate(prop) else: prop = None #print elems #print prop ML.append(Mesh(m.coords,elems,prop,eltype)) if m.nelems() == 0: break ML.append(m) return ML def splitDegenerate(self,autofix=True): """Split a Mesh in degenerate and non-degenerate elements. If autofix is True, the degenerate elements will be tested against known degeneration patterns, and the matching elements will be transformed to non-degenerate elements of a lower plexitude. The return value is a list of Meshes. The first holds the non-degenerate elements of the original Mesh. The last holds the remaining degenerate elements. The intermediate Meshes, if any, hold elements of a lower plexitude than the original. These may still contain degenerate elements. """ deg = self.elems.testDegenerate() M0 = self.select(~deg) M1 = self.select(deg) if autofix: ML = [M0] + M1.reduceDegenerate() else: ML = [M0,M1] return ML def renumber(self,order='elems'): """Renumber the nodes of a Mesh in the specified order. order is an index with length equal to the number of nodes. The index specifies the node number that should come at this position. Thus, the order values are the old node numbers on the new node number positions. order can also be a predefined value that will generate the node index automatically: - 'elems': the nodes are number in order of their appearance in the Mesh connectivity. """ if order == 'elems': order = renumberIndex(self.elems) newnrs = inverseUniqueIndex(order) return Mesh(self.coords[order],newnrs[self.elems],prop=self.prop,eltype=self.eltype) def extrude(self,n,step=1.,dir=0,autofix=True): """Extrude a Mesh in one of the axes directions. Returns a new Mesh obtained by extruding the given Mesh over n steps of length step in direction of axis dir. The returned Mesh has double plexitude of the original. This function is usually used to extrude points into lines, lines into surfaces and surfaces into volumes. By default it will try to fix the connectivity ordering where appropriate. If autofix is switched off, the connectivities are merely stacked, and the user may have to fix it himself. Currently, this function correctly transforms: point1 to line2, line2 to quad4, tri3 to wedge6, quad4 to hex8. """ nplex = self.nplex() coord2 = self.coords.translate(dir,n*step) M = connectMesh(self,Mesh(coord2,self.elems),n) if autofix and nplex == 2: # fix node ordering for line2 to quad4 extrusions M.elems[:,-nplex:] = M.elems[:,-1:-(nplex+1):-1].copy() if autofix: M.eltype = defaultEltype(M.nplex()) return M def revolve(self,n,axis=0,angle=360.,around=None,autofix=True): """Revolve a Mesh around an axis. Returns a new Mesh obtained by revolving the given Mesh over an angle around an axis in n steps, while extruding the mesh from one step to the next. This function is usually used to extrude points into lines, lines into surfaces and surfaces into volumes. By default it will try to fix the connectivity ordering where appropriate. If autofix is switched off, the connectivities are merely stacked, and the user may have to fix it himself. Currently, this function correctly transforms: point1 to line2, line2 to quad4, tri3 to wedge6, quad4 to hex8. """ nplex = self.nplex() angles = arange(n+1) * angle / n coordL = [ self.coords.rotate(angle=a,axis=axis,around=around) for a in angles ] ML = [ Mesh(x,self.elems) for x in coordL ] n1 = n2 = eltype = None if autofix and nplex == 2: # fix node ordering for line2 to quad4 revolutions n1 = [0,1] n2 = [1,0] if autofix: eltype = defaultEltype(2*self.nplex()) CL = [ connectMesh(m1,m2,1,n1,n2,eltype) for (m1,m2) in zip(ML[:-1],ML[1:]) ] return Mesh.concatenate(CL) def sweep(self,path,autofix=True,**kargs): """Sweep a mesh along a path, creating an extrusion Returns a new Mesh obtained by sweeping the given Mesh over a path. The returned Mesh has double plexitude of the original. The operation is similar to the extrude() method, but the path can be any 3D curve. This function is usually used to extrude points into lines, lines into surfaces and surfaces into volumes. By default it will try to fix the connectivity ordering where appropriate. If autofix is switched off, the connectivities are merely stacked, and the user may have to fix it himself. Currently, this function correctly transforms: point1 to line2, line2 to quad4, tri3 to wedge6, quad4 to hex8. """ nplex = self.nplex() seq = sweepCoords(self.coords,path,**kargs) ML = [ Mesh(x,self.elems) for x in seq ] M = connectMeshSequence(ML) #print M if autofix and nplex == 2: # fix node ordering for line2 to quad4 extrusions M.elems[:,-nplex:] = M.elems[:,-1:-(nplex+1):-1].copy() if autofix: M.eltype = defaultEltype(M.nplex()) return M @classmethod def concatenate(clas,meshes,**kargs): """Concatenate a list of meshes of the same plexitude and eltype Merging of the nodes can be tuned by specifying extra arguments that will be passed to :meth:`Coords:fuse`. """ nplex = set([ m.nplex() for m in meshes ]) if len(nplex) > 1: raise ValueError,"Cannot concatenate meshes with different plexitude: %s" % str(nplex) eltype = set([ m.eltype for m in meshes if m.eltype is not None ]) if len(eltype) > 1: raise ValueError,"Cannot concatenate meshes with different eltype: %s" % str(eltype) if len(eltype) == 1: eltype = eltype.pop() else: eltype = None prop = [m.prop for m in meshes] if None in prop: prop = None else: prop = concatenate(prop) coords,elems = mergeMeshes(meshes,**kargs) elems = concatenate(elems,axis=0) #print coords,elems,prop,eltype return Mesh(coords,elems,prop,eltype) # Test and clipping functions def test(self,nodes='all',dir=0,min=None,max=None,atol=0.): """Flag elements having nodal coordinates between min and max. This function is very convenient in clipping a TriSurface in a specified direction. It returns a 1D integer array flagging (with a value 1 or True) the elements having nodal coordinates in the required range. Use where(result) to get a list of element numbers passing the test. Or directly use clip() or cclip() to create the clipped TriSurface The test plane can be defined in two ways, depending on the value of dir. If dir == 0, 1 or 2, it specifies a global axis and min and max are the minimum and maximum values for the coordinates along that axis. Default is the 0 (or x) direction. Else, dir should be compaitble with a (3,) shaped array and specifies the direction of the normal on the planes. In this case, min and max are points and should also evaluate to (3,) shaped arrays. nodes specifies which nodes are taken into account in the comparisons. It should be one of the following: - a single (integer) point number (< the number of points in the Formex) - a list of point numbers - one of the special strings: 'all', 'any', 'none' The default ('all') will flag all the elements that have all their nodes between the planes x=min and x=max, i.e. the elements that fall completely between these planes. One of the two clipping planes may be left unspecified. """ if min is None and max is None: raise ValueError,"At least one of min or max have to be specified." f = self.coords[self.elems] if type(nodes)==str: nod = range(f.shape[1]) else: nod = nodes if type(dir) == int: if not min is None: T1 = f[:,nod,dir] > min if not max is None: T2 = f[:,nod,dir] < max else: if min is not None: T1 = f.distanceFromPlane(min,dir) > -atol if max is not None: T2 = f.distanceFromPlane(max,dir) < atol if min is None: T = T2 elif max is None: T = T1 else: T = T1 * T2 if len(T.shape) == 1: return T if nodes == 'any': T = T.any(axis=1) elif nodes == 'none': T = (1-T.any(1)).astype(bool) else: T = T.all(axis=1) return T def clip(self,t): """Return a TriSurface with all the elements where t>0. t should be a 1-D integer array with length equal to the number of elements of the TriSurface. The resulting TriSurface will contain all elements where t > 0. """ return self.select(t>0) def cclip(self,t): """This is the complement of clip, returning a TriSurface where t<=0. """ return self.select(t<=0) def clipAtPlane(self,p,n,nodes='any',side='+'): """Return the Mesh clipped at plane (p,n). This is a convenience function returning the part of the Mesh at one side of the plane (p,n) """ if side == '-': n = -n return self.clip(self.test(nodes=nodes,dir=n,min=p)) # ?? IS THIS DEFINED FOR *ANY* MESH ?? def equiAngleSkew(self): """Returns the equiAngleSkew of the elements, a mesh quality parameter . It quantifies the skewness of the elements: normalize difference between the worst angle in each element and the ideal angle (angle in the face of an equiangular element, qe). """ eang=self.getAngles(Deg) eangsh= eang.shape eang= eang.reshape(eangsh[0], eangsh[1]*eangsh[2]) eang.max(axis=1), eang.min(axis=1) eangMax, eangmin=eang.max(axis=1), eang.min(axis=1) el = getattr(elements,self.eltype.capitalize()) nedginface= len( el.faces[0] ) qe=180.*(nedginface-2.)/nedginface extremeAngles= [ (eangMax-qe)/(180.-qe), (qe-eangmin)/qe ] return array(extremeAngles).max(axis=0)
def createFrameModel(): """Create the Finite Element Model. It is supposed here that the Geometry has been created and is available as a global variable F. """ wireframe() lights(False) # Turn the Formex structure into a TriSurface # This guarantees that element i of the Formex is element i of the TriSurface S = TriSurface(F) nodes = S.coords elems = S.elems # the triangles # Create edges and faces from edges print "The structure has %s nodes, %s edges and %s faces" % (S.ncoords(),S.nedges(),S.nfaces()) # Create the steel structure E = Formex(nodes[S.getEdges()]) clear() draw(E) # Get the tri elements that are part of a quadrilateral: prop = F.prop quadtri = S.getFaceEdges()[prop==6] nquadtri = quadtri.shape[0] print "%s triangles are part of quadrilateral faces" % nquadtri if nquadtri > 0: # Create triangle definitions of the quadtri faces tri = Connectivity.tangle(quadtri,S.getEdges()) D = Formex(nodes[tri]) clear() flatwire() draw(D,color='yellow') conn = connections(quadtri) print conn # Filter out the single connection edges internal = [ c[0] for c in conn if len(c[1]) > 1 ] print "Internal edges in quadrilaterals: %s" % internal E = Formex(nodes[S.getEdges()],1) E.prop[internal] = 6 wireframe() clear() draw(E) # Remove internal edges tubes = S.getEdges()[E.prop != 6] print "Number of tube elements after removing %s internals: %s" % (len(internal),tubes.shape[0]) D = Formex(nodes[tubes],1) clear() draw(D) # Beam section and material properties b = 60 h = 100 t = 4 b1 = b-2*t h1 = h-2*t A = b*h - b1*h1 print b*h**3 I1 = (b*h**3 - b1*h1**3) / 12 I2 = (h*b**3 - h1*b1**3) / 12 I12 = 0 J = 4 * A**2 / (2*(b+h)/t) tube = { 'name':'tube', 'cross_section': A, 'moment_inertia_11': I1, 'moment_inertia_22': I2, 'moment_inertia_12': I12, 'torsional_constant': J } steel = { 'name':'steel', 'young_modulus' : 206000, 'shear_modulus' : 81500, 'density' : 7.85e-9, } print tube print steel tubesection = ElemSection(section=tube,material=steel) # Calculate the nodal loads # Area of triangles area,normals = S.areaNormals() print "Area:\n%s" % area # compute bar lengths bars = nodes[tubes] barV = bars[:,1,:] - bars[:,0,:] barL = sqrt((barV*barV).sum(axis=-1)) print "Member length:\n%s" % barL ### DEFINE LOAD CASE (ask user) ### res = askItems([('Steel',True), ('Glass',True), ('Snow',False), ('Solver',None,'select',['Calpy','Abaqus']), ]) if not res: return nlc = 0 for lc in [ 'Steel','Glass','Snow' ]: if res[lc]: nlc += 1 NODLoad = zeros((nlc,S.ncoords(),3)) nlc = 0 if res['Steel']: # the STEEL weight lwgt = steel['density'] * tube['cross_section'] * 9810 # mm/s**2 print "Weight per length %s" % lwgt # assemble steel weight load for e,L in zip(tubes,barL): NODLoad[nlc,e] += [ 0., 0., - L * lwgt / 2 ] nlc += 1 if res['Glass']: # the GLASS weight wgt = 450e-6 # N/mm**2 # assemble uniform glass load for e,a in zip(S.elems,area): NODLoad[nlc,e] += [ 0., 0., - a * wgt / 3 ] nlc += 1 if res['Snow']: # NON UNIFORM SNOW fn = 'hesperia-nieve.prop' snowp = fromfile(fn,sep=',') snow_uniform = 320e-6 # N/mm**2 snow_non_uniform = { 1:333e-6, 2:133e-6, 3:133e-6, 4:266e-6, 5:266e-6, 6:667e-6 } # assemble non-uniform snow load for e,a,p in zip(S.elems,area,snowp): NODLoad[nlc,e] += [ 0., 0., - a * snow_non_uniform[p] / 3] nlc += 1 # For Abaqus: put the nodal loads in the properties database print NODLoad PDB = PropertyDB() for lc in range(nlc): for i,P in enumerate(NODLoad[lc]): PDB.nodeProp(tag=lc,set=i,cload=[P[0],P[1],P[2],0.,0.,0.]) # Get support nodes botnodes = where(isClose(nodes[:,2], 0.0))[0] bot = nodes[botnodes] pf.message("There are %s support nodes." % bot.shape[0]) # Upper structure nnodes = nodes.shape[0] # node number offset ntubes = tubes.shape[0] # element number offset PDB.elemProp(set=arange(ntubes),section=tubesection,eltype='FRAME3D') # Create support systems (vertical beams) bot2 = bot + [ 0.,0.,-200.] # new nodes 200mm below bot botnodes2 = arange(botnodes.shape[0]) + nnodes # node numbers nodes = concatenate([nodes,bot2]) supports = column_stack([botnodes,botnodes2]) elems = concatenate([tubes,supports]) ## !!! ## THIS SHOULD BE FIXED !!! supportsection = ElemSection(material=steel,section={ 'name':'support', 'cross_section': A, 'moment_inertia_11': I1, 'moment_inertia_22': I2, 'moment_inertia_12': I12, 'torsional_constant': J }) PDB.elemProp(set=arange(ntubes,elems.shape[0]),section=supportsection,eltype='FRAME3D') # Finally, the botnodes2 get the support conditions botnodes = botnodes2 ## # Radial movement only ## np_fixed = NodeProperty(1,bound=[0,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1]) ## # No movement, since we left out the ring beam ## for i in botnodes: ## NodeProperty(i,bound=[1,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1]) ## np_central_loaded = NodeProperty(3, displacement=[[1,radial_displacement]],coords='cylindrical',coordset=[0,0,0,0,0,1]) ## #np_transf = NodeProperty(0,coords='cylindrical',coordset=[0,0,0,0,0,1]) # Draw the supports S = connect([Formex(bot),Formex(bot2)]) draw(S,color='black') if res['Solver'] == 'Calpy': fe_model = Dict(dict(solver='Calpy',nodes=nodes,elems=elems,prop=PDB,loads=NODLoad,botnodes=botnodes,nsteps=nlc)) else: fe_model = Dict(dict(solver='Abaqus',nodes=nodes,elems=elems,prop=PDB,botnodes=botnodes,nsteps=nlc)) export({'fe_model':fe_model}) print "FE model created and exported as 'fe_model'"
from scipy.interpolate import InterpolatedUnivariateSpline from gaussian import Gaussian from laminate_fem import LaminateFEM from connectivity import Connectivity import scipy.sparse as sparse """ """ material = materials.PiezoMumpsMaterial() cantilever = cantilevers.InitialCantileverFixedTip() la = laminate_analysis.LaminateAnalysis(cantilever, material, True) fem = LaminateFEM(cantilever, material, True) connectivity = Connectivity(fem.mesh) gaussian = Gaussian(fem, fem.cantilever, 0.1) index = 100 # the index of the pseudo-density to vary. ps = np.arange(0.02, 1, 0.01) netas = np.empty_like(ps) dnetas = np.empty_like(ps) dnn = np.empty_like(ps) print('Number of points: %d' % len(ps)) pnew = la.fem.density.copy() tau_all, tau_free = la.connectivity.thermal_analysis() mu = la.connectivity.get_connectivity_penalty(tau_all) guu = gaussian.get_operator() mdofmat = la.mdofmat
def endPoints(parts): """Find the end points of all parts""" ep = Coords.concatenate([ p.coords[[0,-1]] for p in parts ]) endpoints, ind = ep.fuse() ind = Connectivity(ind.reshape(-1,2)) return endpoints,ind
"""" D0 = GPIO16; D1 = GPIO5; D2 = GPIO4; D3 = GPIO0; D4 = GPIO2; D5 = GPIO14; D6 = GPIO12; D7 = GPIO13; D8 = GPIO15; D9 = GPIO3; D10 = GPIO1; LED_BUILTIN = GPIO16 """ from connectivity import Connectivity sv = Connectivity() if not sv.backup(): sv.server() sv.update() sv.wifi() gc.collect()
def GetHull(self): self.find_extreme_points() faces = self.find_simplex() connectivity = Connectivity(faces) dupPoints = set() vert_faces = [None] * len(self.mesh.verts) for i in range(len(self.mesh.verts)): for f in faces: if f.is_dup(i): dupPoints.add(i) break if i in dupPoints: continue for f in faces: if f.add_point(i): if vert_faces[i] == None: vert_faces[i] = set() vert_faces[i].add(f) finalFaces = FaceSet(self.mesh) donePoints = set() iter = -1 bw = None while len(faces): iter += 1 if self.dump_faces: bw = BinaryWriter(open(f"/tmp/quickhull-{iter:05d}.bin", "wb")) self.mesh.write(bw) faces.write(bw) finalFaces.write(bw) f = faces.pop() if not f.vispoints: finalFaces.add(f) iter -= 1 continue point = f.highest_point if f.dist(point) < 1e-12: #FIXME epsilon varies with data # the highest point is essentially on the face (possibly to # the side), so all points are on the face. Thus, this face # is done for p in f.vispoints: vert_faces[p].remove(f) f.vispoints.clear() finalFaces.add(f) iter -= 1 continue litFaces = connectivity.light_faces(f, point, vert_faces) vispoints = connectivity.remove(litFaces) #print(vispoints) for p in vispoints: vert_faces[p] -= litFaces.faces if bw: bw.write_int(point) litFaces.write(bw) horizonEdges = litFaces.find_outer_edges() newFaces = FaceSet(self.mesh) for e in horizonEdges: if e.touches_point(point): re = e.reverse t = connectivity[re] splitEdge = t.find_edge(re) if splitEdge >= 0: self.split_triangle(t, splitEdge, point, connectivity, vert_faces) else: tri = Triangle(self.mesh, e.a, e.b, point) newFaces.add(tri) connectivity.add(tri) donePoints.clear() for lf in litFaces: for p in lf.vispoints: if p in donePoints: continue donePoints.add(p) if vert_faces[p] == None: vert_faces[p] = set() for nf in newFaces: if nf.is_dup(p): dupPoints.add(p) p = -1 break if p < 0: continue for nf in newFaces: if nf.add_point(p): vert_faces[p].add(nf) for p, vf in enumerate(vert_faces): if not vf or p in donePoints: continue donePoints.add(p) for nf in newFaces: if nf.add_point(p): vert_faces[p].add(nf) if bw: newFaces.write(bw) for nf in set(newFaces.faces): if nf.vispoints: faces.add(nf) else: finalFaces.add(nf) if bw: bw.close() bw = None if connectivity.error: vis = set() for lf in litFaces: for vp in lf.vispoints: vis.add(vp) for lf in litFaces: dist1 = 1e38 dist2 = 1e38 for i in range(3): d = lf.edges[i].distance(point) if d < dist1: dist1 = d v = self.mesh.verts[point] v = sub(v, self.mesh.verts[lf.edges[i].a]) d = sqrt(dot(v, v)) if d < dist2: dist2 = d break if self.dump_faces and not connectivity.error: iter += 1 bw = BinaryWriter(open(f"/tmp/quickhull-{iter:05d}.bin", "wb")) self.mesh.write(bw) faces.write(bw) finalFaces.write(bw) bw.write_int(-1) bw.write_int(0) bw.write_int(0) bw.close() self.error = connectivity.error return finalFaces
def endPoints(parts): """Find the end points of all parts""" ep = Coords.concatenate([p.coords[[0, -1]] for p in parts]) endpoints, ind = ep.fuse() ind = Connectivity(ind.reshape(-1, 2)) return endpoints, ind
def main_menu(self, token): self.token = token main_answer = questionary.select( "What do you want to do?", choices=[ 'Connectivity', 'Device Setting', 'User Management', 'Current Status', 'Logout' ]).ask() # returns value of selection if(main_answer == 'Connectivity'): connectivity_answer = self.connectivity_menu() if(connectivity_answer == 'Connect'): connector = input("Enter Connector: ") adaptor = input("Enter Adaptor: ") date_time = input( "Enter Date and Time: (format: 2019-Aug-28 10:10:00)\n") a = Connectivity() a.connect(connector, adaptor, date_time, self.token) elif(connectivity_answer == 'Disconnect'): connector = input("Enter Connector: ") adaptor = input("Enter Adaptor: ") date_time = input( "Enter Date and Time: (format: 2019-Aug-28 10:10:00)\n") a = Connectivity() a.recovery(connector, adaptor, date_time, self.token) elif(connectivity_answer == 'Recovery'): connector = input("Enter Connector: ") adaptor = input("Enter Adaptor: ") date_time = input( "Enter Date and Time: (format: 2019-Aug-28 10:10:00)\n") a = Connectivity() a.disconnect(connector, adaptor, date_time, self.token) elif(connectivity_answer == 'Calibrate'): connector = input("Enter Connector: ") adaptor = input("Enter Adaptor: ") date_time = input( "Enter Date and Time: (format: 2019-Aug-28 10:10:00)\n") a = Connectivity() a.calibrate(connector, adaptor, date_time, self.token) elif(connectivity_answer == 'Queue Management'): a = Connectivity() a.queue_manage(self.token) elif(main_answer == 'Device Setting'): print('2') elif(main_answer == 'User Management'): print('3') elif(main_answer == 'Current Status'): print('4') elif(main_answer == 'Logout'): print('--Goodbye--') return -1 return 0