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 48KB

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