Browse Source

Beginning git project for FEM code. Still need to add examples, documentation, and testing.

iss2
Trevor Irons 8 years ago
commit
a493f49c8d
4 changed files with 1159 additions and 0 deletions
  1. 7
    0
      CMakeLists.txt
  2. 269
    0
      include/FEM4EllipticPDE.h
  3. 5
    0
      src/CMakeLists.txt
  4. 878
    0
      src/FEM4EllipticPDE.cpp

+ 7
- 0
CMakeLists.txt View File

1
+include_directories(${CMAKE_INSTALL_PREFIX}/include)
2
+
3
+include_directories( "${CMAKE_CURRENT_SOURCE_DIR}/include" )
4
+add_subdirectory("src")
5
+
6
+add_library( fem4ellipticpde ${FEMSOURCE} )  
7
+target_link_libraries(fem4ellipticpde "lemmacore")

+ 269
- 0
include/FEM4EllipticPDE.h View File

1
+// ===========================================================================
2
+//
3
+//       Filename:  FEM4EllipticPDE.h
4
+//
5
+//        Created:  08/16/12 18:19:41
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
+/**
31
+  @file
32
+  @author   Trevor Irons
33
+  @date     08/16/12
34
+  @version   $Rev$
35
+ **/
36
+
37
+#ifndef  FEM4ELLIPTICPDE_INC
38
+#define  FEM4ELLIPTICPDE_INC
39
+
40
+#include "LemmaObject.h"
41
+#include "vtkImplicitFunction.h"
42
+#include "vtkDataSet.h"
43
+#include "vtkXMLDataSetWriter.h"
44
+#include "vtkSmartPointer.h"
45
+#include "vtkIdList.h"
46
+#include "vtkCell.h"
47
+#include "vtkDoubleArray.h"
48
+#include "vtkPointData.h"
49
+#include "vtkDataSetSurfaceFilter.h"
50
+#include "vtkCellArray.h"
51
+#include "vtkCellData.h"
52
+
53
+#include <Eigen/IterativeLinearSolvers>
54
+#include "DCSurvey.h"
55
+
56
+namespace Lemma {
57
+
58
+    /** @defgroup FEM4EllipticPDE
59
+     @brief Finite element solver for elliptic PDE's
60
+     @details Uses Galerkin method.
61
+    */
62
+
63
+    /** \addtogroup FEM4EllipticPDE
64
+     @{
65
+     */
66
+
67
+    // ===================================================================
68
+    //  Class:  FEM4EllipticPDE
69
+    /**
70
+      @class FEM4EllipticPDE
71
+      @brief Galerkin FEM solver for Elliptic PDE problems
72
+      @details Solves problems of the type
73
+        \f[ - \nabla  \cdot ( \sigma(\mathbf{r}) \nabla u(\mathbf{r}) ) = g(\mathbf{r})
74
+        \f]
75
+     */
76
+    class FEM4EllipticPDE : public LemmaObject {
77
+
78
+        friend std::ostream &operator<<(std::ostream &stream,
79
+			const FEM4EllipticPDE  &ob);
80
+
81
+        public:
82
+
83
+            // ====================  LIFECYCLE     =======================
84
+
85
+            /** Returns a pointer to a new object of type FEM4EllipticPDE.
86
+             * It allocates all necessary memory.
87
+             */
88
+            static FEM4EllipticPDE* New();
89
+
90
+            /** Deletes this object. Delete also disconnects any
91
+             * attachments to this object.
92
+             */
93
+            void Delete();
94
+
95
+            // ====================  OPERATORS     =======================
96
+
97
+            // ====================  OPERATIONS    =======================
98
+
99
+            /** Sets the vtkImplictFunction that is used as sigma in
100
+            \f$ - \nabla  \cdot ( \sigma(\mathbf{r}) \nabla u(\mathbf{r}) ) = g(\mathbf{r})
101
+            \f$. If this is not set, sigma evaluates as 1.
102
+             */
103
+            void SetSigmaFunction(vtkImplicitFunction* sigma);
104
+
105
+            /** Sets the step to be used in applying boundary conditions.
106
+             */
107
+            void SetBoundaryStep(const Real& h);
108
+
109
+            /** Sets the vtkImplictFunction that is used
110
+             */
111
+            void SetGFunction(vtkImplicitFunction* G);
112
+
113
+            /** Uses a function pointer instead of a vtkImplicitFunction. For functions that can
114
+             *  be written in closed form, this can be much faster and more accurate then interpolating
115
+             *  off a grid.
116
+             */
117
+            void SetGFunction( Real (*pFcn3)(const Real&, const Real&, const Real&) );
118
+
119
+            /** Sets the vtkDataSet that defines the calculation grid.
120
+             */
121
+            void SetGrid(vtkDataSet* Grid);
122
+
123
+            /** Sets up a DC problem with a survey
124
+             *  @param[in] ij is the injection index
125
+             */
126
+            void SetupDC(DCSurvey* Survey, const int& ij);
127
+
128
+            /** Performs solution */
129
+            void Solve(const std::string& fname);
130
+
131
+            /** Performs solution */
132
+            void SolveOLD(const std::string& fname);
133
+
134
+            // ====================  ACCESS        =======================
135
+
136
+            // ====================  INQUIRY       =======================
137
+
138
+        protected:
139
+
140
+            // ====================  LIFECYCLE     =======================
141
+
142
+            /// Default protected constructor.
143
+            FEM4EllipticPDE (const std::string& cname);
144
+
145
+            /** Default protected constructor. */
146
+            ~FEM4EllipticPDE ();
147
+
148
+            /**
149
+             * @copybrief LemmaObject::Release()
150
+             * @copydetails LemmaObject::Release()
151
+             */
152
+            void Release();
153
+
154
+            // ====================  OPERATIONS    =======================
155
+
156
+            /** Used internally to construct stiffness matrix, A
157
+             */
158
+            void ConstructAMatrix();
159
+
160
+            /* Used internally to construct the load vector, g
161
+             */
162
+            //void ConstructLoadVector();
163
+
164
+            // This function is copyright (C) 2012 Joseph Capriotti
165
+            /**
166
+                Returns a vtkIdList of points in member vtkGrid connected to point ido.
167
+                @param[in] ido is the point of interest
168
+                @param[in] grid is a pointer to a vtkDataSet
169
+            */
170
+            vtkSmartPointer<vtkIdList> GetConnectedPoints(const int& id0);
171
+
172
+            /** Uses Simpson's rule to numerically integrate
173
+             * (Shamelessly adapted from http://en.wikipedia.org/wiki/Simpson's_rule
174
+             */
175
+            Real CompositeSimpsons(vtkImplicitFunction* f, Real r0[3], Real r1[3], int numInt);
176
+
177
+            /** Uses Simpson's rule to numerically integrate
178
+             * (Shamelessly adapted from http://en.wikipedia.org/wiki/Simpson's_rule
179
+             *  this method is for a static f
180
+             */
181
+            Real CompositeSimpsons(const Real& f, Real r0[3], Real r1[3], int numInt);
182
+
183
+            /**
184
+             *  Uses Simpson's rule to numerically integrate two functions in 1 dimension over the points
185
+             *   r0 and r1
186
+             */
187
+            Real CompositeSimpsons2(vtkImplicitFunction* f, Real r0[3], Real r1[3], int numInt);
188
+
189
+            /**
190
+             *  Uses Simpson's rule to numerically integrate two functions in 1 dimension over the points
191
+             *   r0 and r1, uses the Hat function and the function pointer fPtr
192
+             */
193
+            Real CompositeSimpsons2(  Real (*fPtr)(const Real&, const Real&, const Real&), Real r0[3], Real r1[3], int numInt);
194
+
195
+            /**
196
+             *  Uses Simpson's rule to numerically integrate two functions in 1 dimension over the points
197
+             *   r0 and r1, uses the Hat function. Assumes a constand function value f
198
+             */
199
+            Real CompositeSimpsons2( const  Real& f, Real r0[3], Real r1[3], int numInt);
200
+
201
+            /**
202
+             *  Uses Simpson's rule to numerically integrate two functions in 1 dimension over the points
203
+             *   r0 and r1, uses the Hat function. Assumes a linear function from f0 to f1.
204
+             */
205
+            Real CompositeSimpsons2( const  Real& f0, const Real& f1, Real r0[3], Real r1[3], int numInt);
206
+
207
+            /**
208
+             *  Uses Simpson's rule to numerically integrate three functions in 1 dimension over the points
209
+             *   r0 and r1, uses two Hat functions. Assumes a linear function from f0 to f1.
210
+             */
211
+            Real CompositeSimpsons3( const  Real& f0, const Real& f1, Real r0[3], Real r1[3], int numInt);
212
+
213
+            /** Hat function for use in Galerkin FEM method, actually a 1/2 Hat.
214
+             */
215
+            //Real hat(Real r[3], Real ri[3], Real rm1[3] );
216
+            Real Hat(const Vector3r &r, const Vector3r& ri, const Vector3r& rm1 );
217
+
218
+            /** Computes distance between r0 and r1
219
+             */
220
+            Real dist(Real r0[3], Real r1[3]);
221
+
222
+            /** Computes distance between r0 and r1
223
+             */
224
+            Real dist(const Vector3r& r0, const Vector3r& r1);
225
+
226
+            // ====================  DATA MEMBERS  =========================
227
+
228
+            /** The effective step at the boundary condition. Defaults to 1
229
+             */
230
+            Real BndryH;
231
+
232
+            /** The sigma value at the boundary condition. Defaults to 1
233
+             */
234
+            Real BndrySigma;
235
+
236
+            /** Implicit function defining the \f$\sigma\f$ term in
237
+                \f$ - \nabla  \cdot ( \sigma(\mathbf{r}) \nabla u(\mathbf{r}) ) = g(\mathbf{r})
238
+                \f$.
239
+                If set to 1, the Poisson equation is solved. Any type of vtkImplicitFunction may
240
+                be used, including vtkImplicitDataSet.
241
+            */
242
+            vtkSmartPointer<vtkImplicitFunction>    vtkSigma;
243
+
244
+            /** Implicit function defining the \f$g\f$ term in
245
+                \f$ - \nabla  \cdot ( \sigma(\mathbf{r}) \nabla u(\mathbf{r}) ) = g(\mathbf{r})
246
+                \f$.
247
+                Typically this is used to define the source term.
248
+            */
249
+            vtkSmartPointer<vtkImplicitFunction>    vtkG;
250
+
251
+            /** Any type of vtkDataSet may be used as a calculation Grid */
252
+            vtkSmartPointer<vtkDataSet>             vtkGrid;
253
+
254
+            /** Function pointer to function describing g */
255
+            Real (*gFcn3)(const Real&, const Real&, const Real&);
256
+
257
+            Eigen::SparseMatrix<Real> A;
258
+
259
+            VectorXr                  g;
260
+
261
+        private:
262
+
263
+    }; // -----  end of class  FEM4EllipticPDE  -----
264
+
265
+    /*! @} */
266
+
267
+}		// -----  end of Lemma  name  -----
268
+
269
+#endif   // ----- #ifndef FEM4ELLIPTICPDE_INC  -----

+ 5
- 0
src/CMakeLists.txt View File

1
+set (FEMSOURCE
2
+	${FEMSOURCE}
3
+	${CMAKE_CURRENT_SOURCE_DIR}/FEM4EllipticPDE.cpp
4
+	PARENT_SCOPE
5
+)

+ 878
- 0
src/FEM4EllipticPDE.cpp View File

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

Loading…
Cancel
Save