Galerkin FEM for elliptic PDEs
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

FEM4EllipticPDE.cpp 46KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122
  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(10), BndrySigma(10),
  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. //SetupLineSourcePotential();
  167. if (vtkGrid->GetPointData()->GetScalars("G") ) {
  168. SetupSurfaceSourcePotential();
  169. } else if (vtkGrid->GetCellData()->GetScalars("G") ) {
  170. SetupPotential();
  171. }
  172. //ConstructLoadVector();
  173. std::cout << "\nSolving" << std::endl;
  174. std::cout << std::setw(5) << " " << std::setw(14) << "rows" << std::setw(14) << "cols" << std::endl;
  175. std::cout << std::setw(5) << " " << std::setw(14) << "--------" << std::setw(14) << "--------" << std::endl;
  176. std::cout << std::setw(5) << "A:" << std::setw(14) << A.rows() << std::setw(14) << A.cols() << std::endl;
  177. std::cout << std::setw(5) << "g:" << std::setw(14) << g.rows() << std::setw(14) << g.cols() << std::endl;
  178. ////////////////////////////////////////////////////////////
  179. // Solving:
  180. //Eigen::SimplicialCholesky<Eigen::SparseMatrix<Real>, Eigen::Lower > chol(A); // performs a Cholesky factorization of A
  181. //VectorXr u = chol.solve(g);
  182. //#define LUSOLVE
  183. #ifdef LUSOLVE
  184. Eigen::SparseLU<Eigen::SparseMatrix<Real, Eigen::ColMajor>, Eigen::COLAMDOrdering<int> > solver;
  185. std::cout << "LU analyze pattern" << std::endl;
  186. solver.analyzePattern(A);
  187. std::cout << "LU factorizing" << std::endl;
  188. solver.factorize(A);
  189. std::cout << "LU solving" << std::endl;
  190. solver.factorize(A);
  191. VectorXr u = solver.solve(g);
  192. #endif
  193. #define CGSOLVE
  194. #ifdef CGSOLVE
  195. // TODO try IncompleteLUT preconditioner
  196. Eigen::BiCGSTAB<Eigen::SparseMatrix<Real> > cg(A);
  197. //cg.setMaxIterations(3000);
  198. //cg.setTolerance(1e-28);
  199. VectorXr u = cg.solve(g);
  200. std::cout << "#iterations: " << cg.iterations() << std::endl;
  201. std::cout << "estimated error: " << cg.error() << std::endl;
  202. #endif
  203. vtkDoubleArray *gArray = vtkDoubleArray::New();
  204. vtkDoubleArray *uArray = vtkDoubleArray::New();
  205. uArray->SetNumberOfComponents(1);
  206. gArray->SetNumberOfComponents(1);
  207. for (int iu = 0; iu<u.size(); ++iu) {
  208. uArray->InsertTuple1(iu, u[iu]);
  209. gArray->InsertTuple1(iu, g[iu]);
  210. }
  211. uArray->SetName("u");
  212. gArray->SetName("g");
  213. vtkGrid->GetPointData()->AddArray(uArray);
  214. vtkGrid->GetPointData()->AddArray(gArray);
  215. vtkXMLDataSetWriter *Writer = vtkXMLDataSetWriter::New();
  216. Writer->SetInputData(vtkGrid);
  217. Writer->SetFileName(resfile.c_str());
  218. Writer->Write();
  219. Writer->Delete();
  220. gArray->Delete();
  221. uArray->Delete();
  222. }
  223. //--------------------------------------------------------------------------------------
  224. // Class: FEM4EllipticPDE
  225. // Method: ConstructAMatrix
  226. //--------------------------------------------------------------------------------------
  227. void FEM4EllipticPDE::ConstructAMatrix ( ) {
  228. /////////////////////////////////////////////////////////////////////////
  229. // Build stiffness matrix (A)
  230. std::cout << "Building Stiffness Matrix (A) " << std::endl;
  231. std::cout << "\tMesh has " << vtkGrid->GetNumberOfCells() << " cells " << std::endl;
  232. std::cout << "\tMesh has " << vtkGrid->GetNumberOfPoints() << " points " << std::endl;
  233. //Eigen::SparseMatrix<Real>
  234. A.resize(vtkGrid->GetNumberOfPoints(), vtkGrid->GetNumberOfPoints());
  235. std::vector< Eigen::Triplet<Real> > coeffs;
  236. if ( !vtkGrid->GetPointData()->GetScalars("HomogeneousDirichlet") ) {
  237. throw std::runtime_error("No HomogeneousDirichlet boundary conditions in input file.");
  238. }
  239. if ( !vtkGrid->GetCellData()->GetScalars("G") && !vtkGrid->GetPointData()->GetScalars("G") ) {
  240. throw std::runtime_error("No Cell or Point Data G");
  241. }
  242. bool GCell = false;
  243. if ( vtkGrid->GetCellData()->GetScalars("G") ) {
  244. GCell = true;
  245. }
  246. // Here we iterate over all of the cells
  247. for (int ic=0; ic < vtkGrid->GetNumberOfCells(); ++ic) {
  248. assert ( vtkGrid->GetCell(ic)->GetNumberOfPoints() == 4 );
  249. // TODO, in production code we might not want to do this check here
  250. if ( vtkGrid->GetCell(ic)->GetNumberOfPoints() != 4 ) {
  251. throw std::runtime_error("Non-tetrahedral mesh encountered!");
  252. }
  253. // construct coordinate matrix C
  254. Eigen::Matrix<Real, 4, 4> C = Eigen::Matrix<Real, 4, 4>::Zero() ;
  255. for (int ii=0; ii<4; ++ii) {
  256. double* pts = vtkGrid->GetCell(ic)->GetPoints()->GetPoint(ii); //[ipc] ;
  257. C(ii, 0) = 1;
  258. C(ii, 1) = pts[0] ;
  259. C(ii, 2) = pts[1] ;
  260. C(ii, 3) = pts[2] ;
  261. }
  262. Eigen::Matrix<Real, 4, 4> GradPhi = C.inverse(); // nabla \phi
  263. Real V = (1./6.) * C.determinant(); // volume of tetrahedra
  264. vtkIdList* Ids = vtkGrid->GetCell(ic)->GetPointIds();
  265. int ID[4];
  266. ID[0] = Ids->GetId(0);
  267. ID[1] = Ids->GetId(1);
  268. ID[2] = Ids->GetId(2);
  269. ID[3] = Ids->GetId(3);
  270. Real sigma_bar(0);
  271. // TEST VOID IN SIGMA!! TODO DON"T KEEP THIS
  272. /*
  273. Real xc = C.col(1).array().mean();
  274. Real yc = C.col(2).array().mean();
  275. Real zc = C.col(3).array().mean();
  276. if ( xc >= 2.5 && xc <= 5. && yc>=2.5 && yc <= 5.) {
  277. sigma_bar = 0.;
  278. } else {
  279. sigma_bar = 1.;
  280. }
  281. */
  282. sigma_bar = 1.;
  283. for (int ii=0; ii<4; ++ii) {
  284. int bbi = vtkGrid->GetPointData()->GetScalars("HomogeneousDirichlet")->GetTuple(ID[ii])[0];
  285. if (bbi) {
  286. /* Dirichlet boundary */
  287. coeffs.push_back( Eigen::Triplet<Real> ( ID[ii], ID[ii], 1));
  288. } else {
  289. for (int jj=0; jj<4; ++jj) {
  290. coeffs.push_back( Eigen::Triplet<Real> ( ID[ii], ID[jj], GradPhi.col(ii).tail<3>().dot(GradPhi.col(jj).tail<3>() ) * V * sigma_bar ) );
  291. }
  292. }
  293. }
  294. std::cout << "\r" << (int)(1e2*((float)(ic) / (float)(vtkGrid->GetNumberOfCells()))) << std::flush ;
  295. }
  296. A.setFromTriplets(coeffs.begin(), coeffs.end());
  297. A.finalize();
  298. A.makeCompressed();
  299. }
  300. void FEM4EllipticPDE::SetupPotential() {
  301. ////////////////////////////////////////////////////////////
  302. // Load vector g
  303. std::cout << "\nBuilding load vector (g)" << std::endl;
  304. g = VectorXr::Zero(vtkGrid->GetNumberOfPoints());
  305. std::cout << "made g with " << vtkGrid->GetNumberOfPoints() << " points" << std::endl;
  306. for (int ic=0; ic < vtkGrid->GetNumberOfCells(); ++ic) {
  307. Eigen::Matrix<Real, 4, 4> C = Eigen::Matrix<Real, 4, 4>::Zero() ;
  308. for (int ii=0; ii<4; ++ii) {
  309. double* pts = vtkGrid->GetCell(ic)->GetPoints()->GetPoint(ii); //[ipc] ;
  310. C(ii, 0) = 1;
  311. C(ii, 1) = pts[0];
  312. C(ii, 2) = pts[1];
  313. C(ii, 3) = pts[2];
  314. }
  315. Real V = (1./6.) * C.determinant(); // volume of tetrahedra
  316. //Eigen::Matrix<Real, 4, 4> GradPhi = C.inverse(); // nabla \phi
  317. /* The indices */
  318. vtkIdList* Ids = vtkGrid->GetCell(ic)->GetPointIds();
  319. int ID[4];
  320. ID[0] = Ids->GetId(0);
  321. ID[1] = Ids->GetId(1);
  322. ID[2] = Ids->GetId(2);
  323. ID[3] = Ids->GetId(3);
  324. /* Fill the RHS vector with Dirichlet conditions or fuction value */
  325. for (int ii=0; ii<4; ++ii) {
  326. if (vtkGrid->GetPointData()->GetScalars("HomogeneousDirichlet")->GetTuple(ID[ii])[0]) {
  327. g(ID[ii]) += vtkGrid->GetPointData()->GetScalars("analytic_phi")->GetTuple(ID[ii])[0];
  328. } else {
  329. g(ID[ii]) += (V/4.)*(vtkGrid->GetCellData()->GetScalars("G")->GetTuple(ic)[0]); // Cell data
  330. /* for point data, determine if it is a point or line source */
  331. //g(ID[ii]) = (vtkGrid->GetPointData()->GetScalars("G")->GetTuple(ID[ii])[0]); // Point source
  332. //g(ID[ii]) = (vtkGrid->GetPointData()->GetScalars("G")->GetTuple(ID[ii])[0]); // Point source
  333. }
  334. }
  335. }
  336. }
  337. void FEM4EllipticPDE::SetupLineSourcePotential() {
  338. std::cerr << " FEM4EllipticPDE::SetupLineSourcePotential is not completed\n";
  339. exit(1);
  340. ////////////////////////////////////////////////////////////
  341. // Load vector g
  342. std::cout << "\nBuilding load vector (g)" << std::endl;
  343. g = VectorXr::Zero(vtkGrid->GetNumberOfPoints());
  344. std::cout << "made g with " << vtkGrid->GetNumberOfPoints() << " points" << std::endl;
  345. for (int ic=0; ic < vtkGrid->GetNumberOfCells(); ++ic) {
  346. Eigen::Matrix<Real, 4, 4> C = Eigen::Matrix<Real, 4, 4>::Zero() ;
  347. for (int ii=0; ii<4; ++ii) {
  348. double* pts = vtkGrid->GetCell(ic)->GetPoints()->GetPoint(ii); //[ipc] ;
  349. C(ii, 0) = 1;
  350. C(ii, 1) = pts[0];
  351. C(ii, 2) = pts[1];
  352. C(ii, 3) = pts[2];
  353. }
  354. Real V = (1./6.) * C.determinant(); // volume of tetrahedra
  355. //Eigen::Matrix<Real, 4, 4> GradPhi = C.inverse(); // nabla \phi
  356. /* The indices */
  357. vtkIdList* Ids = vtkGrid->GetCell(ic)->GetPointIds();
  358. int ID[4];
  359. ID[0] = Ids->GetId(0);
  360. ID[1] = Ids->GetId(1);
  361. ID[2] = Ids->GetId(2);
  362. ID[3] = Ids->GetId(3);
  363. /* Line source between nodes */
  364. for (int ii=0; ii<4; ++ii) {
  365. if (vtkGrid->GetPointData()->GetScalars("HomogeneousDirichlet")->GetTuple(ID[ii])[0]) {
  366. g(ID[ii]) += vtkGrid->GetPointData()->GetScalars("analytic_phi")->GetTuple(ID[ii])[0];
  367. } else {
  368. Real nv1 = vtkGrid->GetPointData()->GetScalars("G")->GetTuple(ID[ii])[0];
  369. for (int jj=ii+1; jj<4; ++jj) {
  370. Real nv2 = vtkGrid->GetPointData()->GetScalars("G")->GetTuple(ID[jj])[0];
  371. if ( std::abs(nv1) > 1e-12 && std::abs(nv2) > 1e-12) {
  372. Real length = ( C.row(ii).tail<3>()-C.row(jj).tail<3>() ).norm();
  373. g(ID[ii]) += ((nv1+nv2)/2.)/(length);//*(V/4.);// * (vtkGrid->GetPointData()->GetScalars("G")->GetTuple(ID[ii])[0]); // Point source
  374. }
  375. }
  376. }
  377. }
  378. }
  379. }
  380. void FEM4EllipticPDE::SetupSurfaceSourcePotential() {
  381. ////////////////////////////////////////////////////////////
  382. // Load vector g
  383. std::cout << "\nBuilding load vector (g)" << std::endl;
  384. g = VectorXr::Zero(vtkGrid->GetNumberOfPoints());
  385. std::cout << "made g with " << vtkGrid->GetNumberOfPoints() << " points" << std::endl;
  386. for (int ic=0; ic < vtkGrid->GetNumberOfCells(); ++ic) {
  387. Eigen::Matrix<Real, 4, 4> C = Eigen::Matrix<Real, 4, 4>::Zero() ;
  388. for (int ii=0; ii<4; ++ii) {
  389. double* pts = vtkGrid->GetCell(ic)->GetPoints()->GetPoint(ii); //[ipc] ;
  390. C(ii, 0) = 1;
  391. C(ii, 1) = pts[0];
  392. C(ii, 2) = pts[1];
  393. C(ii, 3) = pts[2];
  394. }
  395. Real V = (1./6.) * C.determinant(); // volume of tetrahedra
  396. //Eigen::Matrix<Real, 4, 4> GradPhi = C.inverse(); // nabla \phi
  397. /* The indices */
  398. vtkIdList* Ids = vtkGrid->GetCell(ic)->GetPointIds();
  399. int ID[4];
  400. ID[0] = Ids->GetId(0);
  401. ID[1] = Ids->GetId(1);
  402. ID[2] = Ids->GetId(2);
  403. ID[3] = Ids->GetId(3);
  404. // Apply Dirichlet conditions
  405. for (int ii=0; ii<4; ++ii) {
  406. if (vtkGrid->GetPointData()->GetScalars("HomogeneousDirichlet")->GetTuple(ID[ii])[0]) {
  407. g(ID[ii]) += vtkGrid->GetPointData()->GetScalars("analytic_phi")->GetTuple(ID[ii])[0];
  408. }
  409. }
  410. /* the 4 faces of the tetrahedra
  411. ID[0] ID[1] ID[2]
  412. ID[0] ID[1] ID[3]
  413. ID[0] ID[2] ID[3]
  414. ID[1] ID[2] ID[3]
  415. */
  416. //Real i4pi = .99738684646226885559*.961*PI/6.;//(4.*PI);// * V/4.;
  417. //Real i4pi = .99738684646226885559*.961*PI/6.;//(4.*PI);// * V/4.;
  418. //Real i4pi = 0.9584887594502403*PI/6.;//(4.*PI);// * V/4.;
  419. Real i4pi = .5;// 0.1597132348845018*PI; // very nearly .5
  420. /* surfaces of tetrahedra */
  421. // Face 0, ID 0,1,2
  422. Eigen::Matrix<Real, 3, 3> CC = Eigen::Matrix<Real, 3, 3>::Ones() ;
  423. if ( std::abs(vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[0])) > 1e-12 &&
  424. std::abs(vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[1])) > 1e-12 &&
  425. std::abs(vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[2])) > 1e-12 ) {
  426. CC.col(1) = C.row(0).tail<3>() - C.row(1).tail<3>();
  427. CC.col(2) = C.row(0).tail<3>() - C.row(2).tail<3>();
  428. Real TA = .5*CC.col(1).cross(CC.col(2)).norm();
  429. g(ID[0]) += vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[0])*TA/3. * i4pi ;
  430. g(ID[1]) += vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[1])*TA/3. * i4pi ;
  431. g(ID[2]) += vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[2])*TA/3. * i4pi ;
  432. }
  433. // Face 1, ID 0,1,3
  434. if ( std::abs(vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[0])) > 1e-12 &&
  435. std::abs(vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[1])) > 1e-12 &&
  436. std::abs(vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[3])) > 1e-12 ) {
  437. CC.col(1) = C.row(0).tail<3>() - C.row(1).tail<3>();
  438. CC.col(2) = C.row(0).tail<3>() - C.row(3).tail<3>();
  439. Real TA = .5*CC.col(1).cross(CC.col(2)).norm();
  440. g(ID[0]) += vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[0])*TA/3. * i4pi ;
  441. g(ID[1]) += vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[1])*TA/3. * i4pi ;
  442. g(ID[3]) += vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[3])*TA/3. * i4pi ;
  443. }
  444. // Face 2, ID 0,2,3
  445. if ( std::abs(vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[0])) > 1e-12 &&
  446. std::abs(vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[2])) > 1e-12 &&
  447. std::abs(vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[3])) > 1e-12 ) {
  448. CC.col(1) = C.row(0).tail<3>() - C.row(2).tail<3>();
  449. CC.col(2) = C.row(0).tail<3>() - C.row(3).tail<3>();
  450. Real TA = .5*CC.col(1).cross(CC.col(2)).norm();
  451. g(ID[0]) += vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[0])*TA/3. * i4pi ;
  452. g(ID[2]) += vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[2])*TA/3. * i4pi ;
  453. g(ID[3]) += vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[3])*TA/3. * i4pi ;
  454. }
  455. // Face 3, ID 1,2,3
  456. if ( std::abs(vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[1])) > 1e-12 &&
  457. std::abs(vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[2])) > 1e-12 &&
  458. std::abs(vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[3])) > 1e-12 ) {
  459. CC.col(1) = C.row(1).tail<3>() - C.row(2).tail<3>();
  460. CC.col(2) = C.row(1).tail<3>() - C.row(3).tail<3>();
  461. Real TA = .5*CC.col(1).cross(CC.col(2)).norm();
  462. g(ID[1]) += vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[1])*TA/3. * i4pi ;
  463. g(ID[2]) += vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[2])*TA/3. * i4pi ;
  464. g(ID[3]) += vtkGrid->GetPointData()->GetScalars("G")->GetTuple1(ID[3])*TA/3. * i4pi ;
  465. }
  466. }
  467. }
  468. void FEM4EllipticPDE::SolveOLD2(const std::string& fname) {
  469. Real r0[3];
  470. Real r1[3];
  471. /////////////////////////////////////////////////////////////////////////
  472. // Surface filter, to determine if points are on boundary, and need
  473. // boundary conditions applied
  474. vtkDataSetSurfaceFilter* Surface = vtkDataSetSurfaceFilter::New();
  475. Surface->SetInputData(vtkGrid);
  476. Surface->PassThroughPointIdsOn( );
  477. Surface->Update();
  478. vtkIdTypeArray* BdryIds = static_cast<vtkIdTypeArray*>
  479. (Surface->GetOutput()->GetPointData()->GetScalars("vtkOriginalPointIds"));
  480. // Expensive search for whether or not point is on boundary. O(n) cost.
  481. VectorXi bndryCnt = VectorXi::Zero(vtkGrid->GetNumberOfPoints());
  482. for (int isp=0; isp < Surface->GetOutput()->GetNumberOfPoints(); ++isp) {
  483. //double* pts = vtkGrid->GetCell(ic)->GetPoints()->GetPoint(ii); //[ipc] ;
  484. // x \in -14.5 to 14.5
  485. // y \in 0 to 30
  486. bndryCnt(BdryIds->GetTuple1(isp)) += 1;
  487. }
  488. /////////////////////////////////////////////////////////////////////////
  489. // Build stiffness matrix (A)
  490. std::cout << "Building Stiffness Matrix (A) " << std::endl;
  491. std::cout << "\tMesh has " << vtkGrid->GetNumberOfCells() << " cells " << std::endl;
  492. std::cout << "\tMesh has " << vtkGrid->GetNumberOfPoints() << " points " << std::endl;
  493. Eigen::SparseMatrix<Real> A(vtkGrid->GetNumberOfPoints(), vtkGrid->GetNumberOfPoints());
  494. std::vector< Eigen::Triplet<Real> > coeffs;
  495. // Here we iterate over all of the cells
  496. for (int ic=0; ic < vtkGrid->GetNumberOfCells(); ++ic) {
  497. assert ( vtkGrid->GetCell(ic)->GetNumberOfPoints() == 4 );
  498. // TODO, in production code we might not want to do this check here
  499. if ( vtkGrid->GetCell(ic)->GetNumberOfPoints() != 4 ) {
  500. std::cout << "DOOM FEM4EllipticPDE encountered non-tetrahedral mesh\n";
  501. std::cout << "Number of points in cell " << vtkGrid->GetCell(ic)->GetNumberOfPoints() << std::endl ;
  502. exit(1);
  503. }
  504. // construct coordinate matrix C
  505. Eigen::Matrix<Real, 4, 4> C = Eigen::Matrix<Real, 4, 4>::Zero() ;
  506. for (int ii=0; ii<4; ++ii) {
  507. double* pts = vtkGrid->GetCell(ic)->GetPoints()->GetPoint(ii); //[ipc] ;
  508. C(ii, 0) = 1;
  509. C(ii, 1) = pts[0] ;
  510. C(ii, 2) = pts[1] ;
  511. C(ii, 3) = pts[2] ;
  512. }
  513. Eigen::Matrix<Real, 4, 4> GradPhi = C.inverse(); // nabla \phi
  514. Real V = (1./6.) * C.determinant(); // volume of tetrahedra
  515. vtkIdList* Ids = vtkGrid->GetCell(ic)->GetPointIds();
  516. int ID[4];
  517. ID[0] = Ids->GetId(0);
  518. ID[1] = Ids->GetId(1);
  519. ID[2] = Ids->GetId(2);
  520. ID[3] = Ids->GetId(3);
  521. Real sum(0);
  522. Real sigma_bar = vtkGrid->GetCellData()->GetScalars()->GetTuple1(ic);
  523. for (int ii=0; ii<4; ++ii) {
  524. for (int jj=0; jj<4; ++jj) {
  525. if (jj == ii) {
  526. // I apply boundary to Stiffness matrix, it's common to take the other approach and apply to the load vector and then
  527. // solve for the boundaries? Is one better? This seems to work, which is nice.
  528. //Real bdry = V*(1./(BndryH*BndryH))*BndrySigma*bndryCnt( ID[ii] ); // + sum;
  529. Real bb = vtkGrid->GetPointData()->GetScalars("vtkValidPointMask")->GetTuple(ID[ii])[0];
  530. //std::cout << "bb " << bb << std::endl;
  531. Real bdry = V*(1./(BndryH*BndryH))*BndrySigma*bb; // + sum;
  532. 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 ) );
  533. } else {
  534. coeffs.push_back( Eigen::Triplet<Real> ( ID[ii], ID[jj], GradPhi.col(ii).tail<3>().dot(GradPhi.col(jj).tail<3>() ) * V * sigma_bar ) );
  535. }
  536. // Stiffness matrix no longer contains boundary conditions...
  537. //coeffs.push_back( Eigen::Triplet<Real> ( ID[ii], ID[jj], GradPhi.col(ii).tail<3>().dot(GradPhi.col(jj).tail<3>() ) * V * sigma_bar ) );
  538. }
  539. }
  540. std::cout << "\r" << (int)(1e2*((float)(ic) / (float)(vtkGrid->GetNumberOfCells()))) << std::flush ;
  541. }
  542. A.setFromTriplets(coeffs.begin(), coeffs.end());
  543. //A.makeCompressed();
  544. ////////////////////////////////////////////////////////////
  545. // Load vector g, solution vector u
  546. std::cout << "\nBuilding load vector (g)" << std::endl;
  547. VectorXr g = VectorXr::Zero(vtkGrid->GetNumberOfPoints());
  548. std::cout << "made g with " << vtkGrid->GetNumberOfPoints() << " pnts" << std::endl;
  549. // If the G function has been evaluated at each *node*
  550. // --> but still needs to be integrated at the surfaces
  551. // Aha, requires that there is in fact a pointdata memeber // BUG TODO BUG!!!
  552. std::cout << "Point Data ptr " << vtkGrid->GetPointData() << std::endl;
  553. //if ( vtkGrid->GetPointData() != NULL && std::string( vtkGrid->GetPointData()->GetScalars()->GetName() ).compare( std::string("G") ) == 0 ) {
  554. bool pe(false);
  555. bool ne(false);
  556. if ( true ) {
  557. std::cout << "\nUsing G from file" << std::endl;
  558. /* 3D Phi */
  559. for (int ic=0; ic < vtkGrid->GetNumberOfCells(); ++ic) {
  560. Eigen::Matrix<Real, 4, 4> C = Eigen::Matrix<Real, 4, 4>::Zero() ;
  561. for (int ii=0; ii<4; ++ii) {
  562. double* pts = vtkGrid->GetCell(ic)->GetPoints()->GetPoint(ii); //[ipc] ;
  563. C(ii, 0) = 1;
  564. C(ii, 1) = pts[0];
  565. C(ii, 2) = pts[1];
  566. C(ii, 3) = pts[2];
  567. }
  568. Real V = (1./6.) * C.determinant(); // volume of tetrahedra
  569. //Real V = C.determinant(); // volume of tetrahedra
  570. vtkIdList* Ids = vtkGrid->GetCell(ic)->GetPointIds();
  571. int ID[4];
  572. ID[0] = Ids->GetId(0);
  573. ID[1] = Ids->GetId(1);
  574. ID[2] = Ids->GetId(2);
  575. ID[3] = Ids->GetId(3);
  576. /* bad news bears for magnet */
  577. double* pt = vtkGrid->GetCell(ic)->GetPoints()->GetPoint(0);
  578. Real sum(0);
  579. /*
  580. if (!pe) {
  581. if (std::abs(pt[0]) < .2 && std::abs(pt[1]-15) < .2 && pt[2] < 3.25 ) {
  582. sum = 1; //vtkGrid->GetPointData()->GetScalars()->GetTuple(ID[ii])[0];
  583. pe = true;
  584. }
  585. }*/
  586. if (ID[0] == 26) {
  587. sum = 10;
  588. }
  589. if (ID[0] == 30) {
  590. sum = -10;
  591. }
  592. /*
  593. if (!ne) {
  594. if (std::abs(pt[0]+1.) < .2 && std::abs(pt[1]-15) < .2 && pt[2] < 3.25 ) {
  595. sum = -1; //vtkGrid->GetPointData()->GetScalars()->GetTuple(ID[ii])[0];
  596. std::cout << "Negative Electroce\n";
  597. ne = true;
  598. }
  599. }
  600. */
  601. //for (int ii=0; ii<4; ++ii) {
  602. //g(ID[ii]) += (V/4.) * ( vtkGrid->GetPointData()->GetScalars()->GetTuple(ID[ii])[0] ) ;
  603. //if ( std::abs(vtkGrid->GetPointData()->GetScalars()->GetTuple(ID[ii])[0]) > 1e-3 )
  604. //}
  605. // TODO check Load Vector...
  606. g(ID[0]) = sum; //(V/4.) * sum; // Why 3, Why!!!?
  607. std::cout << "\r" << (int)(1e2*((float)(ic) / (float)(vtkGrid->GetNumberOfCells()))) << std::flush ;
  608. }
  609. /*
  610. for (int ic=0; ic < vtkGrid->GetNumberOfPoints(); ++ic) {
  611. vtkGrid->GetPoint(ic, r0);
  612. vtkSmartPointer<vtkIdList> connectedVertices = GetConnectedPoints(ic);
  613. double g0 = vtkGrid->GetPointData()->GetScalars()->GetTuple(ic)[0] ;
  614. //std::cout << "num conn " << connectedVertices->GetNumberOfIds() << std::endl;
  615. if ( std::abs(g0) > 1e-3 ) {
  616. for(vtkIdType i = 0; i < connectedVertices->GetNumberOfIds(); ++i) {
  617. int ii = connectedVertices->GetId(i);
  618. vtkGrid->GetPoint(ii, r1);
  619. double g1 = vtkGrid->GetPointData()->GetScalars()->GetTuple(ii)[0] ;
  620. //g(ic) += g0*dist(r0,r1); //CompositeSimpsons2(g0, r0, r1, 8);
  621. if ( std::abs(g1) > 1e-3 ) {
  622. g(ic) += CompositeSimpsons2(g1, g0, r1, r0, 1000);
  623. }
  624. //g(ic) += CompositeSimpsons2(g0, r1, r0, 8);
  625. //if ( std::abs(g1) > 1e-3 ) {
  626. //g(ic) += CompositeSimpsons2(g0, g1, r0, r1, 8);
  627. //g(ic) += CompositeSimpsons2(g0, g1, r0, r1, 100); // / (2*dist(r0,r1)) ;
  628. // g(ic) += g0*dist(r0,r1); //CompositeSimpsons2(g0, r0, r1, 8);
  629. //g(ic) += CompositeSimpsons2(g0, r0, r1, 8);
  630. // g(ic) += g0; //CompositeSimpsons2(g0, r0, r1, 8);
  631. //} //else {
  632. // g(ic) += g0; //CompositeSimpsons2(g0, r0, r1, 8);
  633. //}
  634. }
  635. }
  636. //g(ic) = 2.* vtkGrid->GetPointData()->GetScalars()->GetTuple(ic)[0] ; // Why 2?
  637. //std::cout << "\r" << (int)(1e2*((float)(ic) / (float)(vtkGrid->GetNumberOfPoints()))) << std::flush ;
  638. }
  639. */
  640. } else if (vtkG) { // VTK implicit function, proceed with care
  641. std::cout << "\nUsing implicit file from file" << std::endl;
  642. // OpenMP right here
  643. for (int ic=0; ic < vtkGrid->GetNumberOfPoints(); ++ic) {
  644. vtkSmartPointer<vtkIdList> connectedVertices = GetConnectedPoints(ic);
  645. //vtkGrid->GetPoint(ic, r0);
  646. //g(ic) += vtkG->FunctionValue(r0[0], r0[1], r0[2]) ;
  647. // std::cout << vtkG->FunctionValue(r0[0], r0[1], r0[2]) << std::endl;
  648. //g(ic) += vtkGrid->GetPointData()->GetScalars()->GetTuple1(ic);// FunctionValue(r0[0], r0[1], r0[2]) ;
  649. /*
  650. for(vtkIdType i = 0; i < connectedVertices->GetNumberOfIds(); ++i) {
  651. int ii = connectedVertices->GetId(i);
  652. vtkGrid->GetPoint(ii, r1);
  653. g(ic) += CompositeSimpsons2(vtkG, r0, r1, 8); // vtkG->FunctionValue(r0[0], r0[1], r0[2]) ;
  654. }
  655. */
  656. std::cout << "\r" << (int)(1e2*((float)(ic) / (float)(vtkGrid->GetNumberOfPoints()))) << std::flush ;
  657. }
  658. } else if (gFcn3) {
  659. std::cout << "\nUsing gFcn3" << std::endl;
  660. for (int ic=0; ic < vtkGrid->GetNumberOfPoints(); ++ic) {
  661. vtkSmartPointer<vtkIdList> connectedVertices = GetConnectedPoints(ic);
  662. vtkGrid->GetPoint(ic, r0);
  663. // TODO, test OMP sum reduction here. Is vtkGrid->GetPoint thread safe?
  664. //Real sum(0.);
  665. //#ifdef LEMMAUSEOMP
  666. //#pragma omp parallel for reduction(+:sum)
  667. //#endif
  668. for(vtkIdType i = 0; i < connectedVertices->GetNumberOfIds(); ++i) {
  669. int ii = connectedVertices->GetId(i);
  670. vtkGrid->GetPoint(ii, r1);
  671. g(ic) += CompositeSimpsons2(gFcn3, r0, r1, 8); // vtkG->FunctionValue(r0[0], r0[1], r0[2]) ;
  672. //sum += CompositeSimpsons2(gFcn3, r0, r1, 8); // vtkG->FunctionValue(r0[0], r0[1], r0[2]) ;
  673. }
  674. std::cout << "\r" << (int)(1e2*((float)(ic) / (float)(vtkGrid->GetNumberOfPoints()))) << std::flush ;
  675. //g(ic) = sum;
  676. }
  677. } else {
  678. std::cout << "No source specified\n";
  679. exit(EXIT_FAILURE);
  680. }
  681. // std::cout << g << std::endl;
  682. //g(85) = 1;
  683. std::cout << "\nSolving" << std::endl;
  684. ////////////////////////////////////////////////////////////
  685. // Solving:
  686. //Eigen::SimplicialCholesky<Eigen::SparseMatrix<Real>, Eigen::Lower > chol(A); // performs a Cholesky factorization of A
  687. //VectorXr u = chol.solve(g);
  688. //Eigen::SparseLU<Eigen::SparseMatrix<Real, Eigen::ColMajor>, Eigen::COLAMDOrdering<int> > solver;
  689. //solver.analyzePattern(A);
  690. //solver.factorize(A);
  691. //VectorXr u = solver.solve(g);
  692. //Eigen::ConjugateGradient<Eigen::SparseMatrix<Real, Eigen::Lower > Eigen::DiagonalPreconditioner > cg;
  693. Eigen::ConjugateGradient< Eigen::SparseMatrix<Real> > cg(A);
  694. //Eigen::BiCGSTAB<Eigen::SparseMatrix<Real> > cg(A);
  695. cg.setMaxIterations(3000);
  696. //cg.compute(A);
  697. //std::cout << "Computed " << std::endl;
  698. VectorXr u = cg.solve(g);
  699. std::cout << "#iterations: " << cg.iterations() << std::endl;
  700. std::cout << "estimated error: " << cg.error() << std::endl;
  701. vtkDoubleArray *gArray = vtkDoubleArray::New();
  702. vtkDoubleArray *uArray = vtkDoubleArray::New();
  703. uArray->SetNumberOfComponents(1);
  704. gArray->SetNumberOfComponents(1);
  705. for (int iu = 0; iu<u.size(); ++iu) {
  706. uArray->InsertTuple1(iu, u[iu]);
  707. gArray->InsertTuple1(iu, g[iu]);
  708. }
  709. uArray->SetName("u");
  710. gArray->SetName("g");
  711. vtkGrid->GetPointData()->AddArray(uArray);
  712. vtkGrid->GetPointData()->AddArray(gArray);
  713. vtkXMLDataSetWriter *Writer = vtkXMLDataSetWriter::New();
  714. Writer->SetInputData(vtkGrid);
  715. Writer->SetFileName(fname.c_str());
  716. Writer->Write();
  717. Writer->Delete();
  718. Surface->Delete();
  719. gArray->Delete();
  720. uArray->Delete();
  721. }
  722. // Uses simpon's rule to perform a definite integral of a
  723. // function (passed as a pointer). The integration occurs from
  724. // (Shamelessly adapted from http://en.wikipedia.org/wiki/Simpson's_rule
  725. Real FEM4EllipticPDE::CompositeSimpsons(vtkImplicitFunction* f, Real r0[3], Real r1[3], int n) {
  726. Vector3r R0(r0[0], r0[1], r0[2]);
  727. Vector3r R1(r1[0], r1[1], r1[2]);
  728. // force n to be even
  729. assert(n > 0);
  730. //n += (n % 2);
  731. Real h = dist(r0, r1) / (Real)(n) ;
  732. Real S = f->FunctionValue(r0) + f->FunctionValue(r1);
  733. Vector3r dr = (R1 - R0).array() / Real(n);
  734. Vector3r rx;
  735. rx.array() = R0.array() + dr.array();
  736. for (int i=1; i<n; i+=2) {
  737. S += 4. * f->FunctionValue(rx[0], rx[1], rx[2]);
  738. rx += 2.*dr;
  739. }
  740. rx.array() = R0.array() + 2*dr.array();
  741. for (int i=2; i<n-1; i+=2) {
  742. S += 2.*f->FunctionValue(rx[0], rx[1], rx[2]);
  743. rx += 2.*dr;
  744. }
  745. return h * S / 3.;
  746. }
  747. // Uses simpon's rule to perform a definite integral of a
  748. // function (passed as a pointer). The integration occurs from
  749. // (Shamelessly adapted from http://en.wikipedia.org/wiki/Simpson's_rule
  750. // This is just here as a convenience
  751. Real FEM4EllipticPDE::CompositeSimpsons(const Real& f, Real r0[3], Real r1[3], int n) {
  752. return dist(r0,r1)*f;
  753. /*
  754. Vector3r R0(r0[0], r0[1], r0[2]);
  755. Vector3r R1(r1[0], r1[1], r1[2]);
  756. // force n to be even
  757. assert(n > 0);
  758. //n += (n % 2);
  759. Real h = dist(r0, r1) / (Real)(n) ;
  760. Real S = f + f;
  761. Vector3r dr = (R1 - R0).array() / Real(n);
  762. //Vector3r rx;
  763. //rx.array() = R0.array() + dr.array();
  764. for (int i=1; i<n; i+=2) {
  765. S += 4. * f;
  766. //rx += 2.*dr;
  767. }
  768. //rx.array() = R0.array() + 2*dr.array();
  769. for (int i=2; i<n-1; i+=2) {
  770. S += 2. * f;
  771. //rx += 2.*dr;
  772. }
  773. return h * S / 3.;
  774. */
  775. }
  776. /*
  777. * Performs numerical integration of two functions, one is passed as a vtkImplicitFunction, the other is the FEM
  778. * test function owned by the FEM implimentaion.
  779. */
  780. Real FEM4EllipticPDE::CompositeSimpsons2(vtkImplicitFunction* f,
  781. Real r0[3], Real r1[3], int n) {
  782. Vector3r R0(r0[0], r0[1], r0[2]);
  783. Vector3r R1(r1[0], r1[1], r1[2]);
  784. // force n to be even
  785. assert(n > 0);
  786. //n += (n % 2);
  787. Real h = dist(r0, r1) / (Real)(n) ;
  788. // For Gelerkin (most) FEM, we can omit this one as test functions are zero valued at element boundaries
  789. Real S = f->FunctionValue(r0) + f->FunctionValue(r1);
  790. //Real S = f->FunctionValue(r0) + f->FunctionValue(r1);
  791. Vector3r dr = (R1 - R0).array() / Real(n);
  792. Vector3r rx;
  793. rx.array() = R0.array() + dr.array();
  794. for (int i=1; i<n; i+=2) {
  795. S += 4. * f->FunctionValue(rx[0], rx[1], rx[2]) * Hat(rx, R0, R1) * Hat(rx, R1, R0);
  796. rx += 2.*dr;
  797. }
  798. rx.array() = R0.array() + 2*dr.array();
  799. for (int i=2; i<n-1; i+=2) {
  800. S += 2. * f->FunctionValue(rx[0], rx[1], rx[2]) * Hat(rx, R0, R1) * Hat(rx, R1, R0);
  801. rx += 2.*dr;
  802. }
  803. return h * S / 3.;
  804. }
  805. /*
  806. * Performs numerical integration of two functions, one is passed as a vtkImplicitFunction, the other is the FEM
  807. * test function owned by the FEM implimentaion.
  808. */
  809. Real FEM4EllipticPDE::CompositeSimpsons2( Real (*f)(const Real&, const Real&, const Real&),
  810. Real r0[3], Real r1[3], int n) {
  811. Vector3r R0(r0[0], r0[1], r0[2]);
  812. Vector3r R1(r1[0], r1[1], r1[2]);
  813. // force n to be even
  814. assert(n > 0);
  815. //n += (n % 2);
  816. Real h = dist(r0, r1) / (Real)(n) ;
  817. // For Gelerkin (most) FEM, we can omit this one as test functions are zero valued at element boundaries
  818. //Real S = f(r0[0], r0[1], r0[2])*Hat(R0, R0, R1) + f(r1[0], r1[1], r1[2])*Hat(R1, R0, R1);
  819. Real S = f(r0[0], r0[1], r0[2]) + f(r1[0], r1[1], r1[2]);
  820. Vector3r dr = (R1 - R0).array() / Real(n);
  821. Vector3r rx;
  822. rx.array() = R0.array() + dr.array();
  823. for (int i=1; i<n; i+=2) {
  824. S += 4. * f(rx[0], rx[1], rx[2]) * Hat(rx, R0, R1) * Hat(rx, R1, R0);
  825. rx += 2.*dr;
  826. }
  827. rx.array() = R0.array() + 2*dr.array();
  828. for (int i=2; i<n-1; i+=2) {
  829. S += 2. * f(rx[0], rx[1], rx[2]) * Hat(rx, R0, R1) * Hat(rx, R1, R0);
  830. rx += 2.*dr;
  831. }
  832. return h * S / 3.;
  833. }
  834. /*
  835. * Performs numerical integration of two functions, one is constant valued f, the other is the FEM
  836. * test function owned by the FEM implimentaion.
  837. */
  838. Real FEM4EllipticPDE::CompositeSimpsons2( const Real& f, Real r0[3], Real r1[3], int n) {
  839. Vector3r R0(r0[0], r0[1], r0[2]);
  840. Vector3r R1(r1[0], r1[1], r1[2]);
  841. // force n to be even
  842. assert(n > 0);
  843. //n += (n % 2);
  844. Real h = dist(r0, r1) / (Real)(n) ;
  845. // For Gelerkin (most) FEM, we can omit this one as test functions are zero valued at element boundaries
  846. Real S = 2*f; //*Hat(R0, R0, R1) + f*Hat(R1, R0, R1);
  847. Vector3r dr = (R1 - R0).array() / Real(n);
  848. Vector3r rx;
  849. rx.array() = R0.array() + dr.array();
  850. for (int i=1; i<n; i+=2) {
  851. S += 4. * f * Hat(rx, R0, R1) * Hat(rx, R1, R0);
  852. rx += 2.*dr;
  853. }
  854. rx.array() = R0.array() + 2*dr.array();
  855. for (int i=2; i<n-1; i+=2) {
  856. S += 2. * f * Hat(rx, R0, R1) * Hat(rx, R1, R0);
  857. rx += 2.*dr;
  858. }
  859. return h * S / 3.;
  860. }
  861. /*
  862. * Performs numerical integration of two functions, one is passed as a vtkImplicitFunction, the other is the FEM
  863. * test function owned by the FEM implimentaion.
  864. */
  865. Real FEM4EllipticPDE::CompositeSimpsons2( const Real& f0, const Real& f1, Real r0[3], Real r1[3], int n) {
  866. Vector3r R0(r0[0], r0[1], r0[2]);
  867. Vector3r R1(r1[0], r1[1], r1[2]);
  868. // force n to be even
  869. assert(n > 0);
  870. //n += (n % 2);
  871. Real h = dist(r0, r1) / (Real)(n) ;
  872. // For Gelerkin (most) FEM, we can omit this one as test functions are zero valued at element boundaries
  873. // NO! We are looking at 1/2 hat often!!! So S = f1!
  874. Real S = f1; //f0*Hat(R0, R0, R1) + f1*Hat(R1, R0, R1);
  875. Vector3r dr = (R1 - R0).array() / Real(n);
  876. // linear interpolate function
  877. //Vector3r rx;
  878. //rx.array() = R0.array() + dr.array();
  879. for (int i=1; i<n; i+=2) {
  880. double fx = f0 + (f1 - f0) * ((i*h)/(h*n));
  881. S += 4. * fx * Hat(R0.array() + i*dr.array(), R0, R1);// * Hat(R1.array() + i*dr.array(), R1, R0) ;
  882. }
  883. //rx.array() = R0.array() + 2*dr.array();
  884. for (int i=2; i<n-1; i+=2) {
  885. double fx = f0 + (f1 - f0) * ((i*h)/(h*n));
  886. S += 2. * fx * Hat(R0.array() + i*dr.array(), R0, R1);// * Hat(R1.array() + i*dr.array(), R1, R0);
  887. }
  888. return h * S / 3.;
  889. }
  890. /*
  891. * Performs numerical integration of two functions, one is passed as a vtkImplicitFunction, the other is the FEM
  892. * test function owned by the FEM implimentaion.
  893. */
  894. Real FEM4EllipticPDE::CompositeSimpsons3( const Real& f0, const Real& f1, Real r0[3], Real r1[3], int n) {
  895. Vector3r R0(r0[0], r0[1], r0[2]);
  896. Vector3r R1(r1[0], r1[1], r1[2]);
  897. // force n to be even
  898. assert(n > 0);
  899. //n += (n % 2);
  900. Real h = dist(r0, r1) / (Real)(n) ;
  901. // For Gelerkin (most) FEM, we can omit this one as test functions are zero valued at element boundaries
  902. // NO! We are looking at 1/2 hat often!!! So S = f1!
  903. Real S = f0+f1; //Hat(R0, R0, R1) + f1*Hat(R1, R0, R1);
  904. Vector3r dr = (R1 - R0).array() / Real(n);
  905. // linear interpolate function
  906. //Vector3r rx;
  907. //rx.array() = R0.array() + dr.array();
  908. for (int i=1; i<n; i+=2) {
  909. double fx = 1;// f0 + (f1 - f0) * ((i*h)/(h*n));
  910. S += 4. * fx * Hat(R0.array() + i*dr.array(), R0, R1) * Hat(R1.array() + i*dr.array(), R1, R0) ;
  911. }
  912. //rx.array() = R0.array() + 2*dr.array();
  913. for (int i=2; i<n-1; i+=2) {
  914. double fx = 1; // f0 + (f1 - f0) * ((i*h)/(h*n));
  915. S += 2. * fx * Hat(R0.array() + i*dr.array(), R0, R1)* Hat(R1.array() + i*dr.array(), R1, R0);
  916. }
  917. return h * S / 3.;
  918. }
  919. //--------------------------------------------------------------------------------------
  920. // Class: FEM4EllipticPDE
  921. // Method: Hat
  922. //--------------------------------------------------------------------------------------
  923. Real FEM4EllipticPDE::Hat ( const Vector3r& r, const Vector3r& r0, const Vector3r& r1 ) {
  924. //return (r-r0).norm() / (r1-r0).norm() ;
  925. return dist(r, r0) / dist(r1, r0) ;
  926. } // ----- end of method FEM4EllipticPDE::Hat -----
  927. } // ----- end of Lemma name -----