def gtsinside(self,pts,dir=0): """_Test whether points are inside the surface. pts is a plex-1 Formex. dir is the shooting direction. Returns a list of point numbers that are inside. This is not intended to be used directly. Use inside instead """ import os print(os.environ) S = self.rollAxes(dir) P = pts.rollAxes(dir) tmp = utils.tempFile(suffix='.gts').name tmp1 = utils.tempFile(suffix='.dta').name tmp2 = utils.tempFile(suffix='.out').name pf.message("Writing temp file %s" % tmp) S.write(tmp,'gts') pf.message("Writing temp file %s" % tmp1) f = open(tmp1,'w') P.coords.tofile(f,sep=' ') f.write('\n') f.close() pf.message("Performing inside testing") cmd = "gtsinside %s %s > %s" % (tmp,tmp1,tmp2) sta,out = utils.runCommand(cmd) os.remove(tmp) os.remove(tmp1) if sta: pf.message("An error occurred during the testing.\nSee file %s for more details." % tmp2) return None pf.message("Reading results from %s" % tmp2) ind = fromfile(tmp2,sep=' ',dtype=Int) return ind
def workHere(): """Change the current working directory to the script's location. This function is deprecated: use chdir(_file__) instead. """ GD.message("workHere is deprecated: use chdir(_file__) instead") chdir(GD.PF.get("curfile", ""))
def printBbox(self): """Print the bbox of the current selection.""" objects = self.check() for n,o in zip(self.names,objects): GD.message("Object %s has bbox %s" % (n,o.bbox())) if len(self.names) > 1: GD.message("Overal bbox is %s" % bbox(objects))
def create_tetgen_volume(): """Generate a volume tetraeder mesh inside an stl surface.""" types = [ 'STL/OFF Files (*.stl *.off)', 'All Files (*)' ] fn = askFilename(GD.cfg['workdir'],types) if os.path.exists(fn): sta,out = utils.runCommand('tetgen -z %s' % fn) GD.message(out)
def printSettings(self): for i,v in enumerate(self.all): pf.message(""" ## VIEWPORTS ## Viewport %s; Current:%s; Settings: %s """ % (i, v == self.current, v.settings))
def read_Formex(fn): GD.message("Reading file %s" % fn) t = timer.Timer() F = Formex.read(fn) nelems,nplex = F.f.shape[0:2] GD.message("Read %d elems of plexitude %d in %s seconds" % (nelems,nplex, t.seconds())) return F
def __init__(self,coords,elems): """Create new model data. coords is an array with nodal coordinates elems is either a single element connectivity array, or a list of such. In a simple case, coords and elems can be the arrays obtained by ``coords, elems = F.feModel()``. This is however limited to a model where all elements have the same number of nodes. Then you can use the list of elems arrays. The 'fe' plugin has a helper function to create this list. E.g., if ``FL`` is a list of Formices (possibly with different plexitude), then ``fe.mergeModels([Fi.feModel() for Fi in FL])`` will return the (coords,elems) tuple to create the Model. The model can have node and element property numbers. """ #Dict.__init__(self) if not type(elems) == list: elems = [ elems ] self.coords = Coords(coords) self.elems = [ Connectivity(e) for e in elems ] self.meshes = [ Mesh(self.coords,e) for e in self.elems ] nnodes = [ m.nnodes() for m in self.meshes ] nelems = [ m.nelems() for m in self.meshes ] nplex = [ m.nplex() for m in self.meshes ] self.cnodes = cumsum([0]+nnodes) self.celems = cumsum([0]+nelems) pf.message("Number of nodes: %s" % self.coords.shape[0]) pf.message("Number of elements: %s" % self.celems[-1]) pf.message("Number of element groups: %s" % len(nelems)) #pf.message("Number of nodes per group: %s" % nnodes) pf.message("Number of elements per group: %s" % nelems) pf.message("Plexitude of each group: %s" % nplex)
def pick_parts(self,obj_type,max_objects,store_closest=False): """Set the list of actor parts inside the pick_window. obj_type can be 'element', 'point' or 'edge'(SurfaceActor only) max_objects specifies the maximum number of objects The picked object numbers are stored in self.picked. If store_closest==True, the closest picked object is stored in as a tuple ( [actor,object] ,distance) in self.picked_closest """ self.picked = [] if max_objects <= 0: GD.message("No such objects to be picked!") return self.camera.loadProjection(pick=self.pick_window) self.camera.loadMatrix() stackdepth = 2 selbuf = GL.glSelectBuffer(max_objects*(3+stackdepth)) GL.glRenderMode(GL.GL_SELECT) GL.glInitNames() for i,a in enumerate(self.actors): GL.glPushName(i) a.pickGL(obj_type) # this will push the number of the part GL.glPopName() self.picked = [] libGL.glRenderMode(GL.GL_RENDER) if selbuf[0] > 0: buf = asarray(selbuf).reshape(-1,3+selbuf[0]) buf = buf[buf[:,0] > 0] self.picked = buf[:,3:] if store_closest and len(buf) > 0: w = buf[:,1].argmin() self.closest_pick = (self.picked[w], buf[w,1])
def write_stl_asc(fn,x): """Write a collection of triangles to an ascii .stl file. Parameters: - `fn`: file name, by preference ending with '.stl' or '.stla' - `x`: (ntriangles,3,3) shaped array with the vertices of the triangles """ if not x.shape[1:] == (4,3): raise ValueError,"Expected an (ntri,4,3) array, got %s" % x.shape pf.message("Writing ascii STL %s" % fn) with open(fn,'wb') as fil: fil.write("solid Created by %s\n" % pf.fullVersion()) for e in x: fil.write(" facet normal %s %s %s\n" % tuple(e[0])) fil.write(" outer loop\n") for p in e[1:]: fil.write(" vertex %s %s %s\n" % tuple(p)) fil.write(" endloop\n") fil.write(" endfacet\n") fil.write("endsolid\n") pf.message("Finished writing ascii STL, %s bytes" % utils.fileSize(fn))
def checkResultsOnServer(host=None,userdir=None): """Get a list of job results from the cluster. Specify userdir='bumper/running' to get a list of running jobs. """ global the_host,the_userdir,the_jobnames if host is None or userdir is None: res = askItems([ ('host',None,'select',{'choices':['bumpfs','bumpfs2','other']}), ('other','',{'text':'Other host name'}), ('status',None,'select',{'choices':['results','running','custom']}), ('userdir','bumper/results/',{'text':'Custom user directory'}), ]) if not res: return host = res['host'] if host == 'other': host = res['other'] status = res['status'] if status in ['results','running']: userdir = 'bumper/%s/' % status else: userdir = res['userdir'] jobnames = getSubdirs(host,userdir) if jobnames: the_host = host the_userdir = userdir the_jobnames = jobnames else: the_host = None the_userdir = None the_jobnames = None pf.message(the_jobnames)
def pick(mode='actor',single=False,func=None,filtr=None): """Enter interactive picking mode and return selection. See viewport.py for more details. This function differs in that it provides default highlighting during the picking operation, a button to stop the selection operation If no filter is given, the available filters are presented in a combobox. """ if GD.canvas.selection_mode is not None: warning("You need to finish the previous picking operation first!") return pick_buttons = widgets.ButtonBox('Selection:',['Cancel','OK'],[GD.canvas.cancel_selection,GD.canvas.accept_selection]) GD.GUI.statusbar.addWidget(pick_buttons) if mode == 'element': filters = selection_filters else: filters = selection_filters[:3] filter_combo = widgets.ComboBox('Filter:',filters,set_selection_filter) if filtr is not None and filtr in selection_filters: i = selection_filters.index(filtr) filter_combo.setIndex(i) GD.GUI.statusbar.addWidget(filter_combo) if func is None: func = highlight_funcs.get(mode,None) GD.message("Select %s %s" % (filtr,mode)) sel = GD.canvas.pick(mode,single,func,filtr) GD.GUI.statusbar.removeWidget(pick_buttons) GD.GUI.statusbar.removeWidget(filter_combo) return sel
def detect(trypaths=None): """Check if we have calpy and if so, add its path to sys.path.""" global calpy_path calpy = utils.checkExternal('calpy') if not calpy: return pf.message("You have calpy version %s" % calpy) path = '' calpy = calpy.split('-')[0] # trim the version trailer if utils.checkVersion('calpy','0.3.4-rev3',external=True) >= 0: sta,out = utils.runCommand('calpy --whereami') if not sta: path = out pf.debug("I found calpy in %s" % path) if not path: if trypaths is None: trypaths = [ '/usr/local/lib', '/usr/local' ] for p in trypaths: path = '%s/calpy-%s' % (p,calpy) if os.path.exists(path): pf.debug('path exists: %s' % path) break else: pf.debug('path does not exist: %s' % path) path = '' if path: #path += '/calpy' pf.message("I found calpy in '%s'" % path) sys.path.append(path) calpy_path = path
def inputControlLines(): """Enter three polyline paths in counterclockwise direction.""" branch = [] BA = [] perspective(False) for i in range(6): pf.message("Input Branch %s" % i) if i % 2 == 0: coords = None else: coords = branch[i-1].coords[-1:] obj = d2.drawObject2D(mode='polyline',npoints=-1,coords=coords,zvalue=0.) obj.specular = 0. pf.canvas.removeHighlight() ## WHY is bbox='last' or zoomAll needed here if obj is not None: BA.append(draw(obj,color='blue',flat=True)) zoomAll() branch.append(obj) else: break if len(branch) == 6: undraw(BA) createBranches(branch) zoomAll() else: warning("Incorrect definition of helper lines")
def closeProject(save=None,clear=None): """Close the current project, saving it or not. Parameters: - `save`: None, True or False. Determines whether the project should be saved prior to closing it. If None, it will be asked from the user. Note that this parameter is only used for named Projects. Temporary Projects are never saved implicitely. - `clear`: None, True or False. """ if pf.PF.filename is not None: if save is None: save = draw.ack("Save the current project before closing it?") pf.message("Closing project %s (save=%s)" % (pf.PF.filename,save)) if save: saveProject() if pf.PF: listProject() if clear is None: clear = draw.ask("What shall I do with the existing globals?",["Delete","Keep"]) == "Delete" if clear: pf.PF.clear() pf.PF.filename = None pf.GUI.setcurproj('None') updateSettings({ 'curproj':pf.PF.filename, },save=True)
def readMeshFile(fn): """Read a nodes/elems model from file. Returns a dict: - `coords`: a Coords with all nodes - `elems`: a list of Connectivities - `esets`: a list of element sets """ d = {} fil = open(fn,'r') for line in fil: if line[0] == '#': line = line[1:] globals().update(getParams(line)) dfil = open(filename,'r') if mode == 'nodes': d['coords'] = readNodes(dfil) elif mode == 'elems': elems = d.setdefault('elems',[]) e = readElems(dfil,int(nplex)) - int(offset) elems.append(e) elif mode == 'esets': d['esets'] = readEsets(dfil) else: pf.message("Skipping unrecognized line: %s" % line) dfil.close() fil.close() return d
def importProject(): """Import an existing project. Ask the user to select an existing project file, and then import all or selected data from it into the current project. """ proj = openProject(exist=True,access='r') if proj: # only if non-empty keys = utils.sortedKeys(proj) res = draw.askItems( [ _I('mode',choices=['All','Defined','Undefined','Selected','None'],itemtype='radio'), _I('selected',choices=keys,itemtype='list'), ], caption='Select variables to import', ) if res: mode = res['mode'][0] if mode == 'A': pass elif mode == 'D': proj = utils.selectDict(proj,pf.PF) elif mode == 'U': proj = utils.removeDict(proj,pf.PF) elif mode == 'S': proj = utils.selectDict(proj,res['selected']) elif mode == 'N': return pf.message("Importing symbols: %s" % utils.sortedKeys(proj)) pf.PF.update(proj) listProject()
def clip_surface(): """Clip the stl model.""" if not check_surface(): return itemlist = [['axis',0],['begin',0.0],['end',1.0],['nodes','any']] res = askItems(itemlist,caption='Clipping Parameters') if res: updateGUI() nodes,elems = PF['old_surface'] = PF['surface'] F = Formex(nodes[elems]) bb = F.bbox() GD.message("Original bbox: %s" % bb) xmi = bb[0][0] xma = bb[1][0] dx = xma-xmi axis = int(res[0][1]) xc1 = xmi + float(res[1][1]) * dx xc2 = xmi + float(res[2][1]) * dx nodid = res[3][1] #print(nodid) clear() draw(F,color='yellow') w = F.test(nodes='any',dir=axis,min=xc1,max=xc2) F = F.clip(w) draw(F,color='red')
def write(self,geom,name=None,sep=None): """Write any geometry object to the geometry file. `geom` is one of the Geometry data types of pyFormex or a list or dict of such objects. Currently exported geometry objects are :class:`Coords`, :class:`Formex`, :class:`Mesh`, :class:`PolyLine`, :class:`BezierSpline`. The geometry object is written to the file using the specified separator, or the default. """ self.checkWritable() if isinstance(geom,dict): for name in geom: self.write(geom[name],name,sep) elif isinstance(geom,list): if name is None: for obj in geom: self.write(obj,None,sep) else: name = utils.NameSequence(name) for obj in geom: self.write(obj,name.next(),sep) elif hasattr(geom,'write_geom'): geom.write_geom(self,name,sep) else: try: writefunc = getattr(self,'write'+geom.__class__.__name__) #print writefunc writefunc(geom,name,sep) except: message("Can not (yet) write objects of type %s to geometry file: skipping" % type(geom))
def readSelection(select=True,draw=True,multi=True): """Read a Formex (or list) from asked file name(s). If select is True (default), this becomes the current selection. If select and draw are True (default), the selection is drawn. """ types = utils.fileDescription(['pgf','all']) fn = askFilename(GD.cfg['workdir'],types,multi=multi) if fn: if not multi: fn = [ fn ] chdir(fn[0]) res = ODict() GD.GUI.setBusy() for f in fn: res.update(readGeomFile(f)) GD.GUI.setBusy(False) export(res) if select: oknames = [ k for k in res if isinstance(res[k],Formex) ] selection.set(oknames) GD.message("Set Formex selection to %s" % oknames) if draw: selection.draw() return fn
def readSelection(select=True,draw=True,multi=True): """Read a Surface (or list) from asked file name(s). If select is True (default), this becomes the current selection. If select and draw are True (default), the selection is drawn. """ types = [ 'Surface Files (*.gts *.stl *.off *.neu *.smesh)', 'All Files (*)' ] fn = askFilename(GD.cfg['workdir'],types,multi=multi) if fn: if not multi: fn = [ fn ] chdir(fn[0]) names = map(utils.projectName,fn) GD.GUI.setBusy() surfaces = [ read_Surface(f,False) for f in fn ] for i,S in enumerate(surfaces): S.setProp(i) GD.GUI.setBusy(False) export(dict(zip(names,surfaces))) if select: GD.message("Set selection to %s" % str(names)) selection.set(names) if draw: if max([named(s).nfaces() for s in selection]) < 100000 or ack(""" This is a large model and drawing could take quite some time. You should consider coarsening the model before drawing it. Shall I proceed with the drawing now? """): selection.draw() return fn
def pause(msg="Use the Step/Continue buttons to proceed"): if msg: GD.message(msg) if allowwait: drawblock() # will need external event to release it while (drawlocked): sleep(0.5) GD.app.processEvents()
def printDataSize(): for s in selection.names: S = named(s) try: size = S.info() except: size = 'no info available' pf.message("* %s (%s): %s" % (s,S.__class__.__name__,size))
def printbbox(self): """Print the bbox of the current selection.""" objects = self.check() if objects: for n,o in zip(self.names,objects): bb = o.bbox() pf.message("* %s (%s): bbox [%s, %s]" % (n,o.__class__.__name__,bb[0],bb[1])) if len(self.names) > 1: pf.message("** Overal bbox: [%s, %s]" % (bb[0],bb[1]))
def selectStepInc(self): res = askItems([('Step',self.DB.step,'select',self.DB.res.keys())]) if res: step = int(res['Step']) res = askItems([('Increment',None,'select',self.DB.res[step].keys())]) if res: inc = int(res['Increment']) GD.message("Step %s; Increment %s;" % (step,inc)) self.DB.setStepInc(step,inc)
def export_webgl(): F = selection.check(single=True) if F: types = map(utils.fileDescription,['stl']) fn = askNewFilename(pf.cfg['workdir'],types) if fn: pf.message("Exporting surface model to %s" % fn) pf.GUI.setBusy() F.webgl(fn) pf.GUI.setBusy(False)
def runCommand(cmd,RaiseError=True,quiet=False): """Run a command and raise error if exited with error.""" if not quiet: pf.message("Running command: %s" % cmd) sta,out = commands.getstatusoutput(cmd) if sta != 0: pf.message(out) if RaiseError: raise RuntimeError, "Error while executing command:\n %s" % cmd return sta,out
def read_Surface(fn,exportName=True): GD.message("Reading file %s" % fn) t = timer.Timer() S = TriSurface.read(fn) GD.message("Read surface with %d vertices, %d edges, %d triangles in %s seconds" % (S.ncoords(),S.nedges(),S.nelems(),t.seconds())) if exportName: name = utils.projectName(fn) export({name:S}) selection.set([name]) return S
def show_volume(): """Display the volume model.""" if PF["volume"] is None: return nodes, elems = PF["volume"] F = Formex(nodes[elems], eltype="tet4") pf.message("BBOX = %s" % F.bbox()) clear() draw(F, color="random") PF["vol_model"] = F
def closeProject(): global the_project,the_project_saved if not (the_project_saved or the_project is None): GD.message("Closing project %s" % the_project.filename) GD .message("Project contents: %s" % the_project.keys()) the_project.save() GD.PF = {} GD.PF.update(the_project) GD.gui.setcurproj('None') the_project = None
def show_volume(): """Display the volume model.""" if PF['volume'] is None: return nodes,elems = PF['volume'] F = Formex(nodes[elems],eltype='tet4') GD.message("BBOX = %s" % F.bbox()) clear() draw(F,color='random') PF['vol_model'] = F
def readInpFile(filename): """Read the geometry from an Abaqus/Calculix .inp file This is a replacement for the convertInp/readMeshFile combination. It uses the ccxinp plugin to provide a direct import of the Finite Element meshes from an Abaqus or Calculix input file. Currently still experimental and limited in functionality (aimed primarily at Calculix). But also many simple meshes from Abaqus can already be read. Returns an dict. """ from plugins import ccxinp, fe ccxinp.skip_unknown_eltype = True model = ccxinp.readInput(filename) pf.message("Number of parts: %s" % len(model.parts)) fem = {} for part in model.parts: try: coords = Coords(part['coords']) nodid = part['nodid'] nodpos = inverseUniqueIndex(nodid) pf.message("nnodes = %s" % coords.shape[0]) pf.message("nodid: %s" % nodid) pf.message("nodpos: %s" % nodpos) for e in part['elems']: pf.message("Orig els %s" % e[1]) pf.message("Trl els %s" % nodpos[e[1]]) elems = [ Connectivity(nodpos[e], eltype=t) for (t, e) in part['elems'] ] pf.message('ELEM TYPES: %s' % [e.eltype for e in elems]) fem[part['name']] = fe.Model(coords, elems) except: pf.message("Skipping part %s" % part['name']) return fem
def read(self, count=-1): """Read a pyFormex Geometry File. fil is a filename or a file object. If the file is in a valid pyFormex geometry file format, geometry objects are read from the file and returned in a dictionary. The object names are used as keys. If the file does not contain object names, they will be autogenerated from the file name. A count may be specified to limit the number of objects read. If the file format is invalid and no valid geometry could be read, None is returned. Valid pyFormex geometry file formats are described in the manual. """ eltype = None # for compatibility with pre 1.1 .formex files ndim = 3 while True: objtype = 'Formex' # the default obj type obj = None nelems = None ncoords = None sep = self.sep name = None normals = None color = None s = self.fil.readline() if len(s) == 0: # end of file break if not s.startswith('#'): # not a header: skip continue ### ### THIS WILL USE UNDEFINED VALUES FROM A PREVIOUS OBJECT ### THIS SHOULD THEREFORE BE CHANGED !!!! ### try: exec(s[1:].strip()) print("READ COLOR: %s" % str(color)) except: nelems = ncoords = None if nelems is None and ncoords is None: # For historical reasons, this is a certain way to test # that no geom data block is following print("SKIPPING %s" % s) continue # not a legal header: skip debug("Reading object of type %s" % objtype, DEBUG.INFO) # OK, we have a legal header, try to read data if objtype == 'Formex': obj = self.readFormex(nelems, nplex, props, eltype, sep) elif objtype in ['Mesh', 'TriSurface']: obj = self.readMesh(ncoords, nelems, nplex, props, eltype, normals, sep, objtype) elif objtype == 'PolyLine': obj = self.readPolyLine(ncoords, closed, sep) elif objtype == 'BezierSpline': if 'nparts' in s: # THis looks like a version 1.3 BezierSpline obj = self.oldReadBezierSpline(ncoords, nparts, closed, sep) else: if not 'degree' in s: # compatibility with 1.4 BezierSpline records degree = 3 obj = self.readBezierSpline(ncoords, closed, degree, sep) elif objtype == 'NurbsCurve': obj = self.readNurbsCurve(ncoords, nknots, closed, sep) elif objtype in globals() and hasattr(globals()[objtype], 'read_geom'): obj = globals()[objtype].read_geom(self) else: message( "Can not (yet) read objects of type %s from geometry file: skipping" % objtype) continue # skip to next header if obj is not None: try: color = checkArray(color, (3, ), 'f') print("SET OBJECT COLOR TO %s" % color) obj.color = color except: print("NOT SETTING COLOR %s" % str(color)) pass if name is None: name = self.objname.next() self.results[name] = obj if count > 0 and len(self.results) >= count: break if self.isname: self.fil.close() return self.results