// implementation of various incarnations of calls // to triangle from the CFemmeDoc class #include "stdafx.h" #include "femm.h" #include "femmeDoc.h" #include "probdlg.h" #include "PtProp.h" #include "OpBlkDlg.h" #include "OpNodeDlg.h" #include "OpSegDlg.h" #include "OpArcSegDlg.h" #include "OpGrp.h" #include "ArcDlg.h" #include "process.h" extern BOOL bLinehook; extern lua_State *lua; extern HANDLE hProc; #define toDegrees(x) ((Im(x)>=0) ? arg(x) : (arg(x) + 2.*PI))*(180./PI) double CFemmeDoc::LineLength(int i) { return abs(nodelist[linelist[i].n0].CC()- nodelist[linelist[i].n1].CC()); } BOOL CFemmeDoc::HasPeriodicBC() { BOOL flag=FALSE; int i,j,k; for(i=0;i=0){ if ((lineproplist[k].BdryFormat==4) || (lineproplist[k].BdryFormat==5)) { flag=TRUE; break; } } } if (flag==TRUE) return TRUE; // If we've gotten this far, we still need to check the // arc segments. for(i=0;i=0){ if ((lineproplist[k].BdryFormat==4) || (lineproplist[k].BdryFormat==5) || (lineproplist[k].BdryFormat==6) || (lineproplist[k].BdryFormat==7)) { flag=TRUE; break; } } } // Finally, we're done. The value of flag now reflects // the judgement on whether or not we have periodic // and/or antiperiodic boundaries. return flag; } // What we do in the normal case is OnWritePoly BOOL CFemmeDoc::OnWritePoly() { // if incremental permeability solution, we crib mesh from the previous problem. // we can just bail out in that case. if ((PrevSoln.GetLength()>0) && (PrevType>0)) return TRUE; FILE *fp; int i,j,k,l,t; double z,R,dL; CComplex a0,a1,a2,c; CString s; CArray< CNode, CNode&> nodelst; CArray< CSegment, CSegment&> linelst; CArray< CArcSegment, CArcSegment&> arclst; CArray< CBlockLabel, CBlockLabel&> blocklst; CNode node; CSegment segm; int bSmartMesh=theApp.session_SmartMesh; if (bSmartMesh<0) bSmartMesh=SmartMesh; nodelst.RemoveAll(); linelst.RemoveAll(); // calculate length used to kludge fine meshing near input node points for (i=0,z=0;i") j++; fprintf(fp,"%i\n",j); for(i=0,k=0;i") { fprintf(fp,"%i %.17g %.17g\n",k,blocklist[i].x,blocklist[i].y); k++; } // figure out a good default mesh size for block labels where // mesh size isn't explicitly specified CComplex xx,yy; double DefaultMeshSize; if (nodelst.GetSize()>1) { xx=nodelst[0].CC(); yy=xx; for(k=0;kRe(yy)) yy.re=nodelst[k].x; if (nodelst[k].y>Im(yy)) yy.im=nodelst[k].y; } DefaultMeshSize=pow(abs(yy-xx)/BoundingBoxFraction,2.); if (!bSmartMesh) DefaultMeshSize=abs(yy-xx); } else DefaultMeshSize=-1; // write out regional attributes fprintf(fp,"%i\n",(int) blocklist.GetSize()-j); for(i=0,k=0;i0) && (blocklist[i].MaxArealine_hook(lua,NULL); Sleep(1); } while(ExitCode==STILL_ACTIVE); hProc=NULL; } } else { MsgBox("Couldn't spawn triangle.exe"); return FALSE; } DWORD ExitCode; GetExitCodeProcess( ProcessInfo.hProcess, // handle to the process &ExitCode // address to receive termination status ); CloseHandle(ProcessInfo.hProcess); CloseHandle(ProcessInfo.hThread); if (ExitCode!=0) { MsgBox("Call to triangle was unsuccessful. Check for small angles."); return FALSE; } return TRUE; } // Call triangle to order segments on the boundary properly BOOL CFemmeDoc::FunnyOnWritePoly() { // if incremental permeability solution, we crib mesh from the previous problem. // we can just bail out in that case. if ((PrevSoln.GetLength()>0) && (Frequency>0)) return TRUE; FILE *fp; int i,j,k,l,t,n,n0,n1,n2; double z,R,dL; CComplex a0,a1,a2,c; CComplex b0,b1,b2; char instring[1024]; CString s; CArray< CNode, CNode&> nodelst; CArray< CSegment, CSegment&> linelst; CArray< CArcSegment, CArcSegment&> arclst; CArray< CBlockLabel, CBlockLabel&> blocklst; CArray< CPeriodicBoundary, CPeriodicBoundary&> pbclst; CArray< CAirGapElement, CAirGapElement&> agelst; CArray< CCommonPoint, CCommonPoint& >ptlst; CNode node; CSegment segm; CPeriodicBoundary pbc; CAirGapElement age; CCommonPoint pt; int bSmartMesh=theApp.session_SmartMesh; if (bSmartMesh<0) bSmartMesh=SmartMesh; nodelst.RemoveAll(); linelst.RemoveAll(); pbclst.RemoveAll(); agelst.RemoveAll(); ptlst.RemoveAll(); UpdateUndo(); // calculate length used to kludge fine meshing near input node points for (i=0,z=0;i") || (blocklist[i].BlockType=="")) j++; fprintf(fp,"%i\n",j); for(i=0,k=0;i") || (blocklist[i].BlockType=="")) { fprintf(fp,"%i %.17g %.17g\n",k,blocklist[i].x,blocklist[i].y); k++; } // figure out a good default mesh size for block labels where // mesh size isn't explicitly specified CComplex xx,yy; double DefaultMeshSize; if (nodelst.GetSize()>1) { xx=nodelst[0].CC(); yy=xx; for(k=0;kRe(yy)) yy.re=nodelst[k].x; if (nodelst[k].y>Im(yy)) yy.im=nodelst[k].y; } DefaultMeshSize=pow(abs(yy-xx)/BoundingBoxFraction,2.); if (!bSmartMesh) DefaultMeshSize=abs(yy-xx); } else DefaultMeshSize=-1; // write out regional attributes fprintf(fp,"%i\n",(int) blocklist.GetSize()-j); for(i=0,k=0;i0) && (blocklist[i].MaxArealine_hook(lua,NULL); Sleep(1); } while(ExitCode==STILL_ACTIVE); hProc=NULL; } } else { MsgBox("Couldn't spawn triangle.exe"); Undo(); UnselectAll(); return FALSE; } DWORD ExitCode; GetExitCodeProcess( ProcessInfo.hProcess, // handle to the process &ExitCode // address to receive termination status ); CloseHandle(ProcessInfo.hProcess); CloseHandle(ProcessInfo.hThread); if (ExitCode!=0) { MsgBox("Call to triangle was unsuccessful. Check for small angles."); Undo(); UnselectAll(); return FALSE; } //#endif // So far, so good. Now, read back in the .edge file // to make sure the points in the segments and arc // segments are ordered in a consistent way so that // the (anti)periodic boundary conditions can be applied. //read meshlines; plyname=pn.Left(pn.ReverseFind('.')) + ".edge"; if((fp=fopen(plyname,"rt"))==NULL){ MsgBox("Call to triangle was unsuccessful"); Undo(); UnselectAll(); return FALSE; } fgets(instring,1024,fp); sscanf(instring,"%i",&k); UnselectAll(); // abuse IsSelected again to keep a // tally of how many subsegments each // entity is sliced into. ptlst.SetSize(linelist.GetSize()+arclist.GetSize()); for(i=0;in1) { n=n0; n0=n1; n1=n; } if (n1>n2) { n=n1; n1=n2; n2=n; } if (n0>n1) { n=n0; n0=n1; n1=n; } // now, check to see if any of the test segments // are sides of this node... for(j=0;j1) { age.BdryName=lineproplist[i].BdryName; age.BdryFormat=lineproplist[i].BdryFormat-6; // 0 for pbc, 1 for apbc age.InnerAngle=lineproplist[i].InnerAngle; age.OuterAngle=lineproplist[i].OuterAngle; agelst.Add(age); } } } // make sure all Air Gap Element arcs have the same discretization // for each arc associated with a particular Air Gap Element... // find out the total arc length and arc elements // corresponding ot each lineproplist entry for(i=0;iagelst[j].ro) agelst[j].ro=R; if (R0) // if the AGE is actually in play { char kludge[32]; double myMaxSideLength,altMaxSideLength; myMaxSideLength=agelst[i].totalArcLength/agelst[i].totalArcElements; agelst[i].totalArcLength/=2; // this is now the angle spanned by the AGE // however, don't want long, skinny air gap elements. Impose some limits // based on the inner and outer radii; altMaxSideLength=(360./PI)*(agelst[i].ro-agelst[i].ri)/(agelst[i].ro+agelst[i].ri); if (altMaxSideLength0) && (pbclst[j].narc>0)) { MsgBox("Can't mix arcs and segments for (anti)periodic BCs"); Undo(); UnselectAll(); return FALSE; } // remove any periodic BC's that aren't actually in play if((pbclst[j].nseg<2) && (pbclst[j].narc<2)) pbclst.RemoveAt(j); else j++; } for(j=0;j0){ // make sure that lines are pretty much the same length if(fabs(LineLength(pbclst[j].seg[0]) -LineLength(pbclst[j].seg[1]))>1.e-06) { MsgBox("(anti)periodic BCs applied to dissimilar segments"); Undo(); UnselectAll(); return FALSE; } // make sure that both lines have the same spacing double len1,len2,len; len1=linelist[pbclst[j].seg[0]].MaxSideLength; len2=linelist[pbclst[j].seg[1]].MaxSideLength; if(len1<=0) len1=len2; if(len2<=0) len2=len1; len=min(len1,len2); linelist[pbclst[j].seg[0]].MaxSideLength=len; linelist[pbclst[j].seg[1]].MaxSideLength=len; } // for arc segments: if(pbclst[j].narc>0){ // make sure that arcs are pretty much the // same arc length if(fabs(arclist[pbclst[j].seg[0]].ArcLength -arclist[pbclst[j].seg[1]].ArcLength)>1.e-06) { MsgBox("(anti)periodic BCs applied to dissimilar arc segments"); Undo(); UnselectAll(); return FALSE; } // make sure that both lines have the same spacing double len1,len2,len; len1=arclist[pbclst[j].seg[0]].MaxSideLength; len2=arclist[pbclst[j].seg[1]].MaxSideLength; len=min(len1,len2); arclist[pbclst[j].seg[0]].MaxSideLength=len; arclist[pbclst[j].seg[1]].MaxSideLength=len; } } // write out new poly and write out adjacent // boundary nodes in a separate .pbc file. // kludge things a bit and use IsSelected to denote // whether or not a line or arc has already been processed. UnselectAll(); nodelst.RemoveAll(); linelst.RemoveAll(); // first, add in existing nodes for(n=0;n myVector; myVector.RemoveAll(); z = (agelst[n].ro + agelst[n].ri)/2.; for(i=0;iz) // on outer radius myVector.Add(arclist[i].n0); else // on inner radius myVector.InsertAt(0,arclist[i].n0); if(k==1){ segm.n0=arclist[i].n0; segm.n1=arclist[i].n1; linelst.Add(segm); } else for(j=0;jz) // on outer radius myVector.Add(l); else // on inner radius myVector.InsertAt(0,l); } else if(j==(k-1)) { l=(int) nodelst.GetSize()-1; segm.n0=l; segm.n1=arclist[i].n1; linelst.Add(segm); } else{ l=(int) nodelst.GetSize(); nodelst.Add(node); segm.n0=l-1; segm.n1=l; linelst.Add(segm); // insert newly created node if (R>z) // on outer radius myVector.Add(l); else // on inner radius myVector.InsertAt(0,l); } } } agelst[n].node=(int *)calloc(myVector.GetSize()+1,sizeof(int)); agelst[n].node[0]=(int) myVector.GetSize(); for(k=0;k") j++; fprintf(fp,"%i\n",j); for(i=0,k=0;i") { fprintf(fp,"%i %.17g %.17g\n",k,blocklist[i].x,blocklist[i].y); k++; } // write out regional attributes fprintf(fp,"%i\n",(int) blocklist.GetSize()-j); for(i=0,k=0;i0) && (blocklist[i].MaxArea InnerRing; CArray OuterRing; InnerRing.RemoveAll(); OuterRing.RemoveAll(); n=agelst[k].node[0]/2; dtta = agelst[k].totalArcLength/n; n0=(int) round(360./dtta); // total elements in a 360deg annular ring; n1=(int) round(360./agelst[k].totalArcLength); // number of copied segments // Should do some consistency checking here; // totalArcLength*n1 should equal 360 // no*dtta should equal 360 // if antiperiodic, n1 should be an even number // otherwise, throw error message, clean up, and return InnerRing.SetSize(n0); OuterRing.SetSize(n0); // map each bdry point onto points on the ring; int kk; for(j=0,kk=0;j InnerRing[jj+1].w0) { qq=InnerRing[jj]; InnerRing[jj]=InnerRing[jj+1]; InnerRing[jj+1]=qq; bDone=0; } } if (bDone) break; } for(int ii=0;ii OuterRing[jj+1].w0) { qq=OuterRing[jj]; OuterRing[jj]=OuterRing[jj+1]; OuterRing[jj+1]=qq; bDone=0; } } if (bDone) break; } // print out AGE definition fprintf(fp,"\"%s\"\n",agelst[k].BdryName); fprintf(fp,"%i %.17g %.17g %.17g %.17g %.17g %.17g %.17g %i %.17g %.17g\n", agelst[k].BdryFormat,agelst[k].InnerAngle,agelst[k].OuterAngle, agelst[k].ri,agelst[k].ro,agelst[k].totalArcLength, Re(agelst[k].agc),Im(agelst[k].agc),n, InnerRing[0].w0,OuterRing[0].w0); for(i=0;i<=n;i++) { int p0,p1; p1=i; if(p1==n0) p1=0; p0=p1-1; if(p0<0) p0=n0+p0; // ring points that bracket points in the annulus mesh // and their sign, for the purposes of periodicity/antiperiodicity fprintf(fp,"%i %g %i %g %i %g %i %g\n", InnerRing[p0].n0, InnerRing[p0].w1, InnerRing[p1].n0, InnerRing[p1].w1, OuterRing[p0].n0, OuterRing[p0].w1, OuterRing[p1].n0, OuterRing[p1].w1); } /* fprintf(fp,"%s\n",agelst[k].BdryName); fprintf(fp,"%i %.17g %.17g %.17g %.17g %.17g %.17g %.17g %i\n", agelst[k].BdryFormat,agelst[k].InnerAngle,agelst[k].OuterAngle, agelst[k].ri,agelst[k].ro,agelst[k].totalArcLength, Re(agelst[k].agc),Im(agelst[k].agc),n); for(i=1;i<=n;i++) fprintf(fp,"%i %i\n",agelst[k].node[i],agelst[k].node[n+i]); */ } fclose(fp); // call triangle with -Y flag. rootname="\"" + pn.Left(pn.ReverseFind('.')) + "\""; sprintf(CommandLine,"\"%striangle.exe\" -p -P -q%f -e -A -a -z -Q -I -Y %s", (const char *) BinDir,__min(MinAngle+MINANGLE_BUMP,MINANGLE_MAX), (const char *) rootname); StartupInfo.cb = sizeof(STARTUPINFO); StartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEOFFFEEDBACK; StartupInfo.wShowWindow = SW_SHOWMINNOACTIVE; if (CreateProcess(NULL,CommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &StartupInfo, &ProcessInfo)){ if(bLinehook==FALSE) WaitForSingleObject(ProcessInfo.hProcess, INFINITE); else{ DWORD ExitCode; hProc=ProcessInfo.hProcess; do{ GetExitCodeProcess(ProcessInfo.hProcess,&ExitCode); ((CFemmApp *)AfxGetApp())->line_hook(lua,NULL); Sleep(1); } while(ExitCode==STILL_ACTIVE); hProc=NULL; } } else { MsgBox("Couldn't spawn triangle.exe"); Undo(); UnselectAll(); return FALSE; } GetExitCodeProcess( ProcessInfo.hProcess, // handle to the process &ExitCode // address to receive termination status ); CloseHandle(ProcessInfo.hProcess); CloseHandle(ProcessInfo.hThread); if (ExitCode!=0) { MsgBox("Call to triangle was unsuccessful"); Undo(); UnselectAll(); return FALSE; } UnselectAll(); // Now restore boundary segment discretizations that have // been mucked up in the process... for(i=0;i