Galerkin FEM for elliptic PDEs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FEM4EllipticPDE.cpp 38KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955
  1. // ===========================================================================
  2. //
  3. // Filename: FEM4EllipticPDE.cpp
  4. //
  5. // Created: 08/16/12 18:19:57
  6. // Compiler: Tested with g++, icpc, and MSVC 2010
  7. //
  8. // Author: Trevor Irons (ti)
  9. //
  10. // Organisation: Colorado School of Mines (CSM)
  11. // United States Geological Survey (USGS)
  12. //
  13. // Email: tirons@mines.edu, tirons@usgs.gov
  14. //
  15. // This program is free software: you can redistribute it and/or modify
  16. // it under the terms of the GNU General Public License as published by
  17. // the Free Software Foundation, either version 3 of the License, or
  18. // (at your option) any later version.
  19. //
  20. // This program is distributed in the hope that it will be useful,
  21. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. // GNU General Public License for more details.
  24. //
  25. // You should have received a copy of the GNU General Public License
  26. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  27. //
  28. // ===========================================================================
  29. /**
  30. @file
  31. @author Trevor Irons
  32. @date 08/16/12
  33. @version 0.0
  34. **/
  35. #include "FEM4EllipticPDE.h"
  36. namespace Lemma {
  37. std::ostream &operator<<(std::ostream &stream,
  38. const FEM4EllipticPDE &ob) {
  39. stream << *(LemmaObject*)(&ob);
  40. return stream;
  41. }
  42. // ==================== LIFECYCLE =======================
  43. FEM4EllipticPDE::FEM4EllipticPDE(const std::string&name) :
  44. LemmaObject(name), BndryH(1), BndrySigma(1),
  45. vtkSigma(NULL), vtkG(NULL), vtkGrid(NULL), gFcn3(NULL) {
  46. }
  47. FEM4EllipticPDE::~FEM4EllipticPDE() {
  48. }
  49. void FEM4EllipticPDE::Release() {
  50. delete this;
  51. }
  52. FEM4EllipticPDE* FEM4EllipticPDE::New( ) {
  53. FEM4EllipticPDE* Obj = new FEM4EllipticPDE("FEM4EllipticPDE");
  54. Obj->AttachTo(Obj);
  55. return Obj;
  56. }
  57. void FEM4EllipticPDE::Delete() {
  58. this->DetachFrom(this);
  59. }
  60. // ==================== OPERATIONS =======================
  61. void FEM4EllipticPDE::SetSigmaFunction(vtkImplicitFunction* sigma) {
  62. vtkSigma = sigma;
  63. }
  64. void FEM4EllipticPDE::SetBoundaryStep(const Real& h) {
  65. BndryH = h;
  66. }
  67. void FEM4EllipticPDE::SetGFunction(vtkImplicitFunction* g) {
  68. vtkG = g;
  69. }
  70. void FEM4EllipticPDE::SetGFunction( Real (*gFcn)(const Real&, const Real&, const Real&) ) {
  71. // vtkG = g;
  72. gFcn3 = gFcn;
  73. }
  74. void FEM4EllipticPDE::SetGrid(vtkDataSet* grid) {
  75. vtkGrid = grid;
  76. }
  77. vtkSmartPointer<vtkIdList> FEM4EllipticPDE::GetConnectedPoints(const int& id0) {
  78. vtkSmartPointer<vtkIdList> pointIds = vtkSmartPointer<vtkIdList>::New();
  79. vtkSmartPointer<vtkIdList> cellList = vtkSmartPointer<vtkIdList>::New();
  80. vtkGrid->GetPointCells(id0, cellList);
  81. for(int i=0;i<cellList->GetNumberOfIds(); ++i){
  82. vtkCell* cell = vtkGrid->GetCell(cellList->GetId(i));
  83. if(cell->GetNumberOfEdges() > 0){
  84. for(int j=0; j<cell->GetNumberOfEdges(); ++j){
  85. vtkCell* edge = cell->GetEdge(j);
  86. vtkIdList* edgePoints=edge->GetPointIds();
  87. if(edgePoints->GetId(0)==id0){
  88. pointIds->InsertUniqueId(edgePoints->GetId(1));
  89. } else if(edgePoints->GetId(1)==id0){
  90. pointIds->InsertUniqueId(edgePoints->GetId(0));
  91. }
  92. }
  93. }
  94. }
  95. return pointIds;
  96. }
  97. Real FEM4EllipticPDE::dist(Real r0[3], Real r1[3]) {
  98. Real rm0 = r1[0] - r0[0];
  99. Real rm1 = r1[1] - r0[1];
  100. Real rm2 = r1[2] - r0[2];
  101. return std::sqrt( rm0*rm0 + rm1*rm1 + rm2*rm2 );
  102. }
  103. Real FEM4EllipticPDE::dist(const Vector3r& r0, const Vector3r& r1) {
  104. Real rm0 = r1[0] - r0[0];
  105. Real rm1 = r1[1] - r0[1];
  106. Real rm2 = r1[2] - r0[2];
  107. return std::sqrt( rm0*rm0 + rm1*rm1 + rm2*rm2 );
  108. }
  109. //--------------------------------------------------------------------------------------
  110. // Class: FEM4EllipticPDE
  111. // Method: SetupDC
  112. //--------------------------------------------------------------------------------------
  113. void FEM4EllipticPDE::SetupDC ( DCSurvey* Survey, const int& ij ) {
  114. ////////////////////////////////////////////////////////////
  115. // Load vector g, solution vector u
  116. std::cout << "\nBuilding load vector (g)" << std::endl;
  117. g = VectorXr::Zero(vtkGrid->GetNumberOfPoints());
  118. std::cout << "made g with " << vtkGrid->GetNumberOfPoints() << " pnts" << std::endl;
  119. int iia(0);
  120. Real jja(0);
  121. Survey->GetA( ij, iia, jja );
  122. //g(ii) = jj;
  123. int iib(0);
  124. Real jjb(0);
  125. Survey->GetB( ij, iib, jjb );
  126. //g(ii) = jj;
  127. /* 3D Phi */
  128. for (int ic=0; ic < vtkGrid->GetNumberOfCells(); ++ic) {
  129. // Eigen::Matrix<Real, 4, 4> C = Eigen::Matrix<Real, 4, 4>::Zero() ;
  130. // for (int ii=0; ii<4; ++ii) {
  131. // double* pts = vtkGrid->GetCell(ic)->GetPoints()->GetPoint(ii); //[ipc] ;
  132. // C(ii, 0) = 1;
  133. // C(ii, 1) = pts[0];
  134. // C(ii, 2) = pts[1];
  135. // C(ii, 3) = pts[2];
  136. // }
  137. // Real V = (1./6.) * C.determinant(); // volume of tetrahedra
  138. //
  139. vtkIdList* Ids = vtkGrid->GetCell(ic)->GetPointIds();
  140. int ID[4];
  141. ID[0] = Ids->GetId(0);
  142. ID[1] = Ids->GetId(1);
  143. ID[2] = Ids->GetId(2);
  144. ID[3] = Ids->GetId(3);
  145. //Real V = C.determinant(); // volume of tetrahedra
  146. Real sum(0);
  147. if (ID[0] == iia || ID[1] == iia || ID[2] == iia || ID[3] == iia ) {
  148. std::cout << "Caught A electrode, injecting " << iia << std::endl;
  149. //sum = 10;
  150. //g(ID[iia]) += jja;
  151. g(iia) += jja;
  152. }
  153. if (ID[0] == iib || ID[1] == iib || ID[2] == iib || ID[3] == iib) {
  154. //sum = -10;
  155. std::cout << "Caught B electrode, injecting " << iib << std::endl;
  156. //g(ID[iib]) += jjb;
  157. g(iib) += jjb;
  158. }
  159. //g(ID[0]) = sum; //(V/4.) * sum; // Why 3, Why!!!?
  160. std::cout << "\r" << (int)(1e2*((float)(ic) / (float)(vtkGrid->GetNumberOfCells()))) << std::flush ;
  161. }
  162. return ;
  163. } // ----- end of method FEM4EllipticPDE::SetupDC -----
  164. void FEM4EllipticPDE::Solve( const std::string& resfile) {
  165. ConstructAMatrix();
  166. //ConstructLoadVector();
  167. std::cout << "\nSolving" << std::endl;
  168. ////////////////////////////////////////////////////////////
  169. // Solving:
  170. //Eigen::SimplicialCholesky<Eigen::SparseMatrix<Real>, Eigen::Lower > chol(A); // performs a Cholesky factorization of A
  171. //VectorXr u = chol.solve(g);
  172. //Eigen::ConjugateGradient<Eigen::SparseMatrix<Real, Eigen::Lower > Eigen::DiagonalPreconditioner > cg;
  173. Eigen::ConjugateGradient< Eigen::SparseMatrix<Real> > cg(A);
  174. //Eigen::BiCGSTAB<Eigen::SparseMatrix<Real> > cg(A);
  175. cg.setMaxIterations(3000);
  176. std::cout <<" rows\tcols\n";
  177. std::cout << "A: " << A.rows() << "\t" << A.cols() << std::endl;
  178. std::cout << "g: " << g.rows() << "\t" << g.cols() << std::endl;
  179. VectorXr u = cg.solve(g);
  180. std::cout << "#iterations: " << cg.iterations() << std::endl;
  181. std::cout << "estimated error: " << cg.error() << std::endl;
  182. vtkDoubleArray *gArray = vtkDoubleArray::New();
  183. vtkDoubleArray *uArray = vtkDoubleArray::New();
  184. uArray->SetNumberOfComponents(1);
  185. gArray->SetNumberOfComponents(1);
  186. for (int iu = 0; iu<u.size(); ++iu) {
  187. uArray->InsertTuple1(iu, u[iu]);
  188. gArray->InsertTuple1(iu, g[iu]);
  189. }
  190. uArray->SetName("u");
  191. gArray->SetName("g");
  192. vtkGrid->GetPointData()->AddArray(uArray);
  193. vtkGrid->GetPointData()->AddArray(gArray);
  194. vtkXMLDataSetWriter *Writer = vtkXMLDataSetWriter::New();
  195. Writer->SetInputData(vtkGrid);
  196. Writer->SetFileName(resfile.c_str());
  197. Writer->Write();
  198. Writer->Delete();
  199. gArray->Delete();
  200. uArray->Delete();
  201. }
  202. //--------------------------------------------------------------------------------------
  203. // Class: FEM4EllipticPDE
  204. // Method: ConstructAMatrix
  205. //--------------------------------------------------------------------------------------
  206. void FEM4EllipticPDE::ConstructAMatrix ( ) {
  207. /////////////////////////////////////////////////////////////////////////
  208. // Build stiffness matrix (A)
  209. std::cout << "Building Stiffness Matrix (A) " << std::endl;
  210. std::cout << "\tMesh has " << vtkGrid->GetNumberOfCells() << " cells " << std::endl;
  211. std::cout << "\tMesh has " << vtkGrid->GetNumberOfPoints() << " points " << std::endl;
  212. //Eigen::SparseMatrix<Real>
  213. A.resize(vtkGrid->GetNumberOfPoints(), vtkGrid->GetNumberOfPoints());
  214. std::vector< Eigen::Triplet<Real> > coeffs;
  215. if ( !vtkGrid->GetPointData()->GetScalars("vtkValidPointMask") ) {
  216. throw std::runtime_error("No vtkValidPointMask");
  217. }
  218. if ( !vtkGrid->GetCellData()->GetScalars("G") && !vtkGrid->GetPointData()->GetScalars("G") ) {
  219. throw std::runtime_error("No Cell or Point Data G");
  220. }
  221. bool GCell = false;
  222. if ( vtkGrid->GetCellData()->GetScalars("G") ) {
  223. GCell = true;
  224. }
  225. // Here we iterate over all of the cells
  226. for (int ic=0; ic < vtkGrid->GetNumberOfCells(); ++ic) {
  227. assert ( vtkGrid->GetCell(ic)->GetNumberOfPoints() == 4 );
  228. // TODO, in production code we might not want to do this check here
  229. if ( vtkGrid->GetCell(ic)->GetNumberOfPoints() != 4 ) {
  230. throw std::runtime_error("Non-tetrahedral mesh encountered!");
  231. }
  232. // construct coordinate matrix C
  233. Eigen::Matrix<Real, 4, 4> C = Eigen::Matrix<Real, 4, 4>::Zero() ;
  234. for (int ii=0; ii<4; ++ii) {
  235. double* pts = vtkGrid->GetCell(ic)->GetPoints()->GetPoint(ii); //[ipc] ;
  236. C(ii, 0) = 1;
  237. C(ii, 1) = pts[0] ;
  238. C(ii, 2) = pts[1] ;
  239. C(ii, 3) = pts[2] ;
  240. }
  241. Eigen::Matrix<Real, 4, 4> GradPhi = C.inverse(); // nabla \phi
  242. Real V = (1./6.) * C.determinant(); // volume of tetrahedra
  243. vtkIdList* Ids = vtkGrid->GetCell(ic)->GetPointIds();
  244. int ID[4];
  245. ID[0] = Ids->GetId(0);
  246. ID[1] = Ids->GetId(1);
  247. ID[2] = Ids->GetId(2);
  248. ID[3] = Ids->GetId(3);
  249. Real sigma_bar(0);
  250. if (GCell) {
  251. sigma_bar = vtkGrid->GetCellData()->GetScalars("G")->GetTuple1(ic);
  252. } else {
  253. sigma_bar = vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[0]);
  254. sigma_bar += vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[1]);
  255. sigma_bar += vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[2]);
  256. sigma_bar += vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[3]);
  257. sigma_bar /= 4.;
  258. }
  259. sigma_bar = 1.;
  260. for (int ii=0; ii<4; ++ii) {
  261. for (int jj=0; jj<4; ++jj) {
  262. if (jj == ii) {
  263. // Apply Homogeneous Dirichlet Boundary conditions
  264. Real bb = vtkGrid->GetPointData()->GetScalars("vtkValidPointMask")->GetTuple(ID[ii])[0];
  265. Real bdry = (1./(BndryH*BndryH))*BndrySigma*bb; // + sum;
  266. //Real bdry = GradPhi.col(ii).tail<3>().dot(GradPhi.col(ii).tail<3>())*BndrySigma*bb; // + sum;
  267. coeffs.push_back( Eigen::Triplet<Real> ( ID[ii], ID[jj], bdry + GradPhi.col(ii).tail<3>().dot(GradPhi.col(jj).tail<3>() ) * V * sigma_bar ) );
  268. } else {
  269. coeffs.push_back( Eigen::Triplet<Real> ( ID[ii], ID[jj], GradPhi.col(ii).tail<3>().dot(GradPhi.col(jj).tail<3>() ) * V * sigma_bar ) );
  270. }
  271. // Stiffness matrix no longer contains boundary conditions...
  272. //coeffs.push_back( Eigen::Triplet<Real> ( ID[ii], ID[jj], GradPhi.col(ii).tail<3>().dot(GradPhi.col(jj).tail<3>() ) * V * sigma_bar ) );
  273. }
  274. }
  275. std::cout << "\r" << (int)(1e2*((float)(ic) / (float)(vtkGrid->GetNumberOfCells()))) << std::flush ;
  276. }
  277. A.setFromTriplets(coeffs.begin(), coeffs.end());
  278. //std::cout << "A\n" << A << std::endl;
  279. }
  280. void FEM4EllipticPDE::SetupPotential() {
  281. ////////////////////////////////////////////////////////////
  282. // Load vector g
  283. std::cout << "\nBuilding load vector (g)" << std::endl;
  284. g = VectorXr::Zero(vtkGrid->GetNumberOfPoints());
  285. std::cout << "made g with " << vtkGrid->GetNumberOfPoints() << " points" << std::endl;
  286. for (int ic=0; ic < vtkGrid->GetNumberOfCells(); ++ic) {
  287. Eigen::Matrix<Real, 4, 4> C = Eigen::Matrix<Real, 4, 4>::Zero() ;
  288. for (int ii=0; ii<4; ++ii) {
  289. double* pts = vtkGrid->GetCell(ic)->GetPoints()->GetPoint(ii); //[ipc] ;
  290. C(ii, 0) = 1;
  291. C(ii, 1) = pts[0];
  292. C(ii, 2) = pts[1];
  293. C(ii, 3) = pts[2];
  294. }
  295. Real V = (1./6.) * C.determinant(); // volume of tetrahedra
  296. vtkIdList* Ids = vtkGrid->GetCell(ic)->GetPointIds();
  297. int ID[4];
  298. ID[0] = Ids->GetId(0);
  299. ID[1] = Ids->GetId(1);
  300. ID[2] = Ids->GetId(2);
  301. ID[3] = Ids->GetId(3);
  302. Real avg(0);
  303. Real GG[4];
  304. for (int ii=0; ii<4; ++ii) {
  305. GG[ii] = vtkGrid->GetPointData()->GetScalars("G")->GetTuple(ID[ii])[0];
  306. //avg /= 4.;
  307. }
  308. if ( std::abs( (GG[0]+GG[1]+GG[2]+GG[3])/4. - GG[0]) < 1e-5) {
  309. avg = GG[0];
  310. }
  311. for (int ii=0; ii<4; ++ii) {
  312. // avg += vtkGrid->GetPointData()->GetScalars()->GetTuple(ID[ii])[0];
  313. // //if ( std::abs(vtkGrid->GetPointData()->GetScalars()->GetTuple(ID[ii])[0]) > 1e-3 )
  314. //}
  315. //TODO this seems wrong!
  316. //avg /= 4.;
  317. //g(ID[ii]) += (V) * ( vtkGrid->GetPointData()->GetScalars("G")->GetTuple(ID[ii])[0] ) ;
  318. g(ID[ii]) += PI* (V) * avg;
  319. //g(ID[ii]) += 6.67 *(V/4.) * avg;
  320. }
  321. //g(ID[0]) += (V/4.) * avg;
  322. }
  323. }
  324. void FEM4EllipticPDE::SolveOLD(const std::string& fname) {
  325. Real r0[3];
  326. Real r1[3];
  327. /////////////////////////////////////////////////////////////////////////
  328. // Surface filter, to determine if points are on boundary, and need
  329. // boundary conditions applied
  330. vtkDataSetSurfaceFilter* Surface = vtkDataSetSurfaceFilter::New();
  331. Surface->SetInputData(vtkGrid);
  332. Surface->PassThroughPointIdsOn( );
  333. Surface->Update();
  334. vtkIdTypeArray* BdryIds = static_cast<vtkIdTypeArray*>
  335. (Surface->GetOutput()->GetPointData()->GetScalars("vtkOriginalPointIds"));
  336. // Expensive search for whether or not point is on boundary. O(n) cost.
  337. VectorXi bndryCnt = VectorXi::Zero(vtkGrid->GetNumberOfPoints());
  338. for (int isp=0; isp < Surface->GetOutput()->GetNumberOfPoints(); ++isp) {
  339. //double* pts = vtkGrid->GetCell(ic)->GetPoints()->GetPoint(ii); //[ipc] ;
  340. // x \in -14.5 to 14.5
  341. // y \in 0 to 30
  342. bndryCnt(BdryIds->GetTuple1(isp)) += 1;
  343. }
  344. /////////////////////////////////////////////////////////////////////////
  345. // Build stiffness matrix (A)
  346. std::cout << "Building Stiffness Matrix (A) " << std::endl;
  347. std::cout << "\tMesh has " << vtkGrid->GetNumberOfCells() << " cells " << std::endl;
  348. std::cout << "\tMesh has " << vtkGrid->GetNumberOfPoints() << " points " << std::endl;
  349. Eigen::SparseMatrix<Real> A(vtkGrid->GetNumberOfPoints(), vtkGrid->GetNumberOfPoints());
  350. std::vector< Eigen::Triplet<Real> > coeffs;
  351. // Here we iterate over all of the cells
  352. for (int ic=0; ic < vtkGrid->GetNumberOfCells(); ++ic) {
  353. assert ( vtkGrid->GetCell(ic)->GetNumberOfPoints() == 4 );
  354. // TODO, in production code we might not want to do this check here
  355. if ( vtkGrid->GetCell(ic)->GetNumberOfPoints() != 4 ) {
  356. std::cout << "DOOM FEM4EllipticPDE encountered non-tetrahedral mesh\n";
  357. std::cout << "Number of points in cell " << vtkGrid->GetCell(ic)->GetNumberOfPoints() << std::endl ;
  358. exit(1);
  359. }
  360. // construct coordinate matrix C
  361. Eigen::Matrix<Real, 4, 4> C = Eigen::Matrix<Real, 4, 4>::Zero() ;
  362. for (int ii=0; ii<4; ++ii) {
  363. double* pts = vtkGrid->GetCell(ic)->GetPoints()->GetPoint(ii); //[ipc] ;
  364. C(ii, 0) = 1;
  365. C(ii, 1) = pts[0] ;
  366. C(ii, 2) = pts[1] ;
  367. C(ii, 3) = pts[2] ;
  368. }
  369. Eigen::Matrix<Real, 4, 4> GradPhi = C.inverse(); // nabla \phi
  370. Real V = (1./6.) * C.determinant(); // volume of tetrahedra
  371. vtkIdList* Ids = vtkGrid->GetCell(ic)->GetPointIds();
  372. int ID[4];
  373. ID[0] = Ids->GetId(0);
  374. ID[1] = Ids->GetId(1);
  375. ID[2] = Ids->GetId(2);
  376. ID[3] = Ids->GetId(3);
  377. Real sum(0);
  378. Real sigma_bar = vtkGrid->GetCellData()->GetScalars()->GetTuple1(ic);
  379. for (int ii=0; ii<4; ++ii) {
  380. for (int jj=0; jj<4; ++jj) {
  381. if (jj == ii) {
  382. // I apply boundary to Stiffness matrix, it's common to take the other approach and apply to the load vector and then
  383. // solve for the boundaries? Is one better? This seems to work, which is nice.
  384. //Real bdry = V*(1./(BndryH*BndryH))*BndrySigma*bndryCnt( ID[ii] ); // + sum;
  385. Real bb = vtkGrid->GetPointData()->GetScalars("vtkValidPointMask")->GetTuple(ID[ii])[0];
  386. //std::cout << "bb " << bb << std::endl;
  387. Real bdry = V*(1./(BndryH*BndryH))*BndrySigma*bb; // + sum;
  388. coeffs.push_back( Eigen::Triplet<Real> ( ID[ii], ID[jj], bdry + GradPhi.col(ii).tail<3>().dot(GradPhi.col(jj).tail<3>() ) * V * sigma_bar ) );
  389. } else {
  390. coeffs.push_back( Eigen::Triplet<Real> ( ID[ii], ID[jj], GradPhi.col(ii).tail<3>().dot(GradPhi.col(jj).tail<3>() ) * V * sigma_bar ) );
  391. }
  392. // Stiffness matrix no longer contains boundary conditions...
  393. //coeffs.push_back( Eigen::Triplet<Real> ( ID[ii], ID[jj], GradPhi.col(ii).tail<3>().dot(GradPhi.col(jj).tail<3>() ) * V * sigma_bar ) );
  394. }
  395. }
  396. std::cout << "\r" << (int)(1e2*((float)(ic) / (float)(vtkGrid->GetNumberOfCells()))) << std::flush ;
  397. }
  398. A.setFromTriplets(coeffs.begin(), coeffs.end());
  399. //A.makeCompressed();
  400. ////////////////////////////////////////////////////////////
  401. // Load vector g, solution vector u
  402. std::cout << "\nBuilding load vector (g)" << std::endl;
  403. VectorXr g = VectorXr::Zero(vtkGrid->GetNumberOfPoints());
  404. std::cout << "made g with " << vtkGrid->GetNumberOfPoints() << " pnts" << std::endl;
  405. // If the G function has been evaluated at each *node*
  406. // --> but still needs to be integrated at the surfaces
  407. // Aha, requires that there is in fact a pointdata memeber // BUG TODO BUG!!!
  408. std::cout << "Point Data ptr " << vtkGrid->GetPointData() << std::endl;
  409. //if ( vtkGrid->GetPointData() != NULL && std::string( vtkGrid->GetPointData()->GetScalars()->GetName() ).compare( std::string("G") ) == 0 ) {
  410. bool pe(false);
  411. bool ne(false);
  412. if ( true ) {
  413. std::cout << "\nUsing G from file" << std::endl;
  414. /* 3D Phi */
  415. for (int ic=0; ic < vtkGrid->GetNumberOfCells(); ++ic) {
  416. Eigen::Matrix<Real, 4, 4> C = Eigen::Matrix<Real, 4, 4>::Zero() ;
  417. for (int ii=0; ii<4; ++ii) {
  418. double* pts = vtkGrid->GetCell(ic)->GetPoints()->GetPoint(ii); //[ipc] ;
  419. C(ii, 0) = 1;
  420. C(ii, 1) = pts[0];
  421. C(ii, 2) = pts[1];
  422. C(ii, 3) = pts[2];
  423. }
  424. Real V = (1./6.) * C.determinant(); // volume of tetrahedra
  425. //Real V = C.determinant(); // volume of tetrahedra
  426. vtkIdList* Ids = vtkGrid->GetCell(ic)->GetPointIds();
  427. int ID[4];
  428. ID[0] = Ids->GetId(0);
  429. ID[1] = Ids->GetId(1);
  430. ID[2] = Ids->GetId(2);
  431. ID[3] = Ids->GetId(3);
  432. /* bad news bears for magnet */
  433. double* pt = vtkGrid->GetCell(ic)->GetPoints()->GetPoint(0);
  434. Real sum(0);
  435. /*
  436. if (!pe) {
  437. if (std::abs(pt[0]) < .2 && std::abs(pt[1]-15) < .2 && pt[2] < 3.25 ) {
  438. sum = 1; //vtkGrid->GetPointData()->GetScalars()->GetTuple(ID[ii])[0];
  439. pe = true;
  440. }
  441. }*/
  442. if (ID[0] == 26) {
  443. sum = 10;
  444. }
  445. if (ID[0] == 30) {
  446. sum = -10;
  447. }
  448. /*
  449. if (!ne) {
  450. if (std::abs(pt[0]+1.) < .2 && std::abs(pt[1]-15) < .2 && pt[2] < 3.25 ) {
  451. sum = -1; //vtkGrid->GetPointData()->GetScalars()->GetTuple(ID[ii])[0];
  452. std::cout << "Negative Electroce\n";
  453. ne = true;
  454. }
  455. }
  456. */
  457. //for (int ii=0; ii<4; ++ii) {
  458. //g(ID[ii]) += (V/4.) * ( vtkGrid->GetPointData()->GetScalars()->GetTuple(ID[ii])[0] ) ;
  459. //if ( std::abs(vtkGrid->GetPointData()->GetScalars()->GetTuple(ID[ii])[0]) > 1e-3 )
  460. //}
  461. // TODO check Load Vector...
  462. g(ID[0]) = sum; //(V/4.) * sum; // Why 3, Why!!!?
  463. std::cout << "\r" << (int)(1e2*((float)(ic) / (float)(vtkGrid->GetNumberOfCells()))) << std::flush ;
  464. }
  465. /*
  466. for (int ic=0; ic < vtkGrid->GetNumberOfPoints(); ++ic) {
  467. vtkGrid->GetPoint(ic, r0);
  468. vtkSmartPointer<vtkIdList> connectedVertices = GetConnectedPoints(ic);
  469. double g0 = vtkGrid->GetPointData()->GetScalars()->GetTuple(ic)[0] ;
  470. //std::cout << "num conn " << connectedVertices->GetNumberOfIds() << std::endl;
  471. if ( std::abs(g0) > 1e-3 ) {
  472. for(vtkIdType i = 0; i < connectedVertices->GetNumberOfIds(); ++i) {
  473. int ii = connectedVertices->GetId(i);
  474. vtkGrid->GetPoint(ii, r1);
  475. double g1 = vtkGrid->GetPointData()->GetScalars()->GetTuple(ii)[0] ;
  476. //g(ic) += g0*dist(r0,r1); //CompositeSimpsons2(g0, r0, r1, 8);
  477. if ( std::abs(g1) > 1e-3 ) {
  478. g(ic) += CompositeSimpsons2(g1, g0, r1, r0, 1000);
  479. }
  480. //g(ic) += CompositeSimpsons2(g0, r1, r0, 8);
  481. //if ( std::abs(g1) > 1e-3 ) {
  482. //g(ic) += CompositeSimpsons2(g0, g1, r0, r1, 8);
  483. //g(ic) += CompositeSimpsons2(g0, g1, r0, r1, 100); // / (2*dist(r0,r1)) ;
  484. // g(ic) += g0*dist(r0,r1); //CompositeSimpsons2(g0, r0, r1, 8);
  485. //g(ic) += CompositeSimpsons2(g0, r0, r1, 8);
  486. // g(ic) += g0; //CompositeSimpsons2(g0, r0, r1, 8);
  487. //} //else {
  488. // g(ic) += g0; //CompositeSimpsons2(g0, r0, r1, 8);
  489. //}
  490. }
  491. }
  492. //g(ic) = 2.* vtkGrid->GetPointData()->GetScalars()->GetTuple(ic)[0] ; // Why 2?
  493. //std::cout << "\r" << (int)(1e2*((float)(ic) / (float)(vtkGrid->GetNumberOfPoints()))) << std::flush ;
  494. }
  495. */
  496. } else if (vtkG) { // VTK implicit function, proceed with care
  497. std::cout << "\nUsing implicit file from file" << std::endl;
  498. // OpenMP right here
  499. for (int ic=0; ic < vtkGrid->GetNumberOfPoints(); ++ic) {
  500. vtkSmartPointer<vtkIdList> connectedVertices = GetConnectedPoints(ic);
  501. //vtkGrid->GetPoint(ic, r0);
  502. //g(ic) += vtkG->FunctionValue(r0[0], r0[1], r0[2]) ;
  503. // std::cout << vtkG->FunctionValue(r0[0], r0[1], r0[2]) << std::endl;
  504. //g(ic) += vtkGrid->GetPointData()->GetScalars()->GetTuple1(ic);// FunctionValue(r0[0], r0[1], r0[2]) ;
  505. /*
  506. for(vtkIdType i = 0; i < connectedVertices->GetNumberOfIds(); ++i) {
  507. int ii = connectedVertices->GetId(i);
  508. vtkGrid->GetPoint(ii, r1);
  509. g(ic) += CompositeSimpsons2(vtkG, r0, r1, 8); // vtkG->FunctionValue(r0[0], r0[1], r0[2]) ;
  510. }
  511. */
  512. std::cout << "\r" << (int)(1e2*((float)(ic) / (float)(vtkGrid->GetNumberOfPoints()))) << std::flush ;
  513. }
  514. } else if (gFcn3) {
  515. std::cout << "\nUsing gFcn3" << std::endl;
  516. for (int ic=0; ic < vtkGrid->GetNumberOfPoints(); ++ic) {
  517. vtkSmartPointer<vtkIdList> connectedVertices = GetConnectedPoints(ic);
  518. vtkGrid->GetPoint(ic, r0);
  519. // TODO, test OMP sum reduction here. Is vtkGrid->GetPoint thread safe?
  520. //Real sum(0.);
  521. //#ifdef LEMMAUSEOMP
  522. //#pragma omp parallel for reduction(+:sum)
  523. //#endif
  524. for(vtkIdType i = 0; i < connectedVertices->GetNumberOfIds(); ++i) {
  525. int ii = connectedVertices->GetId(i);
  526. vtkGrid->GetPoint(ii, r1);
  527. g(ic) += CompositeSimpsons2(gFcn3, r0, r1, 8); // vtkG->FunctionValue(r0[0], r0[1], r0[2]) ;
  528. //sum += CompositeSimpsons2(gFcn3, r0, r1, 8); // vtkG->FunctionValue(r0[0], r0[1], r0[2]) ;
  529. }
  530. std::cout << "\r" << (int)(1e2*((float)(ic) / (float)(vtkGrid->GetNumberOfPoints()))) << std::flush ;
  531. //g(ic) = sum;
  532. }
  533. } else {
  534. std::cout << "No source specified\n";
  535. exit(EXIT_FAILURE);
  536. }
  537. // std::cout << g << std::endl;
  538. //g(85) = 1;
  539. std::cout << "\nSolving" << std::endl;
  540. ////////////////////////////////////////////////////////////
  541. // Solving:
  542. //Eigen::SimplicialCholesky<Eigen::SparseMatrix<Real>, Eigen::Lower > chol(A); // performs a Cholesky factorization of A
  543. //VectorXr u = chol.solve(g);
  544. //Eigen::ConjugateGradient<Eigen::SparseMatrix<Real, Eigen::Lower > Eigen::DiagonalPreconditioner > cg;
  545. Eigen::ConjugateGradient< Eigen::SparseMatrix<Real> > cg(A);
  546. //Eigen::BiCGSTAB<Eigen::SparseMatrix<Real> > cg(A);
  547. cg.setMaxIterations(3000);
  548. //cg.compute(A);
  549. //std::cout << "Computed " << std::endl;
  550. VectorXr u = cg.solve(g);
  551. std::cout << "#iterations: " << cg.iterations() << std::endl;
  552. std::cout << "estimated error: " << cg.error() << std::endl;
  553. vtkDoubleArray *gArray = vtkDoubleArray::New();
  554. vtkDoubleArray *uArray = vtkDoubleArray::New();
  555. uArray->SetNumberOfComponents(1);
  556. gArray->SetNumberOfComponents(1);
  557. for (int iu = 0; iu<u.size(); ++iu) {
  558. uArray->InsertTuple1(iu, u[iu]);
  559. gArray->InsertTuple1(iu, g[iu]);
  560. }
  561. uArray->SetName("u");
  562. gArray->SetName("g");
  563. vtkGrid->GetPointData()->AddArray(uArray);
  564. vtkGrid->GetPointData()->AddArray(gArray);
  565. vtkXMLDataSetWriter *Writer = vtkXMLDataSetWriter::New();
  566. Writer->SetInputData(vtkGrid);
  567. Writer->SetFileName(fname.c_str());
  568. Writer->Write();
  569. Writer->Delete();
  570. Surface->Delete();
  571. gArray->Delete();
  572. uArray->Delete();
  573. }
  574. // Uses simpon's rule to perform a definite integral of a
  575. // function (passed as a pointer). The integration occurs from
  576. // (Shamelessly adapted from http://en.wikipedia.org/wiki/Simpson's_rule
  577. Real FEM4EllipticPDE::CompositeSimpsons(vtkImplicitFunction* f, Real r0[3], Real r1[3], int n) {
  578. Vector3r R0(r0[0], r0[1], r0[2]);
  579. Vector3r R1(r1[0], r1[1], r1[2]);
  580. // force n to be even
  581. assert(n > 0);
  582. //n += (n % 2);
  583. Real h = dist(r0, r1) / (Real)(n) ;
  584. Real S = f->FunctionValue(r0) + f->FunctionValue(r1);
  585. Vector3r dr = (R1 - R0).array() / Real(n);
  586. Vector3r rx;
  587. rx.array() = R0.array() + dr.array();
  588. for (int i=1; i<n; i+=2) {
  589. S += 4. * f->FunctionValue(rx[0], rx[1], rx[2]);
  590. rx += 2.*dr;
  591. }
  592. rx.array() = R0.array() + 2*dr.array();
  593. for (int i=2; i<n-1; i+=2) {
  594. S += 2.*f->FunctionValue(rx[0], rx[1], rx[2]);
  595. rx += 2.*dr;
  596. }
  597. return h * S / 3.;
  598. }
  599. // Uses simpon's rule to perform a definite integral of a
  600. // function (passed as a pointer). The integration occurs from
  601. // (Shamelessly adapted from http://en.wikipedia.org/wiki/Simpson's_rule
  602. // This is just here as a convenience
  603. Real FEM4EllipticPDE::CompositeSimpsons(const Real& f, Real r0[3], Real r1[3], int n) {
  604. return dist(r0,r1)*f;
  605. /*
  606. Vector3r R0(r0[0], r0[1], r0[2]);
  607. Vector3r R1(r1[0], r1[1], r1[2]);
  608. // force n to be even
  609. assert(n > 0);
  610. //n += (n % 2);
  611. Real h = dist(r0, r1) / (Real)(n) ;
  612. Real S = f + f;
  613. Vector3r dr = (R1 - R0).array() / Real(n);
  614. //Vector3r rx;
  615. //rx.array() = R0.array() + dr.array();
  616. for (int i=1; i<n; i+=2) {
  617. S += 4. * f;
  618. //rx += 2.*dr;
  619. }
  620. //rx.array() = R0.array() + 2*dr.array();
  621. for (int i=2; i<n-1; i+=2) {
  622. S += 2. * f;
  623. //rx += 2.*dr;
  624. }
  625. return h * S / 3.;
  626. */
  627. }
  628. /*
  629. * Performs numerical integration of two functions, one is passed as a vtkImplicitFunction, the other is the FEM
  630. * test function owned by the FEM implimentaion.
  631. */
  632. Real FEM4EllipticPDE::CompositeSimpsons2(vtkImplicitFunction* f,
  633. Real r0[3], Real r1[3], int n) {
  634. Vector3r R0(r0[0], r0[1], r0[2]);
  635. Vector3r R1(r1[0], r1[1], r1[2]);
  636. // force n to be even
  637. assert(n > 0);
  638. //n += (n % 2);
  639. Real h = dist(r0, r1) / (Real)(n) ;
  640. // For Gelerkin (most) FEM, we can omit this one as test functions are zero valued at element boundaries
  641. Real S = f->FunctionValue(r0) + f->FunctionValue(r1);
  642. //Real S = f->FunctionValue(r0) + f->FunctionValue(r1);
  643. Vector3r dr = (R1 - R0).array() / Real(n);
  644. Vector3r rx;
  645. rx.array() = R0.array() + dr.array();
  646. for (int i=1; i<n; i+=2) {
  647. S += 4. * f->FunctionValue(rx[0], rx[1], rx[2]) * Hat(rx, R0, R1) * Hat(rx, R1, R0);
  648. rx += 2.*dr;
  649. }
  650. rx.array() = R0.array() + 2*dr.array();
  651. for (int i=2; i<n-1; i+=2) {
  652. S += 2. * f->FunctionValue(rx[0], rx[1], rx[2]) * Hat(rx, R0, R1) * Hat(rx, R1, R0);
  653. rx += 2.*dr;
  654. }
  655. return h * S / 3.;
  656. }
  657. /*
  658. * Performs numerical integration of two functions, one is passed as a vtkImplicitFunction, the other is the FEM
  659. * test function owned by the FEM implimentaion.
  660. */
  661. Real FEM4EllipticPDE::CompositeSimpsons2( Real (*f)(const Real&, const Real&, const Real&),
  662. Real r0[3], Real r1[3], int n) {
  663. Vector3r R0(r0[0], r0[1], r0[2]);
  664. Vector3r R1(r1[0], r1[1], r1[2]);
  665. // force n to be even
  666. assert(n > 0);
  667. //n += (n % 2);
  668. Real h = dist(r0, r1) / (Real)(n) ;
  669. // For Gelerkin (most) FEM, we can omit this one as test functions are zero valued at element boundaries
  670. //Real S = f(r0[0], r0[1], r0[2])*Hat(R0, R0, R1) + f(r1[0], r1[1], r1[2])*Hat(R1, R0, R1);
  671. Real S = f(r0[0], r0[1], r0[2]) + f(r1[0], r1[1], r1[2]);
  672. Vector3r dr = (R1 - R0).array() / Real(n);
  673. Vector3r rx;
  674. rx.array() = R0.array() + dr.array();
  675. for (int i=1; i<n; i+=2) {
  676. S += 4. * f(rx[0], rx[1], rx[2]) * Hat(rx, R0, R1) * Hat(rx, R1, R0);
  677. rx += 2.*dr;
  678. }
  679. rx.array() = R0.array() + 2*dr.array();
  680. for (int i=2; i<n-1; i+=2) {
  681. S += 2. * f(rx[0], rx[1], rx[2]) * Hat(rx, R0, R1) * Hat(rx, R1, R0);
  682. rx += 2.*dr;
  683. }
  684. return h * S / 3.;
  685. }
  686. /*
  687. * Performs numerical integration of two functions, one is constant valued f, the other is the FEM
  688. * test function owned by the FEM implimentaion.
  689. */
  690. Real FEM4EllipticPDE::CompositeSimpsons2( const Real& f, Real r0[3], Real r1[3], int n) {
  691. Vector3r R0(r0[0], r0[1], r0[2]);
  692. Vector3r R1(r1[0], r1[1], r1[2]);
  693. // force n to be even
  694. assert(n > 0);
  695. //n += (n % 2);
  696. Real h = dist(r0, r1) / (Real)(n) ;
  697. // For Gelerkin (most) FEM, we can omit this one as test functions are zero valued at element boundaries
  698. Real S = 2*f; //*Hat(R0, R0, R1) + f*Hat(R1, R0, R1);
  699. Vector3r dr = (R1 - R0).array() / Real(n);
  700. Vector3r rx;
  701. rx.array() = R0.array() + dr.array();
  702. for (int i=1; i<n; i+=2) {
  703. S += 4. * f * Hat(rx, R0, R1) * Hat(rx, R1, R0);
  704. rx += 2.*dr;
  705. }
  706. rx.array() = R0.array() + 2*dr.array();
  707. for (int i=2; i<n-1; i+=2) {
  708. S += 2. * f * Hat(rx, R0, R1) * Hat(rx, R1, R0);
  709. rx += 2.*dr;
  710. }
  711. return h * S / 3.;
  712. }
  713. /*
  714. * Performs numerical integration of two functions, one is passed as a vtkImplicitFunction, the other is the FEM
  715. * test function owned by the FEM implimentaion.
  716. */
  717. Real FEM4EllipticPDE::CompositeSimpsons2( const Real& f0, const Real& f1, Real r0[3], Real r1[3], int n) {
  718. Vector3r R0(r0[0], r0[1], r0[2]);
  719. Vector3r R1(r1[0], r1[1], r1[2]);
  720. // force n to be even
  721. assert(n > 0);
  722. //n += (n % 2);
  723. Real h = dist(r0, r1) / (Real)(n) ;
  724. // For Gelerkin (most) FEM, we can omit this one as test functions are zero valued at element boundaries
  725. // NO! We are looking at 1/2 hat often!!! So S = f1!
  726. Real S = f1; //f0*Hat(R0, R0, R1) + f1*Hat(R1, R0, R1);
  727. Vector3r dr = (R1 - R0).array() / Real(n);
  728. // linear interpolate function
  729. //Vector3r rx;
  730. //rx.array() = R0.array() + dr.array();
  731. for (int i=1; i<n; i+=2) {
  732. double fx = f0 + (f1 - f0) * ((i*h)/(h*n));
  733. S += 4. * fx * Hat(R0.array() + i*dr.array(), R0, R1);// * Hat(R1.array() + i*dr.array(), R1, R0) ;
  734. }
  735. //rx.array() = R0.array() + 2*dr.array();
  736. for (int i=2; i<n-1; i+=2) {
  737. double fx = f0 + (f1 - f0) * ((i*h)/(h*n));
  738. S += 2. * fx * Hat(R0.array() + i*dr.array(), R0, R1);// * Hat(R1.array() + i*dr.array(), R1, R0);
  739. }
  740. return h * S / 3.;
  741. }
  742. /*
  743. * Performs numerical integration of two functions, one is passed as a vtkImplicitFunction, the other is the FEM
  744. * test function owned by the FEM implimentaion.
  745. */
  746. Real FEM4EllipticPDE::CompositeSimpsons3( const Real& f0, const Real& f1, Real r0[3], Real r1[3], int n) {
  747. Vector3r R0(r0[0], r0[1], r0[2]);
  748. Vector3r R1(r1[0], r1[1], r1[2]);
  749. // force n to be even
  750. assert(n > 0);
  751. //n += (n % 2);
  752. Real h = dist(r0, r1) / (Real)(n) ;
  753. // For Gelerkin (most) FEM, we can omit this one as test functions are zero valued at element boundaries
  754. // NO! We are looking at 1/2 hat often!!! So S = f1!
  755. Real S = f0+f1; //Hat(R0, R0, R1) + f1*Hat(R1, R0, R1);
  756. Vector3r dr = (R1 - R0).array() / Real(n);
  757. // linear interpolate function
  758. //Vector3r rx;
  759. //rx.array() = R0.array() + dr.array();
  760. for (int i=1; i<n; i+=2) {
  761. double fx = 1;// f0 + (f1 - f0) * ((i*h)/(h*n));
  762. S += 4. * fx * Hat(R0.array() + i*dr.array(), R0, R1) * Hat(R1.array() + i*dr.array(), R1, R0) ;
  763. }
  764. //rx.array() = R0.array() + 2*dr.array();
  765. for (int i=2; i<n-1; i+=2) {
  766. double fx = 1; // f0 + (f1 - f0) * ((i*h)/(h*n));
  767. S += 2. * fx * Hat(R0.array() + i*dr.array(), R0, R1)* Hat(R1.array() + i*dr.array(), R1, R0);
  768. }
  769. return h * S / 3.;
  770. }
  771. //--------------------------------------------------------------------------------------
  772. // Class: FEM4EllipticPDE
  773. // Method: Hat
  774. //--------------------------------------------------------------------------------------
  775. Real FEM4EllipticPDE::Hat ( const Vector3r& r, const Vector3r& r0, const Vector3r& r1 ) {
  776. //return (r-r0).norm() / (r1-r0).norm() ;
  777. return dist(r, r0) / dist(r1, r0) ;
  778. } // ----- end of method FEM4EllipticPDE::Hat -----
  779. } // ----- end of Lemma name -----