# (c) 2010 LGPL #Make mesh of pn junction in TetGen format import FreeCAD, FreeCADGui, Part, Mesh App = FreeCAD # shortcut Gui = FreeCADGui # shortcut def exportMeshToTetGenPoly(meshToExport,filePath,beVerbose=1): """Export mesh to TetGen *.poly file format""" ## Part 1 - write node list to output file if beVerbose == 1: FreeCAD.Console.PrintMessage("\nExport of mesh to TetGen file ...") (allVertices,allFacets) = meshToExport.Topology f = open(filePath, 'w') f.write("# This file was generated from FreeCAD geometry\n") f.write("# Part 1 - node list\n") f.write("%(TotalNumOfPoints)i %(NumOfDimensions)i %(NumOfProperties)i %(BoundaryMarkerExists)i\n" % \ {'TotalNumOfPoints':len(allVertices), \ 'NumOfDimensions':3, \ 'NumOfProperties':0, \ 'BoundaryMarkerExists':0}) for PointIndex in range(len(allVertices)): f.write("%(PointIndex)5i %(x) e %(y) e %(z) e\n" % \ {'PointIndex':PointIndex, \ 'x':allVertices[PointIndex].x, \ 'y':allVertices[PointIndex].y, \ 'z':allVertices[PointIndex].z}) ## Find out BoundaryMarker for each facet. If edge connects only two facets, # then this facets should have the same BoundaryMarker BoundaryMarkerExists = 1 PointList=[allFacets[0][1],allFacets[0][0]] PointList.sort() EdgeFacets = {(PointList[0],PointList[1]):set([0])} Edge = [] # Finde all facets for each edge for FacetIndex in range(len(allFacets)): Facet = allFacets[FacetIndex] for i in range(0,-len(Facet),-1): tmpEdge = [Facet[i],Facet[i+1]] tmpEdge.sort() Edge.append(tmpEdge) for i in range(len(Edge)): EdgeIndex = (Edge[i][0],Edge[i][1]) if EdgeIndex in EdgeFacets: EdgeFacets[EdgeIndex].add(FacetIndex) else: EdgeFacets[EdgeIndex] = set([FacetIndex]) Edge = [] # Find BoundaryMarker for each facet BoundaryMarker = [] for index in range(len(allFacets)): BoundaryMarker.append(0) MinMarker = -1 InitialFacet = 0 BoundaryMarker[InitialFacet] = MinMarker EdgeKeys = EdgeFacets.keys() disconnectedEdges = len(EdgeKeys) if beVerbose == 1: FreeCAD.Console.PrintMessage('\nBoundaryMarker:'+repr(BoundaryMarker)+' '+repr(len(EdgeFacets))) searchForPair = 1 # Main loop: first search for all complementary facets, then fill one branch and repeat while edges are available while len(EdgeFacets)>0: removeEdge = 0 for EdgeIndex in EdgeKeys: if len(EdgeFacets[EdgeIndex]) == 1: removeEdge = 1 break if len(EdgeFacets[EdgeIndex]) == 2: FacetPair = [] for facet in EdgeFacets[EdgeIndex]: FacetPair.append(facet) if (BoundaryMarker[FacetPair[0]]==0) and (BoundaryMarker[FacetPair[1]]==0): continue if (BoundaryMarker[FacetPair[0]]!=0) and (BoundaryMarker[FacetPair[1]]!=0): removeEdge = 1 break if (BoundaryMarker[FacetPair[0]]!=0): BoundaryMarker[FacetPair[1]] = BoundaryMarker[FacetPair[0]] else: BoundaryMarker[FacetPair[0]] = BoundaryMarker[FacetPair[1]] removeEdge = 1 break if searchForPair == 1: continue FacetTree = [] AllMarkers = 1 MarkerSum = 0 for facet in EdgeFacets[EdgeIndex]: FacetTree.append(facet) MarkerSum += BoundaryMarker[facet] if MarkerSum == 0: continue for facet in EdgeFacets[EdgeIndex]: if BoundaryMarker[facet] == 0: MinMarker -= 1 BoundaryMarker[facet] = MinMarker searchForPair = 1 removeEdge = 1 break if removeEdge == 1: del EdgeFacets[EdgeIndex] EdgeKeys = EdgeFacets.keys() continue searchForPair = 0 # End of main loop if beVerbose == 1: FreeCAD.Console.PrintMessage('\nNew BoundaryMarker:'+repr(BoundaryMarker)+' '+repr(len(EdgeFacets))) ## Part 2 - write all facets to *.poly file f.write("# Part 2 - facet list\n") f.write("%(TotalNumOfFacets)i %(BoundaryMarkerExists)i\n" %\ {'TotalNumOfFacets':len(allFacets),\ 'BoundaryMarkerExists':BoundaryMarkerExists}) for FacetIndex in range(len(allFacets)): f.write("# FacetIndex = %(Index)i\n"%{'Index':FacetIndex}) f.write("%(NumOfPolygons)3i "%{'NumOfPolygons':1}) if BoundaryMarkerExists == 1: f.write("0 %(BoundaryMarker)i"%{'BoundaryMarker':BoundaryMarker[FacetIndex]}) f.write("\n%(NumOfConers)3i "%{'NumOfConers':len(allFacets[FacetIndex])}) for PointIndex in range(len(allFacets[FacetIndex])): # f.write(repr(allFacets[FacetIndex][PointIndex])) f.write("%(PointIndex)i "%{'PointIndex':allFacets[FacetIndex][PointIndex]}) f.write("\n") ## Part 3 and Part 4 are zero f.write("# Part 3 - the hole list.\n# There is no hole in bar.\n0\n") f.write("# Part 4 - the region list.\n# There is no region defined.\n0\n") f.write("# This file was generated from FreeCAD geometry\n") f.close() def export(objectslist,filename): """Called when freecad exports a mesh to poly format""" for obj in objectslist: if isinstance(obj,Mesh.Feature): exportMeshToTetGenPoly(obj.Mesh,filename,False) break def createMesh(): ## ======================== Script beginning... ======================== beVerbose = 1 if beVerbose == 1: FreeCAD.Console.PrintMessage("\n\n\n\n\n\n\n\nScript starts...") ## Geometry defenition # Define objects names PyDocumentName = "pnJunction" PSideBoxName = "PSide" NSideBoxName = "NSide" DepletionBoxName = "Depletion" SurfDepletionBoxName = "SurfDepletion" OxideBoxName = "Oxide" AdsorbtionBoxName = "Adsorbtion" pnMeshName = "pnMesh" # Init objects if beVerbose == 1: FreeCAD.Console.PrintMessage("\nInit Objects...") # App.closeDocument(App.ActiveDocument.Label) #closeDocument after restart of macro. Needs any ActiveDocument. AppPyDoc = App.newDocument(PyDocumentName) NSideBox = AppPyDoc.addObject("Part::Box",NSideBoxName) PSideBox = AppPyDoc.addObject("Part::Box",PSideBoxName) DepletionBox = AppPyDoc.addObject("Part::Box",DepletionBoxName) SurfDepletionBox = AppPyDoc.addObject("Part::Box",SurfDepletionBoxName) OxideBox = AppPyDoc.addObject("Part::Box",OxideBoxName) AdsorbtionBox = AppPyDoc.addObject("Part::Box",AdsorbtionBoxName) pnMesh = AppPyDoc.addObject("Mesh::Feature",pnMeshName) BoxList=[NSideBox, DepletionBox, PSideBox, OxideBox, AdsorbtionBox, SurfDepletionBox] NSideBoxMesh = Mesh.Mesh() PSideBoxMesh = Mesh.Mesh() DepletionBoxMesh = Mesh.Mesh() SurfDepletionBoxMesh = Mesh.Mesh() OxideBoxMesh = Mesh.Mesh() AdsorbtionBoxMesh = Mesh.Mesh() BoxMeshList = [NSideBoxMesh, DepletionBoxMesh, PSideBoxMesh, OxideBoxMesh, AdsorbtionBoxMesh, SurfDepletionBoxMesh] if beVerbose == 1: if len(BoxList)!=len(BoxMeshList): FreeCAD.Console.PrintMessage("\n ERROR! Input len() of BoxList and BoxMeshList is not the same! ") ## Set sizes in nanometers if beVerbose == 1: FreeCAD.Console.PrintMessage("\nSet sizes...") tessellationTollerance = 0.05 ModelWidth = 300 BulkHeight = 300 BulkLength = 300 DepletionSize = 50 OxideThickness = 5 AdsorbtionThickness = 10 # Big volumes of n and p material NSideBox.Height = BulkHeight #Z-direction NSideBox.Width = ModelWidth #Y-direction = const NSideBox.Length = BulkLength #X-direction PSideBox.Height = BulkHeight PSideBox.Width = ModelWidth PSideBox.Length = BulkLength # Thin depletion layer between DepletionBox.Height = BulkHeight DepletionBox.Width = ModelWidth DepletionBox.Length = DepletionSize*2 # Surface deplation layer SurfDepletionBox.Height = DepletionSize SurfDepletionBox.Width = ModelWidth SurfDepletionBox.Length = BulkLength*2 + DepletionSize*2 # Oxide on the top OxideBox.Height = OxideThickness OxideBox.Width = ModelWidth OxideBox.Length = BulkLength*2 + DepletionSize*2 # Adsorbtion layer AdsorbtionBox.Height = AdsorbtionThickness AdsorbtionBox.Width = ModelWidth AdsorbtionBox.Length = BulkLength*2 + DepletionSize*2 # Object placement Rot = App.Rotation(0,0,0,1) NSideBox.Placement = App.Placement(App.Vector(0,0,-BulkHeight),Rot) PSideBox.Placement = App.Placement(App.Vector(DepletionSize*2+BulkLength,0,-BulkHeight),Rot) DepletionBox.Placement = App.Placement(App.Vector(BulkLength,0,-BulkHeight),Rot) SurfDepletionBox.Placement = App.Placement(App.Vector(0,0,0),Rot) OxideBox.Placement = App.Placement(App.Vector(0,0,DepletionSize),Rot) AdsorbtionBox.Placement = App.Placement(App.Vector(0,0,DepletionSize+OxideThickness),Rot) ## Unite if beVerbose == 1: FreeCAD.Console.PrintMessage("\nFuse objects...") fuseShape = BoxList[0].Shape for index in range(1,len(BoxList),1): fuseShape = fuseShape.fuse(BoxList[index].Shape) nmesh = Mesh.Mesh() nmesh.addFacets(fuseShape.tessellate(tessellationTollerance)) # for index in range(len(BoxList)): for index in range(len(BoxList)-1): # Manual hack BoxMeshList[index].addFacets(BoxList[index].Shape.tessellate(tessellationTollerance)) nmesh.addMesh(BoxMeshList[index]) nmesh.removeDuplicatedPoints() nmesh.removeDuplicatedFacets() pnMesh.Mesh = nmesh # Hide all boxes for box in BoxList: Gui.hideObject(box) # # Remove all boxes # for box in BoxList: # App.ActiveDocument.removeObject(box.Name) # Update document AppPyDoc.recompute() ## export to TenGen *.poly (use File|Export instead) #filePath = "/home/tig/tmp/tetgen/pnJunction.poly" #exportMeshToTetGenPoly(pnMesh.Mesh,filePath,beVerbose) Gui.activeDocument().activeView().viewAxometric() Gui.SendMsgToActiveView("ViewFit") if beVerbose == 1: FreeCAD.Console.PrintMessage("\nScript finished without errors.")