├── Future_Improvements.md
├── Home.md
├── Installation_Instruction.md
├── Intro_To_FEM.md
├── LICENSE.md
├── README.md
├── _Sidebar.md
├── images
├── Bumpy_Torus_Mesh_Surface.jpg
├── Bumpy_Torus_Mesh_Surface_With_Lighting.jpg
├── Curved_Domain_Conv_Rate.jpg
├── Curved_Domain_Diagram.jpg
├── Curved_Domain_Forms.jpg
├── Curved_Domain_Param.jpg
├── Demo_Stokes_2D_weak_formulation.jpg
├── FELICITY_Logo_Pic.jpg
├── Laplace_Beltrami_Open_Surface_Bilinear.jpg
├── Laplace_Beltrami_Open_Surface_Conv_Rate.jpg
├── Laplace_Beltrami_Open_Surface_Diagram.jpg
├── Laplace_Beltrami_Open_Surface_Discrete_Form.jpg
├── Laplace_Beltrami_Open_Surface_Errors.jpg
├── Laplace_Beltrami_Open_Surface_FE_Spaces.jpg
├── Laplace_Beltrami_Open_Surface_Param.jpg
├── Laplace_Beltrami_Open_Surface_Soln_lambda_h.jpg
├── Laplace_Beltrami_Open_Surface_Soln_u_h.jpg
├── Laplace_Beltrami_Open_Surface_Strong_Form.jpg
├── Laplace_Beltrami_Open_Surface_Weak_Form.jpg
├── Laplace_On_Cube_3D_weak_formulation.jpg
├── Laplace_Var_Form.jpg
├── Mesh_Gen_With_PDE_Solve_Domain_Fig.jpg
├── Mesh_Gen_With_PDE_Solve_Solution_Fig.jpg
├── Mesh_Gen_With_PDE_Solve_weak_formulation.jpg
├── Simple_Elasticity_3D_weak_formulation.jpg
├── Star_Mesh_Polygon.jpg
├── Tutorial_Interpolation_Ex_P2.jpg
├── Tutorial_Managing_DoFs_Ex_P1_On_1D_Subdomain.jpg
├── Tutorial_Managing_DoFs_Ex_P2.jpg
└── blank.gitkeep
└── tutorials
├── Allocate_DoFs_1.md
├── Computing_Closest_Points_To_Surface_Mesh_1.md
├── FE_Space_on_Higher_Order_Mesh_1.md
├── Laplace_Beltrami_Open_Surface_1.md
├── Laplace_On_Cube_3D_1.md
├── Managing_DoFs_1.md
├── Managing_DoFs_2.md
├── Mesh_Generation_With_Solving_PDE_1.md
├── Mesh_Generation_with_TIGER_1.md
├── Mesh_Generation_with_TIGER_2.md
├── Mesh_Smoothing_1.md
├── Quadtree_Example_1.md
├── Solve_Laplaces_Eqn_1.md
├── Solve_Simple_Elasticity_3D_1.md
├── Solve_Stokes_2D_1.md
├── Tutorial_Interpolation_1.md
├── Tutorial_Meshes_1.md
└── Tutorials_TOC.md
/Future_Improvements.md:
--------------------------------------------------------------------------------
1 | Future Additions and Improvements to FELICITY
2 | =============================================
3 |
4 | I plan to add the following items to FELICITY (in no particular order).
5 |
6 | * Adaptive h-refinement for 3-D tetrahedral meshes. Mesh coarsening would also be desirable.
7 | * Add more finite elements.
8 | * Add functionality for Discontinuous Galerkin (DG) methods.
9 | * Represent _mixed_ finite element spaces.
10 | * Make a better mesh C++ class.
11 | * Translate FELICITY to python.
12 | * Add access to external (3rd party) solvers.
13 | * Implement third derivative operators for basis functions and coefficient functions.
--------------------------------------------------------------------------------
/Home.md:
--------------------------------------------------------------------------------
1 | Welcome to the FELICITY wiki!
2 | =============================
3 |
4 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/FELICITY_Logo_Pic.jpg|alt=Welcome to the FELICITY wiki!]]
5 |
6 | FELICITY: Finite ELement Implementation and Computational Interface Tool for You
7 | --------------------------------------------------------------------------------
8 |
9 | See the MATLAB central file exchange to download the toolbox (.zip file): FELICITY Download
10 |
11 | Users may post questions/comments to the Discussion Forum.
12 |
13 | # Introduction
14 |
15 | This is a MATLAB/C++ code for solving PDEs that are discretized by a finite element method on unstructured simplex grids. It uses a Domain-Specific-Language (DSL) to help streamline implementation of FE discretizations (e.g. matrix assembly) by automatic code generation. The resultant sparse matrices can be manipulated by MATLAB for ease in solving a PDE on a triangular (or tetrahedral) mesh.
16 |
17 | # Features
18 |
19 | * PDEs can be defined on 1-D, 2-D, and 3-D domains. Moreover, the domains can be curves or surfaces embedded in 3-D. For example, you can solve the Laplace-Beltrami equation on a 2-D surface in 3-D.
20 | * Can have multiple interacting sub-domains in a single problem. For example, can have a 1-D curve sub-domain embedded in a 3-D bulk mesh. Can also have a 2-D surface sub-domain embedded in a 3-D bulk-mesh.
21 | * Can define bilinear and linear forms with contributions from multiple embedded sub-domains of different dimensions (i.e. co-dimension >= 0).
22 | * Can do higher order geometry (e.g. quadratic triangle mappings).
23 | * New elements are easily added using a simple ``flat'' m-file.
24 | * Automatically generate custom matrix assembly codes that are callable from MATLAB.
25 | * Automatically generate DoF (Degree-of-Freedom) numbering/allocation for any element implemented in FELICITY.
26 | * Generic mesh classes that implement some useful mesh routines, such as 1-D and 2-D adaptive mesh refinement.
27 | * Some mesh generation utilities (see below).
28 | * MATLAB classes for managing FEM spaces.
29 | * H(div) and H(curl) elements are implemented.
30 | * Support for finite element interpolation in 1-D, 2-D, and 3-D.
31 | * Efficient C++ implementations of bitree, quadtree, and octree; useful for nearest neighbor searching.
32 | * Efficient closest point searching of simplex meshes. This includes finding closest points on surface meshes in 3-D.
33 |
34 | Please see the manual (PDF) in the .zip file for more information.
35 |
36 | # Citing FELCITY
37 |
38 | If you use this toolbox in your work, then you must acknowledge it. Please cite the paper on FELICITY (bibtex entry given):
39 | ```
40 | @Article{Walker_SJSC2018,
41 | author = {Shawn W. Walker},
42 | title = {{FELICITY}: A Matlab/C++ Toolbox For Developing Finite Element Methods And Simulation Modeling},
43 | journal = {SIAM Journal on Scientific Computing (accepted)},
44 | year = {2018},
45 | }
46 | ```
47 |
48 | # Hey, I Just Want The Mesh Generator
49 |
50 | Then download the FELICITY package and only keep this sub-directory:
51 | ```
52 | ./FELICITY/Static_Codes/Isosurface_Meshing
53 | ```
54 | Next, run `compile_mex_2D_mesh_tiger_code`, `compile_mex_3D_mesh_tiger_code` to compile the C++ code.
55 |
56 | Then look at the tutorial: [Mesh Generation With TIGER: Part 1](../wiki/Mesh_Generation_with_TIGER_1).
57 |
58 | # Citing TIGER Mesh Generator
59 |
60 | If you use the mesh generator in your work, then you must acknowledge it. Please cite the paper on TIGER (bibtex entry given):
61 | ```
62 | @Article{Walker_SISC2013,
63 | author = {Shawn W. Walker},
64 | title = {Tetrahedralization of Isosurfaces with Guaranteed-Quality by Edge
65 | Rearrangement ({TIGER})},
66 | journal = {SIAM Journal on Scientific Computing},
67 | year = {2013},
68 | volume = {35},
69 | pages = {A294-A326},
70 | number = {1},
71 | doi = {10.1137/120866075},
72 | eprint = {http://epubs.siam.org/doi/pdf/10.1137/120866075}
73 | }
74 | ```
--------------------------------------------------------------------------------
/Installation_Instruction.md:
--------------------------------------------------------------------------------
1 | How to Install FELICITY
2 | =======================
3 |
4 | Installing FELICITY mostly involves unzipping it to a directory, configuring the MATLAB path, and running some tests to verify that it works on your system. Note: you can download it [here](http://www.mathworks.com/matlabcentral/fileexchange/31141-felicity).
5 |
6 | # Details
7 |
8 | Perform the following steps.
9 |
10 | * Install a C++ compiler, e.g. MS Visual Studio, or gcc, etc... See the web site
11 |
12 | http://www.mathworks.com/support/compilers/R2012a/win64.html
13 |
14 | for more info on installing a compatible C++ compiler with MATLAB.
15 |
16 | * Configure the C++ compiler with the command: `mex -setup` at the MATLAB prompt.
17 | * Unzip the file `FELICITY.zip` to a directory on your computer.
18 | * Open MATLAB and change to the directory you created in the previous step. Execute the m-script `FELICITY_paths` to add FELICITY to your MATLAB path.
19 | * Execute the script `test_FELICITY` to run a series of unit tests to verify that FELICITY works with your system.
20 | * Execute the script `FELICITY_user_help` to see a listing of FELICITY classes and m-files that are relevant to the user.
21 |
22 | # Other Requirements
23 |
24 | FELICITY *requires* the MATLAB Symbolic Computing toolbox.
25 |
26 | # Troubleshooting
27 |
28 | A common problem with using FELICITY is not having the correct C++ compiler linked to MATLAB. If the compiler is not setup correctly, then FELICITY will not work. But this is *not* an issue with FELICITY; this is a problem with MATLAB and your compiler.
29 |
30 | ## Problem On LINUX
31 |
32 | For example, this web page describes a common problem people have when running on LINUX:
33 |
34 | http://www.mathworks.com/matlabcentral/newsreader/view_thread/162466
35 |
36 | If you google this compiler problem, you will find more info.
37 |
38 | ## Problem On Windows
39 |
40 | Compiler problems can also happen with Windows. For example, several user comments here:
41 |
42 | http://www.mathworks.com/matlabcentral/fileexchange/31141-felicity/
43 |
44 | describe some issues with installing MS Visual C++ and interfacing with MATLAB. But again, this is not related to FELICITY. This is a MATLAB/compiler issue.
45 |
46 | ## Problem on Mac
47 |
48 | Some Mac users have experienced some issues with running FELICITY. Unfortunately, I don't have a Mac, so I cannot trouble-shoot this. If anyone can offer advice, please do.
49 |
50 | ## How To Fix Compiler Problem
51 |
52 | There are two ways to fix this:
53 |
54 | * Google search for the problem you are having and hopefully find a solution.
55 | * *Complain* to MATLAB support people. If you are a licensed user, they _must_ help you! In my experience, they always get back to me in a reasonable time and their advice is useful.
56 |
57 | # Other Issues With FELICITY
58 |
59 | If you are having problems using the FELICITY toolbox, then either
60 |
61 | * send me an email at: walker@math.lsu.edu; or
62 | * go to the FELICITY forum and post a comment describing the problem: [TODO]
63 |
--------------------------------------------------------------------------------
/Intro_To_FEM.md:
--------------------------------------------------------------------------------
1 | Introduction to the Finite Element Method (FEM)
2 | ===============================================
3 |
4 | # Introduction
5 |
6 | The finite element method in its full glory can be very intimidating. One has to know many things, such as partial differential equation (PDE) theory, numerical analysis, interpolating with basis functions, matrix assembly, solving linear systems, etc...
7 |
8 | So here are some suggestions on ways to familiarize yourself with the Finite Element Method (FEM) and how it is implemented.
9 |
10 | # Resources Available
11 |
12 | ## Laplace's Equation in MATLAB
13 |
14 | I highly recommend starting by reading this:
15 |
16 | J. Alberty, C. Carstensen, S. A. Funken, "Remarks Around 50 Lines Of Matlab: Short Finite Element Implementation," Numerical Algorithms, 1998, 20, 117-137
17 |
18 | It can be found here (along with the MATLAB code):
19 |
20 | http://www.math.hu-berlin.de/~cc/english/software/shortFE.html
21 |
22 | The code solves Laplace's equation in 2-D on unstructured meshes. The paper also tells you how to modify the code to do more complicated things, like solving non-linear problems and implementing Laplace's equation in 3-D.
23 |
24 | This is how I started learning about the finite element method, simultaneous with taking a graduate course on the mathematics of the finite element method. I also recommend taking a course, or at least some formal self-study.
25 |
26 | ## Video Lectures on Scientific Computing
27 |
28 | For more advanced instruction, with particular emphasis on C++, the following video lectures may be useful to you:
29 |
30 | http://www.math.tamu.edu/~bangerth/videos.html
31 |
32 | The videos focus on the package deal.ii, but many of the lessons covered are general and pertain to any kind of scientific computing.
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016, Shawn W. Walker
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5 |
6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8 | * Neither the name of the "Louisiana State University and Agricultural and Mechanical College" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
9 |
10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # felicity-finite-element-toolbox
2 |
3 | The FELICITY Package
4 |
5 | Finite ELement Implementation and Computational Interface Tool for You
6 |
7 | (c) 11/18/2016, Shawn W. Walker
8 |
9 | This code is open source under the BSD license (see the LICENSE.txt file).
10 |
11 |
12 | DESCRIPTION
13 | ========================================================================
14 |
15 | This code implements mesh manipulation and refinement, generic finite element matrix assembly for problems in 1D, 2D, and 3D, interpolation of finite element data, as well as some other tools. See the accompanying FELICITY.pdf and Quickref.pdf files for more info.
16 |
17 | USAGE
18 | ========================================================================
19 |
20 | 0. Download the package from: http://www.mathworks.com/matlabcentral/fileexchange/31141-felicity
21 |
22 | 1. Extract the given .zip file and read the "FELICITY.pdf" file for more info. In particular, see the INSTALLATION instructions in Chapter 1 of the PDF file.
23 |
24 | 2. You need a C++ compiler. I use MS Visual C++, express edition. If you use LINUX, then the gcc compiler should be fine. You can configure MATLAB to use a compatible C++ compiler by typing "mex -setup" at the MATLAB prompt.
25 |
26 | 3. Check the manual for tutorials, as well as GitHub:
27 |
28 | http://github.com/walkersw/felicity-finite-element-toolbox/wiki
29 |
30 | 4. Type "FELICITY_user_help" at the MATLAB prompt for info on useful classes within FELICITY.
31 | Look in the "Demo" directories for examples of how to use FELICITY. Also, check out the quick refernce guide: Quickref.pdf.
32 |
33 | 5. Warning! Make sure that the meshes you use are positively oriented, e.g. make sure edge, triangle, and tetrahedra connectivity lists are positively oriented (i.e. right-hand-rule). If you do not know what this means, then you should not use this toolbox!
34 |
35 |
36 | COMPATIBILITY NOTES
37 | ========================================================================
38 | The tool was developed in its current form with R2016b.
39 |
40 | You need the MATLAB Symbolic Math Toolbox to use FELICITY.
41 | You need a C++ compiler that MATLAB can use with its "mex" command.
42 |
43 |
44 | Tested on these systems:
45 |
46 | -- Windows 10, 64-bit
47 | Fully functional with R2016a, R2016b.
48 |
49 | -- LINUX KDE/Ubuntu, 64-bit
50 | Fully functional with R2014a.
51 |
52 |
53 | BUG REPORTS AND FEEDBACK
54 | ========================================================================
55 | Please report any problems and/or bugs to: walker@math.lsu.edu
56 |
57 |
58 | ACKNOWLEDGEMENTS
59 | ========================================================================
60 |
61 | I would like to acknowledge the following contributions from the MATLAB file exchange.
62 | These files are now included in FELICITY.
63 |
64 |
65 | http://www.mathworks.com/matlabcentral/fileexchange/10391-fast-points-in-polygon-test
66 |
67 | by Darren Engwirda
68 |
69 | http://www.mathworks.com/matlabcentral/fileexchange/37856-inpolyhedron-are-points-inside-a-volume
70 |
71 | by Sven Holcombe
72 |
73 | http://www.mathworks.com/matlabcentral/fileexchange/38964-example-matlab-class-wrapper-for-a-c++-class
74 |
75 | by Oliver Woodford
--------------------------------------------------------------------------------
/_Sidebar.md:
--------------------------------------------------------------------------------
1 | * [Home](../wiki)
2 | * [Future Improvements](../wiki/Future_Improvements)
3 | * [Installation](../wiki/Installation_Instruction)
4 | * [Getting Started](../wiki/Intro_To_FEM)
5 | * [Tutorials](../wiki/Tutorials_TOC)
--------------------------------------------------------------------------------
/images/Bumpy_Torus_Mesh_Surface.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Bumpy_Torus_Mesh_Surface.jpg
--------------------------------------------------------------------------------
/images/Bumpy_Torus_Mesh_Surface_With_Lighting.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Bumpy_Torus_Mesh_Surface_With_Lighting.jpg
--------------------------------------------------------------------------------
/images/Curved_Domain_Conv_Rate.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Curved_Domain_Conv_Rate.jpg
--------------------------------------------------------------------------------
/images/Curved_Domain_Diagram.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Curved_Domain_Diagram.jpg
--------------------------------------------------------------------------------
/images/Curved_Domain_Forms.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Curved_Domain_Forms.jpg
--------------------------------------------------------------------------------
/images/Curved_Domain_Param.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Curved_Domain_Param.jpg
--------------------------------------------------------------------------------
/images/Demo_Stokes_2D_weak_formulation.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Demo_Stokes_2D_weak_formulation.jpg
--------------------------------------------------------------------------------
/images/FELICITY_Logo_Pic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/FELICITY_Logo_Pic.jpg
--------------------------------------------------------------------------------
/images/Laplace_Beltrami_Open_Surface_Bilinear.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Laplace_Beltrami_Open_Surface_Bilinear.jpg
--------------------------------------------------------------------------------
/images/Laplace_Beltrami_Open_Surface_Conv_Rate.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Laplace_Beltrami_Open_Surface_Conv_Rate.jpg
--------------------------------------------------------------------------------
/images/Laplace_Beltrami_Open_Surface_Diagram.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Laplace_Beltrami_Open_Surface_Diagram.jpg
--------------------------------------------------------------------------------
/images/Laplace_Beltrami_Open_Surface_Discrete_Form.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Laplace_Beltrami_Open_Surface_Discrete_Form.jpg
--------------------------------------------------------------------------------
/images/Laplace_Beltrami_Open_Surface_Errors.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Laplace_Beltrami_Open_Surface_Errors.jpg
--------------------------------------------------------------------------------
/images/Laplace_Beltrami_Open_Surface_FE_Spaces.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Laplace_Beltrami_Open_Surface_FE_Spaces.jpg
--------------------------------------------------------------------------------
/images/Laplace_Beltrami_Open_Surface_Param.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Laplace_Beltrami_Open_Surface_Param.jpg
--------------------------------------------------------------------------------
/images/Laplace_Beltrami_Open_Surface_Soln_lambda_h.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Laplace_Beltrami_Open_Surface_Soln_lambda_h.jpg
--------------------------------------------------------------------------------
/images/Laplace_Beltrami_Open_Surface_Soln_u_h.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Laplace_Beltrami_Open_Surface_Soln_u_h.jpg
--------------------------------------------------------------------------------
/images/Laplace_Beltrami_Open_Surface_Strong_Form.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Laplace_Beltrami_Open_Surface_Strong_Form.jpg
--------------------------------------------------------------------------------
/images/Laplace_Beltrami_Open_Surface_Weak_Form.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Laplace_Beltrami_Open_Surface_Weak_Form.jpg
--------------------------------------------------------------------------------
/images/Laplace_On_Cube_3D_weak_formulation.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Laplace_On_Cube_3D_weak_formulation.jpg
--------------------------------------------------------------------------------
/images/Laplace_Var_Form.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Laplace_Var_Form.jpg
--------------------------------------------------------------------------------
/images/Mesh_Gen_With_PDE_Solve_Domain_Fig.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Mesh_Gen_With_PDE_Solve_Domain_Fig.jpg
--------------------------------------------------------------------------------
/images/Mesh_Gen_With_PDE_Solve_Solution_Fig.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Mesh_Gen_With_PDE_Solve_Solution_Fig.jpg
--------------------------------------------------------------------------------
/images/Mesh_Gen_With_PDE_Solve_weak_formulation.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Mesh_Gen_With_PDE_Solve_weak_formulation.jpg
--------------------------------------------------------------------------------
/images/Simple_Elasticity_3D_weak_formulation.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Simple_Elasticity_3D_weak_formulation.jpg
--------------------------------------------------------------------------------
/images/Star_Mesh_Polygon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Star_Mesh_Polygon.jpg
--------------------------------------------------------------------------------
/images/Tutorial_Interpolation_Ex_P2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Tutorial_Interpolation_Ex_P2.jpg
--------------------------------------------------------------------------------
/images/Tutorial_Managing_DoFs_Ex_P1_On_1D_Subdomain.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Tutorial_Managing_DoFs_Ex_P1_On_1D_Subdomain.jpg
--------------------------------------------------------------------------------
/images/Tutorial_Managing_DoFs_Ex_P2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkersw/felicity-finite-element-toolbox/050e1a57545cdee2c57307eade5eb5b38b068e2d/images/Tutorial_Managing_DoFs_Ex_P2.jpg
--------------------------------------------------------------------------------
/images/blank.gitkeep:
--------------------------------------------------------------------------------
1 | blank
2 |
--------------------------------------------------------------------------------
/tutorials/Allocate_DoFs_1.md:
--------------------------------------------------------------------------------
1 | Allocating Degrees-of-Freedom (DoFs)
2 | ====================================
3 |
4 | # Introduction
5 |
6 | In the previous tutorials, the finite element space was very simple: piecewise linear, continuous Lagrange polynomials. In this case, the Degrees-of-Freedom (DoFs) correspond directly to the vertices of the mesh. Hence, you can use the mesh element connectivity data to know which DoFs belong to each element.
7 |
8 | This is not true for higher order elements (e.g. piecewise quadratic) or more exotic elements (e.g. Raviart-Thomas).
9 |
10 | Allocating DoFs is not just a simple matter of distributing a bunch of indices over a set of mesh elements. You must ensure that neighboring elements "share" common DoFs.
11 |
12 | It is certainly possible for you to write a special purpose MATLAB code to allocate DoFs for a particular element. However, FELICITY can automatically generate a code to do this for you!
13 |
14 | In this tutorial, we will generate code (MEX file) to allocate DoFs for continuous Lagrange, piecewise linear and piecewise quadratic finite element spaces on a 2-D triangle mesh. Then we will input a sample mesh to this MEX file to test it.
15 |
16 | # Code Generation
17 |
18 | Type the following at the MATLAB prompt (or put it in a script file):
19 | ```matlab
20 | Elem = lagrange_deg1_dim2(); % piecewise linear
21 | Elem(2) = lagrange_deg2_dim2(); % piecewise quadratic
22 | ```
23 |
24 | This creates some MATLAB structs that contain important info about the finite element spaces.
25 |
26 | Choose a directory on your hard drive to store the MEX file. Make sure it is in your MATLAB path. Define a MATLAB string variable that records that directory name:
27 | ```matlab
28 | Main_Dir = 'C:\Your_Favorite_Directory\';
29 | ```
30 |
31 | Now generate and compile the DoF allocation code by typing the following command at the MATLAB prompt and press "ENTER":
32 | ```matlab
33 | Create_DoF_Allocator(Elem,'mexDoF_Example_2D',Main_Dir);
34 | ```
35 |
36 | Here we named the executable `mexDoF_Example_2D` (see next section).
37 |
38 | # Run It!
39 |
40 | First, load up a 2-D triangle mesh:
41 | ```matlab
42 | [Vtx, Tri] = Standard_Triangle_Mesh_Test_Data();
43 | tr = triangulation(Tri,Vtx); % triangulation is a built-in MATLAB class
44 | ```
45 |
46 | For the purposes of illustration, we use the MATLAB class `triangulation` instead of FELICITY's mesh class.
47 |
48 | Plot the mesh:
49 | ```matlab
50 | figure;
51 | p1 = trimesh(tr.ConnectivityList,tr.Points(:,1),tr.Points(:,2),0*tr.Points(:,2));
52 | view(2);
53 | axis equal;
54 | shading interp;
55 | set(p1,'edgecolor','k'); % make mesh black
56 | title('Sample Mesh for Automatic DoF Allocation Test');
57 | ```
58 |
59 | Now, allocate the DoFs:
60 | ```matlab
61 | [P1_DoFmap, P2_DoFmap] = mexDoF_Example_2D(uint32(tr.ConnectivityList));
62 | ```
63 |
64 | Note: try running `mexDoF_Example_2D` with the wrong number of arguments; it will give you an error message. But it will also tell you what the inputs should be, and what the outputs are.
65 |
66 | You can now display the DoFmaps:
67 | ```matlab
68 | disp('-----------------------');
69 | disp('P1 DoFmap:');
70 | disp(P1_DoFmap);
71 | disp('-----------------------');
72 | disp('P2 DoFmap:');
73 | disp(P2_DoFmap);
74 | disp('-----------------------');
75 | ```
76 |
77 | `P1_DoFmap` is an Mx3 matrix, where M is the number of triangles. For the mesh in this example, M = 16. It has 3 columns because there are only 3 independent basis functions in the piecewise linear polynomial space on each triangle.
78 |
79 | `P2_DoFmap` is an Mx6 matrix. It has 6 columns because there are 6 independent basis functions in the piecewise quadratic polynomial space on each triangle.
80 |
81 | Row k (of both matrices) corresponds to triangle k in the mesh.
82 |
83 | These DoFmaps can now be used in a matrix assembly routine to generate sparse finite element matrices (see the other tutorials).
--------------------------------------------------------------------------------
/tutorials/Computing_Closest_Points_To_Surface_Mesh_1.md:
--------------------------------------------------------------------------------
1 | Find Closest Points on a Surface Mesh
2 | =====================================
3 |
4 | This tutorial on how to find the closest points on a surface triangulation to a given set of arbitrary points in 3-D.
5 |
6 | # Introduction
7 |
8 | Finding the closest point to a manifold is a common task. It is needed for interpolating data on the surface, or for computing distance functions.
9 |
10 | FELICITY provides code generation to do this. In fact, the underlying mesh geometry may be higher order (e.g. quadratic curved triangles). The generated code is specific to the type of mesh geometry.
11 |
12 | # Generating The Code
13 |
14 | ## Input File
15 |
16 | First, generate code to search surface meshes in 3-D. In the MATLAB editor, create the following m-function and name it `Point_Search_Surface.m`:
17 |
18 | ```matlab
19 | function DOM = Point_Search_Surface()
20 |
21 | % define domain (surface embedded in 3-D)
22 | Surface = Domain('triangle',3);
23 |
24 | % define geometry representation of global domain: Domain, reference element
25 | % Here we assume the default reference element:
26 | % Lagrange piecewise linear continuous,
27 | % in other words, the triangulation is flat (not curved).
28 | G1 = GeoElement(Surface);
29 |
30 | % define a set of domains to point search in
31 | DOM = PointSearches(G1);
32 |
33 | % collect together all of the domains to be searched
34 | DOM = DOM.Append_Domain(Surface);
35 |
36 | end
37 | ```
38 |
39 | Don't worry about what it means just yet (the PDF manual explains more; see Chapter XX (to be added)).
40 |
41 | ## Compile It
42 |
43 | Put the file `Point_Search_Surface.m` into a directory that is *in your MATLAB path*. Now compile it by typing the following command at the MATLAB prompt and press "ENTER":
44 |
45 | ```matlab
46 | Convert_PtSearch_Definition_to_MEX(@Point_Search_Surface,{},'mex_Point_Search');
47 | ```
48 |
49 | Here we named the executable `mex_Point_Search` (see next section).
50 |
51 | # Computing Closest Points To A Sphere
52 |
53 | A sphere may seem too easy of an example (since the closest points can be computed _exactly_ by hand). However, it gives us a way to check the accuracy of the code. And, the tutorial below works *exactly the same* for any other surface mesh.
54 |
55 | ## Initialization
56 |
57 | Either in an m-script or at the MATLAB prompt, create a unit sphere triangle mesh:
58 | ```matlab
59 | Refine_Level = 4;
60 | Center = [0, 0, 0];
61 | Radius = 1;
62 | [TRI, VTX] = triangle_mesh_of_sphere(Center,Radius,Refine_Level);
63 | Mesh = MeshTriangle(TRI, VTX, 'Surface');
64 | ```
65 | The function `triangle_mesh_of_sphere` is a FELICITY routine.
66 |
67 | ## Create Points To Search
68 |
69 | Define points in the global space:
70 | ```matlab
71 | % define a regular grid of points
72 | s_vec = (-1:0.6:1)'; % make sure no point is *exactly* at the origin
73 | [XX,YY,ZZ] = meshgrid(s_vec,s_vec,s_vec);
74 | GX = [XX(:), YY(:), ZZ(:)];
75 | ```
76 |
77 | In order to search for the closest point on the surface triangulation, we need an *initial guess* for the particular triangle (i.e. cell) that contains the closest point.
78 |
79 | The closer the initial guess, the faster the algorithm runs. *Warning:* if the surface is very contorted and non-convex, and you give a bad initial guess, then the closest point computed by the MEX file may not be the true closest point. The search algorithm may get trapped in a "local minimum."
80 |
81 | For this example, we take the initial guess (for every point to be searched) to be triangle (cell) #1:
82 | ```matlab
83 | Cell_Indices = uint32(ones(size(GX,1),1));
84 | ```
85 |
86 | We also need a neighbor data structure for the mesh (to facilitate traversing the mesh). This is done by the following command:
87 | ```matlab
88 | Surface_Neighbors = uint32(Mesh.neighbors);
89 | ```
90 |
91 | Next, put all this together into a cell array:
92 | ```matlab
93 | Given_Points = {Cell_Indices, GX, Surface_Neighbors};
94 | ```
95 | Note: you can also set `Cell_Indices` to an empty matrix. In this case, the code assumes the initial guess to be cell #1 (for every point).
96 |
97 | ## Run The Search
98 |
99 | Now we run the MEX file we generated before:
100 | ```matlab
101 | SEARCH = mex_Point_Search(Mesh.Points,uint32(Mesh.ConnectivityList),[],[],Given_Points);
102 | ```
103 |
104 | The output `SEARCH` is a struct of the form:
105 | ```matlab
106 | SEARCH.DATA = cell array
107 | SEARCH.Name = 'Surface'
108 | ```
109 | where `SEARCH.DATA` is a 1x2 cell array. The contents are:
110 | ```matlab
111 | CI = double(SEARCH.DATA{1});
112 | Local_Ref_Coord = SEARCH.DATA{2};
113 | ```
114 | where `CI` are the cell (triangle) indices (found by the algorithm) that contain the closest points.
115 |
116 | In addition, the coordinates of the closest point *with respect to the enclosing cell* are given in `Local_Ref_Coord`. In other words, these coordinates are relative to the standard reference triangle. This is very useful if you want to *interpolate* finite element functions defined on the surface mesh.
117 |
118 | ## Plot Results
119 |
120 | Of course, we can convert these local coordinates to coordinates in the global space:
121 | ```matlab
122 | % get global coordinates of the "found" closest points on sphere
123 | XC = Mesh.referenceToCartesian(CI,Local_Ref_Coord);
124 | ```
125 |
126 | And we can now plot it:
127 | ```matlab
128 | FH = Mesh.Plot;
129 | set(FH,'facealpha',0.4);
130 | hold on;
131 | plot3(GX(:,1),GX(:,2),GX(:,3),'k*','MarkerSize',10);
132 | plot3(XC(:,1),XC(:,2),XC(:,3),'r.','MarkerSize',20);
133 | plot3([XC(:,1), GX(:,1)]',[XC(:,2), GX(:,2)]',[XC(:,3), GX(:,3)]',...
134 | 'b-','LineWidth',1.6);
135 | hold off;
136 | axis([-1.5 1.5 -1.5 1.5 -1.5 1.5]);
137 | axis equal;
138 | title('Closest Points On Triangulation Of Sphere');
139 | ```
140 |
141 | This tutorial can be found in ".\Demo\Closest_Point_Sphere" sub-directory of FELICITY. Also, see the PDF manual for another tutorial on finding points in planar triangulations.
--------------------------------------------------------------------------------
/tutorials/FE_Space_on_Higher_Order_Mesh_1.md:
--------------------------------------------------------------------------------
1 | A Finite Element Space on a Higher Order Mesh
2 | =============================================
3 |
4 | This tutorial describes how to do higher order meshes (e.g. piecewise quadratic triangles) instead of the standard piecewise linear case. The details can be found in the FELICITY manual, the chapter on "Helper Routines For Finite Element Codes".
5 |
6 | Note: this example can be run using the script `test_Curved_Domain_2D.m` located in
7 | ```
8 | ./FELICITY/Demo/Curved_Domain_2D/
9 | ```
10 |
11 | # Domain Definition
12 |
13 | We will define a piecewise quadratic triangulation of the following parameterized domain:
14 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Curved_Domain_Param.jpg|width=320|alt=Curved Domain Param]]
15 |
16 | A piecewise linear mesh approximation of the above domain is shown below:
17 |
18 |
19 |
20 | # Example Finite Element Forms
21 |
22 | We will define a continuous piecewise quadratic (Lagrange) finite element space on the piecewise quadratic mesh, a so-called iso-parametric FE space. In addition, we will define the following bilinear and linear forms on the FE space:
23 |
24 |
25 |
26 | where the last linear form computes the integral of the Neumann data of `v_h`.
27 |
28 | # Abstract Definition File
29 |
30 | The (abstract) m-file that defines all of this is given here:
31 | ```
32 | function MATS = MatAssem_Curved_Domain_2D()
33 |
34 | % define domain (in the x-y plane)
35 | Omega = Domain('triangle');
36 | Gamma = Domain('interval') < Omega; % subset
37 |
38 | % define finite element spaces
39 | V_h = Element(Omega, lagrange_deg2_dim2); % piecewise quadratic
40 |
41 | % define functions in FE spaces
42 | v_h = Test(V_h);
43 | u_h = Trial(V_h);
44 |
45 | % geometric function
46 | gf = GeoFunc(Gamma);
47 |
48 | % define (discrete) forms
49 | M = Bilinear(V_h,V_h);
50 | M = M + Integral(Omega, v_h.val * u_h.val );
51 |
52 | M_Gamma = Bilinear(V_h,V_h);
53 | M_Gamma = M_Gamma + Integral(Gamma, v_h.val * u_h.val );
54 |
55 | B_neu = Linear(V_h);
56 | B_neu = B_neu + Integral(Gamma, v_h.grad' * gf.N );
57 |
58 | % set the minimum order of accuracy for the quad rule
59 | Quadrature_Order = 10;
60 | % define how \Omega is represented: with piecewise quadratics
61 | G1 = GeoElement(Omega,lagrange_deg2_dim2);
62 | % define a set of matrices
63 | MATS = Matrices(Quadrature_Order,G1);
64 |
65 | % collect all of the matrices together
66 | MATS = MATS.Append_Matrix(B_neu);
67 | MATS = MATS.Append_Matrix(M);
68 | MATS = MATS.Append_Matrix(M_Gamma);
69 |
70 | end
71 | ```
72 | Note that the space `V_h` is piecewise quadratic, and the `GeoElement` is defined to be piecewise quadratic.
73 |
74 | # Implementation
75 |
76 | After compiling the abstract definition file (for example, see [Solve Laplace's Equation](../wiki/Solve_Laplaces_Eqn_1)), we now implement a script for assembling the previously defined forms (FE matrices) and computing some basic consistency checks. This involves doing a convergence check, i.e. we will loop over several meshes of decreasing mesh size.
77 |
78 | Note: this can be found in `Execute_Curved_Domain_2D.m`.
79 |
80 | ## Initialization
81 |
82 | We start with some initialization:
83 | ```
84 | Main_Dir = ;
85 |
86 | Refine_Vec = [0 1 2 3 4 5];
87 | Num_Refine = length(Refine_Vec);
88 |
89 | % init two arrays, one for each mapping method
90 | Neumann_Error_Vec = {zeros(Num_Refine,1); zeros(Num_Refine,1)};
91 | ```
92 |
93 | Now define the domain parameterization, as well as parameterizations of the bottom and top boundary curves of the domain:
94 | ```
95 | % define domain parameterization
96 | Psi = @(u,v) [u, v + u.*(1-u).*(v-0.5)];
97 | Gamma_y_bot = @(x) x.*(1-x).*(-0.5);
98 | Gamma_y_top = @(x) 1 + x.*(1-x).*(1-0.5);
99 | ```
100 |
101 | Next, start the big loop:
102 | ```
103 | % mm==1 % square
104 | % mm==2 % mapped square
105 | for mm = 1:2
106 | for kk = 1:Num_Refine
107 | ```
108 | where we loop over two different methods for defining the piecewise quadratic domain; we also loop over successively finer meshes.
109 |
110 | ## Create The Base (Piecewise Linear) Mesh
111 |
112 | Now create a mesh of the unit triangle `[0, 1] x [0, 1]`:
113 | ```
114 | % BEGIN: define reference mesh
115 | Refine_Level = Refine_Vec(kk);
116 | Np = 2^Refine_Level + 1;
117 | [Tri, Pts_u_v] = bcc_triangle_mesh(Np,Np);
118 | ```
119 | and we either keep the unit square, or we map it (depending on the "method" chosen):
120 | ```
121 | if (mm==1)
122 | Mesh_Pts = Pts_u_v;
123 | else
124 | % apply parameterization
125 | Mesh_Pts = Psi(Pts_u_v(:,1), Pts_u_v(:,2));
126 | end
127 | Mesh = MeshTriangle(Tri, Mesh_Pts, 'Omega');
128 | % END: define reference mesh
129 | ```
130 | This gives us a base piecewise *linear* mesh.
131 |
132 | Add the boundary `Gamma` as a sub-domain:
133 | ```
134 | % add the open boundary
135 | BDY = Mesh.freeBoundary();
136 | Mesh = Mesh.Append_Subdomain('1D','Gamma',BDY);
137 | ```
138 |
139 | ## Define a GeoElementSpace
140 |
141 | Next, we create a special object for handling the higher order mesh geometry:
142 | ```
143 | % create GeoElementSpace
144 | P2_RefElem = ReferenceFiniteElement(lagrange_deg2_dim2());
145 | G_Space = GeoElementSpace('G_h',P2_RefElem,Mesh);
146 | G_DoFmap = DEMO_Curved_Domain_mex_V_Space_DoF_Allocator(uint32(Mesh.ConnectivityList));
147 | G_Space = G_Space.Set_DoFmap(Mesh,uint32(G_DoFmap));
148 | ```
149 | Note: at this point, we have already created a mex file to allocate DoFs for the piecewise quadratic space.
150 |
151 | ## Create the Piecewise Quadratic Mapping
152 |
153 | With this, we get an initial piecewise quadratic FE function that represents the identity map for our mesh:
154 | ```
155 | % BEGIN: define the higher order domain
156 | Geo_Points_hat = G_Space.Get_Mapping_For_Piecewise_Linear_Mesh(Mesh);
157 | % initially starts as a piecewise quadratic polynomial (P2 function) interpolating a
158 | % piecewise linear (P1) function (representing the initial square mesh)
159 | ```
160 | Since the base mesh is piecewise *linear*, this "quadratic" function is actually interpolating a piecewise linear function.
161 |
162 | Now, depending on the "method" we chose, we create a new variable `Geo_Points` that represents the true piecewise quadratic mapping that we need to define our curved domain:
163 | ```
164 | if (mm==1)
165 | % apply parameterization
166 | Geo_Points = Psi(Geo_Points_hat(:,1), Geo_Points_hat(:,2));
167 | else
168 | DoFs_on_Gamma = G_Space.Get_DoFs_On_Subdomain(Mesh,'Gamma');
169 | GP_Gamma = Geo_Points_hat(DoFs_on_Gamma,:);
170 | Bot_Mask = GP_Gamma(:,2) < 0 + 1e-12; % <= zero
171 | Top_Mask = GP_Gamma(:,2) > 1 - 1e-12; % >= 1
172 | GP_Gamma(Bot_Mask,2) = Gamma_y_bot(GP_Gamma(Bot_Mask,1));
173 | GP_Gamma(Top_Mask,2) = Gamma_y_top(GP_Gamma(Top_Mask,1));
174 | Geo_Points = Geo_Points_hat;
175 | Geo_Points(DoFs_on_Gamma,:) = GP_Gamma;
176 | clear DoFs_on_Gamma GP_Gamma Bot_Mask Top_Mask;
177 | end
178 | % END: define the higher order domain
179 | ```
180 | *Method 1* is the simplest, but it requires mapping from the unit square to the curved domain (so `Geo_Points` is not a small perturbation). Furthermore, Method 1 has *all* of the elements being curved.
181 |
182 | *Method 2* has the base mesh being a piecewise linear approximation of the curved domain; thus, the P2 function `Geo_Points` is a small perturbation of the piecewise linear approximation. Indeed, only the triangles with an edge on the bottom or top boundary are curved; all other triangles are actually straight, even though a P2 mapping is used.
183 |
184 | Depending on the situation, one method may be more useful than another.
185 |
186 | ## Define a Finite Element Space
187 |
188 | ```
189 | V_Space = FiniteElementSpace('V_h', P2_RefElem, Mesh, 'Omega');
190 | V_Space = V_Space.Set_DoFmap(Mesh,uint32(G_DoFmap)); % G_Space has the same DoFmap
191 | ```
192 | we use the same DoFmap, because `G_Space` and `V_Space` have the same reference finite element.
193 |
194 | ## Assemble the Matrices
195 |
196 | Because `Omega` and `Gamma` are interacting sub-domains, we must compute the (topological) embedding data for the mesh:
197 | ```
198 | Domain_Names = {'Omega'; 'Gamma'};
199 | Omega_Embed = Mesh.Generate_Subdomain_Embedding_Data(Domain_Names);
200 | ```
201 |
202 | Then (assuming we have mex'ed our form definition file), we assemble the matrices:
203 | ```
204 | FEM = DEMO_mex_MatAssem_Curved_Domain_2D([],Geo_Points,G_Space.DoFmap,[],...
205 | Omega_Embed,V_Space.DoFmap);
206 | ```
207 | Note that we feed the higher order mesh information `Geo_Points`, `G_Space.DoFmap` into the matrix assembler.
208 |
209 | Extracting the matrices is easy with
210 | ```
211 | % put FEM into a nice object to make accessing the matrices easier
212 | my_Mats = FEMatrixAccessor('Curved Domain',FEM);
213 |
214 | % pull out the matrices
215 | M = my_Mats.Get_Matrix('M');
216 | M_Gamma = my_Mats.Get_Matrix('M_Gamma');
217 | B_neu = my_Mats.Get_Matrix('B_neu');
218 | ```
219 |
220 | ## Simple Error Checks
221 |
222 | We numerically compute the area of the domain and the length of its perimeter:
223 | ```
224 | % check domain measures
225 | Area_by_matrix = sum(M(:));
226 | Len_by_matrix = sum(M_Gamma(:));
227 | ```
228 | and we compare to the "true" value computed analytically:
229 | ```
230 | disp('Area by FE matrix: error is (small):');
231 | Area_by_matrix - (7/6)
232 | disp('Length by FE matrix: error is (small):');
233 | Len_by_matrix - 4.080457638869102
234 | ```
235 |
236 | ## Define a Finite Element Function
237 |
238 | Now we interpolate a known function, using the coordinates of the DoFs in our *iso-parametric* FE space. To do this, we need both `G_Space` and `V_Space`. In fact, we need to do interpolation within `G_Space`. Hence, we need to set a directory to store the mex file for the interpolation code:
239 | ```
240 | G_Space = G_Space.Set_mex_Dir(Main_Dir,'mex_Curved_Domain_G_h_Interpolation');
241 | ```
242 | Here, we named the "internal" interpolation mex file of `G_Space`.
243 |
244 | Now we can get the precise coordinates of the DoFs in `V_Space`.
245 | ```
246 | V_Points = V_Space.Get_DoF_Coord(Mesh,G_Space,Geo_Points);
247 | ```
248 | Note: `V_Points` and `Geo_Points` are actually the same because `V_Space` and `G_Space` have the same reference finite element (P2 Lagrange) and the same DoFmap.
249 |
250 | With this, we can interpolate a given smooth function:
251 | ```
252 | f_exact = @(x,y) sin(y);
253 | f_h = f_exact(V_Points(:,1),V_Points(:,2));
254 | ```
255 |
256 | ## Consistency Check
257 |
258 | We use `B_neu` to compute the integrated Neumann data for `f_h`, and we compare to the "exact" analytic value:
259 | ```
260 | % compute Neumann data for known function
261 | Neumann_data_exact = -5.277816286458646e-01;
262 | Neumann_data_by_matrix = f_h' * B_neu;
263 | Neumann_Error = abs(Neumann_data_exact - Neumann_data_by_matrix);
264 | Neumann_Error_Vec{mm}(kk) = Neumann_Error;
265 | ```
266 |
267 | Finally, we close the two `for` loops:
268 | ```
269 | end
270 |
271 | end
272 | ```
273 |
274 | # Plotting
275 |
276 | Plot the mesh of `Omega` and highlight boundary `Gamma`:
277 | ```
278 | FH_mesh = figure('Renderer','painters','PaperPositionMode','auto','Color','w');
279 | Mesh.Plot;
280 | hold on;
281 | Mesh.Plot_Subdomain('Gamma');
282 | hold off;
283 | title('Curved Domain \Omega (Piecewise Linear Approximation)');
284 | %title('Om');
285 | grid on;
286 | axis([-0.2 1.2 -0.2 1.2]);
287 | axis equal;
288 | axis([-0.2 1.2 -0.2 1.2]);
289 | ```
290 |
291 | Now plot the error decay in order to view the convergence rate:
292 | ```
293 | Order_2_Line = 0.1 * 4.^(-Refine_Vec);
294 | FH_error = figure('Renderer','painters','PaperPositionMode','auto','Color','w');
295 | semilogy(Refine_Vec,Neumann_Error_Vec{1},'r-o',...
296 | Refine_Vec,Neumann_Error_Vec{2},'b-o',...
297 | Refine_Vec,Order_2_Line,'m-d');
298 | xlabel('Refinement Level');
299 | ylabel('Error');
300 | title('Numerical Convergence Rate');
301 | axis([0 5 1e-5 1e-1]);
302 | legend({'$| Q - \int_{\Gamma} \nabla v_h \cdot \mathbf{\nu} |$ (method 1)',...
303 | '$| Q - \int_{\Gamma} \nabla v_h \cdot \mathbf{\nu} |$ (method 2)',...
304 | '$O(h^2)$ line'},'Interpreter','latex','FontSize',12,'Location','best');
305 | grid;
306 | ```
307 |
308 | Here is a plot of the convergence rate:
309 |
310 |
311 |
312 | # Conclusion
313 |
314 | One can use higher than degree 2 Lagrange elements for the geometry discretization.
315 |
--------------------------------------------------------------------------------
/tutorials/Laplace_Beltrami_Open_Surface_1.md:
--------------------------------------------------------------------------------
1 | Solving Laplace-Beltrami on a Higher Order Mesh
2 | ===============================================
3 |
4 | This tutorial describes how to solve the Laplace-Beltrami equation on a 3-D surface with open boundary using higher order meshes (e.g. piecewise quadratic triangles) instead of the standard piecewise linear case. This problem is described in the FELICITY paper (coming soon). Also see the chapter on "Helper Routines For Finite Element Codes" in the FELICITY manual for details on higher order meshes.
5 |
6 | Note: this example can be run using the script `test_Laplace_Beltrami_Open_Surface.m` located in
7 | ```
8 | ./FELICITY/Demo/Laplace_Beltrami_Open_Surface/
9 | ```
10 |
11 | # Problem Definition
12 |
13 | The strong formulation of the PDE is:
14 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Laplace_Beltrami_Open_Surface_Strong_Form.jpg|width=320|alt=Strong Formulation]]
15 |
16 | Here is an illustration of the domain:
17 |
18 |
19 | The *weak* formulation of the PDE is:
20 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Laplace_Beltrami_Open_Surface_Weak_Form.jpg|width=320|alt=Weak Formulation]]
21 |
22 | # Domain Definition
23 |
24 | We will define a piecewise quadratic triangulation of the following parameterized domain:
25 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Laplace_Beltrami_Open_Surface_Param.jpg|width=320|alt=Saddle Domain Param]]
26 |
27 | See the figure above.
28 |
29 | # Curved Finite Element Spaces
30 |
31 | We will define a continuous piecewise quadratic (Lagrange) finite element space on the piecewise quadratic mesh, a so-called iso-parametric FE space. In addition, we will define a piecewise linear space on the piecewise quadratic mesh. The exact mathematical formulation is:
32 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Laplace_Beltrami_Open_Surface_FE_Spaces.jpg|width=320|alt=Curved FE Spaces]]
33 |
34 | # Finite Element Formulation
35 |
36 | We wish to solve the following problem:
37 |
38 |
39 | ## Finite Element Forms
40 |
41 | We need the following bilinear forms to construct the linear system representing the finite element formulation above.
42 |
43 |
44 |
45 | ## Abstract Definition File
46 |
47 | The (abstract) m-file that defines the bilinear forms is:
48 | ```
49 | function MATS = MatAssem_Laplace_Beltrami_Open_Surface()
50 |
51 | % define domain (2-D surface in 3-D)
52 | Gamma = Domain('triangle',3);
53 | dGamma = Domain('interval') < Gamma; % subset
54 |
55 | % define finite element spaces
56 | V_h = Element(Gamma, lagrange_deg2_dim2,1); % piecewise quadratic
57 | W_h = Element(dGamma,lagrange_deg1_dim1,1); % piecewise linear
58 |
59 | % define functions in FE spaces
60 | v_h = Test(V_h);
61 | u_h = Trial(V_h);
62 | mu_h = Test(W_h);
63 |
64 | % define (discrete) forms
65 | M = Bilinear(V_h,V_h);
66 | M = M + Integral(Gamma, v_h.val * u_h.val );
67 |
68 | K = Bilinear(V_h,V_h);
69 | K = K + Integral(Gamma, v_h.grad' * u_h.grad );
70 |
71 | B = Bilinear(W_h,V_h);
72 | B = B + Integral(dGamma, mu_h.val * u_h.val );
73 |
74 | % set the minimum order of accuracy for the quad rule
75 | Quadrature_Order = 10;
76 | % define geometry representation - Domain, piecewise quadratic representation
77 | G1 = GeoElement(Gamma,lagrange_deg2_dim2);
78 | % define a set of matrices
79 | MATS = Matrices(Quadrature_Order,G1);
80 |
81 | % collect all of the matrices together
82 | MATS = MATS.Append_Matrix(B);
83 | MATS = MATS.Append_Matrix(K);
84 | MATS = MATS.Append_Matrix(M);
85 |
86 | end
87 | ```
88 | Note that the space `V_h` is piecewise quadratic, and the `GeoElement` is defined to be piecewise quadratic. `W_h` is piecewise linear.
89 |
90 | # Convergence Check
91 |
92 | We will check the accuracy of our finite element method via a convergence check.
93 |
94 | ## Numerical Errors
95 |
96 | We will evaluate the following quantities of interest in order to compute the convergence rate of the method:
97 |
98 |
99 |
100 | ## Abstract Definition File
101 |
102 | The (abstract) m-file that defines the numerical errors is:
103 | ```
104 | function MATS = Compute_Errors_Laplace_Beltrami_Open_Surface()
105 |
106 | % define domain (2-D surface in 3-D)
107 | Gamma = Domain('triangle',3);
108 | dGamma = Domain('interval') < Gamma; % subset
109 |
110 | % define finite element spaces
111 | V_h = Element(Gamma, lagrange_deg2_dim2,1); % piecewise quadratic
112 | W_h = Element(dGamma,lagrange_deg1_dim1,1); % piecewise linear
113 | G_h = Element(Gamma, lagrange_deg2_dim2,3); % vector piecewise quadratic
114 |
115 | % define functions in FE spaces
116 | u_h = Coef(V_h);
117 | lambda_h = Coef(W_h);
118 |
119 | NV_h = Coef(G_h);
120 | TV_h = Coef(G_h);
121 |
122 | % geometry access functions
123 | gf_Gamma = GeoFunc(Gamma);
124 | gf_dGamma = GeoFunc(dGamma);
125 |
126 | % define exact solns
127 | u_exact = @(u,v) cos(v.*pi.*2.0).*sin(u.*pi.*2.0);
128 | lambda_exact = ;
129 |
130 | % define (discrete) forms
131 | u_L2_Error_sq = Real(1,1);
132 | u_L2_Error_sq = u_L2_Error_sq + Integral(Gamma, (u_h.val - u_exact(gf_Gamma.X(1),gf_Gamma.X(2)))^2 );
133 |
134 | lambda_L2_Error_sq = Real(1,1);
135 | lambda_L2_Error_sq = lambda_L2_Error_sq + Integral(dGamma, (lambda_h.val - lambda_exact(gf_dGamma.X(1),gf_dGamma.X(2)))^2 );
136 |
137 | % check normal vector
138 | NV_Error_sq = Real(1,1);
139 | NV_Error_sq = NV_Error_sq + Integral(dGamma, sum((gf_Gamma.N - NV_h.val).^2) );
140 |
141 | % check tangent vector
142 | TV_Error_sq = Real(1,1);
143 | TV_Error_sq = TV_Error_sq + Integral(dGamma, sum((gf_dGamma.T - TV_h.val).^2) );
144 |
145 | % check Neumann data
146 | xi_h = cross(gf_dGamma.T,gf_Gamma.N);
147 | Neumann_L2_Error_sq = Real(1,1);
148 | Neumann_L2_Error_sq = Neumann_L2_Error_sq + Integral(dGamma, ( lambda_h.val - ( -dot(xi_h,u_h.grad) ) )^2 );
149 |
150 | % set the minimum order of accuracy for the quad rule
151 | Quadrature_Order = 10;
152 | % define geometry representation - Domain, piecewise quadratic representation
153 | G1 = GeoElement(Gamma,lagrange_deg2_dim2);
154 | % define a set of matrices
155 | MATS = Matrices(Quadrature_Order,G1);
156 |
157 | % collect all of the matrices together
158 | MATS = MATS.Append_Matrix(u_L2_Error_sq);
159 | MATS = MATS.Append_Matrix(lambda_L2_Error_sq);
160 | MATS = MATS.Append_Matrix(NV_Error_sq);
161 | MATS = MATS.Append_Matrix(TV_Error_sq);
162 | MATS = MATS.Append_Matrix(Neumann_L2_Error_sq);
163 |
164 | end
165 | ```
166 | Note that the space `G_h` corresponds to the representation of the piecewise quadratic triangulation of the domain.
167 |
168 | # Implementation
169 |
170 | After compiling the abstract definition files (for example, see [Solve Laplace's Equation](../wiki/Solve_Laplaces_Eqn_1)), we now implement a script for assembling the previously defined forms (FE matrices) and quantities of interest. This involves doing a convergence check, which means we will loop over several meshes of decreasing mesh size.
171 |
172 | Note: this can be found in `Execute_Laplace_Beltrami_Open_Surface.m`.
173 |
174 | ## Initialization
175 |
176 | We start with some initialization:
177 | ```
178 | Main_Dir = ;
179 |
180 | Refine_Vec = [2 3 4 5 6 7];
181 | Num_Refine = length(Refine_Vec);
182 |
183 | % init arrays to store numerical errors
184 | u_Error_Vec = zeros(Num_Refine,1);
185 | lambda_Error_Vec = zeros(Num_Refine,1);
186 | NV_Error_Vec = zeros(Num_Refine,1);
187 | TV_Error_Vec = zeros(Num_Refine,1);
188 | Neumann_Error_Vec = zeros(Num_Refine,1);
189 | ```
190 |
191 | Now define the domain parameterization:
192 | ```
193 | % define domain parameterization
194 | Psi = @(q1,q2) [q1, q2, 0.5*(q1.^2 - q2.^2)];
195 | ```
196 |
197 | Next, start the for loop:
198 | ```
199 | for kk = 1:Num_Refine
200 | ```
201 |
202 | ## Create The Base (Piecewise Linear) Mesh
203 |
204 | Now create a mesh of the unit disk and map it with the parameterization:
205 | ```
206 | % BEGIN: define saddle surface (piecewise linear mesh)
207 | Refine_Level = Refine_Vec(kk);
208 | % create mesh of unit disk centered at origin
209 | [Tri, Pts_q] = triangle_mesh_of_disk([0 0 0],1,Refine_Level);
210 |
211 | % apply parameterization
212 | Pts_x_y_z = Psi(Pts_q(:,1), Pts_q(:,2));
213 | Mesh = MeshTriangle(Tri, Pts_x_y_z, 'Gamma');
214 | % END: define saddle surface (piecewise linear mesh)
215 | ```
216 | This gives us a base piecewise *linear* mesh.
217 |
218 | Add the boundary `dGamma` as a sub-domain:
219 | ```
220 | % add the open boundary
221 | BDY = Mesh.freeBoundary();
222 | Mesh = Mesh.Append_Subdomain('1D','dGamma',BDY);
223 | ```
224 |
225 | ## Define a GeoElementSpace
226 |
227 | Next, we create a special object for handling the higher order mesh geometry:
228 | ```
229 | % create GeoElementSpace
230 | P2_RefElem = ReferenceFiniteElement(lagrange_deg2_dim2());
231 | G_Space = GeoElementSpace('G_h',P2_RefElem,Mesh);
232 | G_DoFmap = DEMO_LB_mex_V_Space_DoF_Allocator(uint32(Mesh.ConnectivityList));
233 | G_Space = G_Space.Set_DoFmap(Mesh,uint32(G_DoFmap));
234 | ```
235 | Note: at this point, we have already created a mex file to allocate DoFs for the piecewise quadratic space.
236 |
237 | ## Create the Piecewise Quadratic Mapping
238 |
239 | With this, we get an initial piecewise quadratic FE function that represents the identity map for our mesh:
240 | ```
241 | % BEGIN: define the higher order surface
242 | Geo_Points_hat = G_Space.Get_Mapping_For_Piecewise_Linear_Mesh(Mesh);
243 | % initially starts as a piecewise quadratic polynomial (P2 function) interpolating a
244 | % piecewise linear (P1) function (representing the PW linear saddle surface mesh)
245 | ```
246 | Since the base mesh is piecewise *linear*, this "quadratic" function is actually interpolating a piecewise linear function.
247 |
248 | Now, we create a new variable `Geo_Points` that represents the true piecewise quadratic mapping that we need to define our curved domain:
249 | ```
250 | % map back to (q_1,q_2) plane
251 | Geo_q = Geo_Points_hat(:,1:2);
252 | % Geo_q is a piecewise linear approximation of the unit disk
253 | dGamma_DoF_Indices = G_Space.Get_DoFs_On_Subdomain(Mesh,'dGamma');
254 | Geo_q_on_bdy = Geo_q(dGamma_DoF_Indices,:);
255 | % ensure the (q_1,q_2) points on the bdy of the mesh lie *exactly* on the
256 | % boundary of the unit disk: {q_1^2 + q_2^2 < 1}
257 | Norm_on_bdy = sqrt(sum(Geo_q_on_bdy.^2,2));
258 | Geo_q_on_bdy(:,1) = Geo_q_on_bdy(:,1) ./ Norm_on_bdy; % normalize
259 | Geo_q_on_bdy(:,2) = Geo_q_on_bdy(:,2) ./ Norm_on_bdy; % normalize
260 | % update boundary points in global array (all other nodal values are unaffected)
261 | Geo_q(dGamma_DoF_Indices,:) = Geo_q_on_bdy;
262 | ```
263 | Thus, `Geo_q` is now a piecewise quadratic approximation of the unit disk. Next,
264 | ```
265 | % apply parameterization
266 | Geo_Points = Psi(Geo_q(:,1), Geo_q(:,2));
267 | % END: define the higher order surface
268 | ```
269 |
270 | ## Define Finite Element Spaces
271 |
272 | ```
273 | % define FE spaces
274 | V_Space = FiniteElementSpace('V_h', P2_RefElem, Mesh, 'Gamma');
275 | V_Space = V_Space.Set_DoFmap(Mesh,uint32(G_DoFmap)); % G_Space has the same DoFmap
276 |
277 | P1_RefElem = ReferenceFiniteElement(lagrange_deg1_dim1());
278 | W_Space = FiniteElementSpace('W_h', P1_RefElem, Mesh, 'dGamma');
279 | W_DoFmap = DEMO_LB_mex_W_Space_DoF_Allocator(uint32(Mesh.Get_Global_Subdomain('dGamma')));
280 | W_Space = W_Space.Set_DoFmap(Mesh,uint32(W_DoFmap));
281 | ```
282 | we use the same DoFmap for `V_Space`, because `G_Space` and `V_Space` have the same reference finite element. Note that we have a separate DoF allocator for `W_Space`.
283 |
284 | ## Assemble the Matrices
285 |
286 | Because `Gamma` and `dGamma` are interacting sub-domains, we must compute the (topological) embedding data for the mesh:
287 | ```
288 | Domain_Names = {'Gamma'; 'dGamma'};
289 | Gamma_Embed = Mesh.Generate_Subdomain_Embedding_Data(Domain_Names);
290 | ```
291 |
292 | Then (assuming we have mex'ed our bilinear form definition file), we assemble the matrices:
293 | ```
294 | FEM = DEMO_mex_MatAssem_Laplace_Beltrami_Open_Surface([],Geo_Points,G_Space.DoFmap,[],...
295 | Gamma_Embed,V_Space.DoFmap,W_Space.DoFmap);
296 | ```
297 | Note that we feed the higher order mesh information `Geo_Points`, `G_Space.DoFmap` into the matrix assembler.
298 |
299 | Extracting the matrices is easy with
300 | ```
301 | % put FEM into a nice object to make accessing the matrices easier
302 | LB_Mats = FEMatrixAccessor('Laplace-Beltrami',FEM);
303 |
304 | % pull out the matrices
305 | M = LB_Mats.Get_Matrix('M');
306 | K = LB_Mats.Get_Matrix('K');
307 | B = LB_Mats.Get_Matrix('B');
308 | ```
309 |
310 | ## Simple Error Checks
311 |
312 | We numerically compute the length of `dGamma` using the `B` matrix:
313 | ```
314 | dGamma_Subdomain = Mesh.Output_Subdomain_Mesh('dGamma');
315 | disp('Check length of boundary curve:');
316 | Len_mesh = sum(dGamma_Subdomain.Volume());
317 | Len_by_matrix = sum(B(:));
318 | ```
319 | and we compare to the "true" value computed analytically:
320 | ```
321 | disp('Length by mesh: error is (small):');
322 | Len_mesh - 7.640395578055425e+00
323 | disp('Length by B matrix: error is (small):');
324 | Len_by_matrix - 7.640395578055425e+00
325 | ```
326 |
327 | ## Define Finite Element Functions
328 |
329 | Now we interpolate a known exact solution, and the corresponding right-hand-side data, using the coordinates of the DoFs in our *iso-parametric* FE space. To do this, we need both `G_Space` and `V_Space`. In fact, we need to do interpolation within `G_Space`. Hence, we need to set a directory to store the mex file for the interpolation code:
330 | ```
331 | G_Space = G_Space.Set_mex_Dir(Main_Dir,'mex_Laplace_Beltrami_Open_Surface_G_h_Interpolation');
332 | ```
333 | Here, we named the "internal" interpolation mex file of `G_Space`.
334 |
335 | Now we can get the precise coordinates of the DoFs in `V_Space`.
336 | ```
337 | V_Points = V_Space.Get_DoF_Coord(Mesh,G_Space,Geo_Points);
338 | ```
339 | Note: `V_Points` and `Geo_Points` are actually the same because `V_Space` and `G_Space` have the same reference finite element (P2 Lagrange) and the same DoFmap.
340 |
341 | With this, we can interpolate given functions in order to compute the Right-Hand-Side `RHS` data:
342 | ```
343 | f_h = f_exact(V_Points(:,1),V_Points(:,2));
344 | g_h = u_exact(V_Points(:,1),V_Points(:,2));
345 | ```
346 |
347 | ## Solve the Linear System
348 |
349 | Next, form the big system and solve:
350 | ```
351 | W_Num = W_Space.num_dof;
352 | ZZ = sparse(W_Num,W_Num);
353 | MAT = [K, B';
354 | B, ZZ];
355 | RHS = [M*f_h; B*g_h];
356 | % use backslash to solve
357 | Soln = MAT \ RHS;
358 | ```
359 | Then parse the solution vector:
360 | ```
361 | V_Num = V_Space.num_dof;
362 | u_h = Soln(1:V_Num,1);
363 | lambda_h = Soln(V_Num+1:end,1);
364 | ```
365 |
366 | ## Interpolate Normal and Tangent Vectors
367 |
368 | ```
369 | % define exact normal and tangent vectors on dGamma
370 | NV_func = @(u,v) [-u ./ sqrt(u.^2 + v.^2 + 1), v ./ sqrt(u.^2 + v.^2 + 1), 1 ./ sqrt(u.^2 + v.^2 + 1)];
371 | TV_func = @(u,v) [-v ./ sqrt(u.^2 + v.^2 + 4*u.^2.*v.^2), u ./ sqrt(u.^2 + v.^2 + 4*u.^2.*v.^2), -2*u.*v ./ sqrt(u.^2 + v.^2 + 4*u.^2.*v.^2)];
372 | % interpolate normal and tangent vectors using the G_Space nodal coordinates
373 | NV_h = NV_func(Geo_Points(:,1),Geo_Points(:,2));
374 | TV_h = TV_func(Geo_Points(:,1),Geo_Points(:,2));
375 | ```
376 |
377 | ## Compute Numerical Errors
378 |
379 | Now (assuming we have mex'ed our error definition file), we assemble the errors:
380 | ```
381 | FEM = DEMO_mex_Compute_Errors_Laplace_Beltrami_Open_Surface([],Geo_Points,G_Space.DoFmap,[],...
382 | Gamma_Embed,G_Space.DoFmap,V_Space.DoFmap,W_Space.DoFmap,...
383 | NV_h,TV_h,lambda_h,u_h);
384 | ```
385 |
386 | Extracting the errors is easy with
387 | ```
388 | % put FEM into a nice object to make accessing the matrices easier
389 | Error_MATS = FEMatrixAccessor('Errors',FEM);
390 |
391 | % pull out the (squared) errors, and take sqrt!
392 | u_L2_Error = sqrt(Error_MATS.Get_Matrix('u_L2_Error_sq'));
393 | lambda_L2_Error = sqrt(Error_MATS.Get_Matrix('lambda_L2_Error_sq'));
394 | NV_Error = sqrt(Error_MATS.Get_Matrix('NV_Error_sq'));
395 | TV_Error = sqrt(Error_MATS.Get_Matrix('TV_Error_sq'));
396 | Neumann_L2_Error = sqrt(Error_MATS.Get_Matrix('Neumann_L2_Error_sq'));
397 | ```
398 |
399 | And record each error in an array:
400 | ```
401 | u_Error_Vec(kk) = u_L2_Error;
402 | lambda_Error_Vec(kk) = lambda_L2_Error;
403 | NV_Error_Vec(kk) = NV_Error;
404 | TV_Error_Vec(kk) = TV_Error;
405 | Neumann_Error_Vec(kk) = Neumann_L2_Error;
406 | ```
407 |
408 | Finally, we close the *refinement* `for` loop:
409 | ```
410 | end
411 | ```
412 |
413 | # Plotting
414 |
415 | We will plot the solution data corresponding to the finest mesh that was used.
416 |
417 | ## Process P2 Solution into P1 Solution
418 |
419 | We cannot plot `u_h` directly because it is a function in `V_Space`. We need to interpolate it at the vertices of the piecewise linear mesh. Here is one way to do it that avoids writing an interpolation code:
420 | ```
421 | % find the mapping from mesh points to P2 nodes
422 | BB = [-1.001, 1.001, -1.001, 1.001, -1.001, 1.001];
423 | OT = mexOctree(V_Points,BB);
424 |
425 | % find the mapping via closest point
426 | [P1_to_P2, OT_dist] = OT.kNN_Search(Mesh.Points,1);
427 |
428 | % interpolate solution back onto piecewise linear mesh for plotting
429 | u_h_P1 = u_h(P1_to_P2,1);
430 |
431 | delete(OT); % clear the octree object!
432 | ```
433 | Note that `mexOctree` is included in FELICITY.
434 |
435 | ## Plot `u_h_P1`
436 |
437 | ```
438 | % plot u solution
439 | FH_u_h = figure('Renderer','painters','PaperPositionMode','auto','Color','w');
440 | trisurf(Mesh.ConnectivityList,Mesh.Points(:,1),Mesh.Points(:,2),Mesh.Points(:,3),u_h_P1);
441 | shading interp;
442 | colormap(jet);
443 | lightangle(80,-20);
444 | lightangle(-40,40);
445 | lighting phong;
446 | colorbar;
447 | caxis([0 max(u_h)]);
448 | title('Color plot of u_h on \Gamma');
449 | AX = [-1 1 -1 1 -0.5 0.5];
450 | axis(AX);
451 | axis equal;
452 | axis(AX);
453 | view([-35,30]);
454 | ```
455 |
456 | Here is a plot of `u_h_P1`:
457 |
458 |
459 |
460 | ## Plot `lambda_h`
461 |
462 | ```
463 | % Plot the \lambda solution:
464 | Bdy_X = W_Space.Get_DoF_Coord(Mesh,G_Space,Geo_Points);
465 | FH_lambda_h = figure('Renderer','painters','PaperPositionMode','auto','Color','w');
466 | p2 = edgemesh(W_Space.DoFmap, Bdy_X, lambda_h);
467 | set(p2,'LineWidth',2.0);
468 | shading interp;
469 | title('Color plot of \lambda_h on \partial \Gamma');
470 | axis equal;
471 | colorbar;
472 | view([-35,30]);
473 | ```
474 | Note that `edgemesh` is part of FELICITY.
475 |
476 | Here is a plot of `u_h_P1`:
477 |
478 |
479 |
480 | ## Plot The Error Decay
481 |
482 | Now plot the error decay in order to view the convergence rate:
483 | ```
484 | Order_2_Line = 4 * 4.^(-Refine_Vec);
485 | Order_3_Line = 1.0 * 8.^(-Refine_Vec);
486 | FH_error = figure('Renderer','painters','PaperPositionMode','auto','Color','w');
487 | semilogy(Refine_Vec,u_Error_Vec,'k-*',Refine_Vec,lambda_Error_Vec,'r-.',...
488 | Refine_Vec,NV_Error_Vec,'b-d',Refine_Vec,TV_Error_Vec,'g-s',...
489 | Refine_Vec,Neumann_Error_Vec,'r--o',...
490 | Refine_Vec,Order_2_Line,'m-d',...
491 | Refine_Vec,Order_3_Line,'k-d');
492 | xlabel('Refinement Level');
493 | ylabel('L^2 errors');
494 | title('Numerical Convergence Rates');
495 | axis([2 7 1e-8 1e1]);
496 | legend({'$\| u - u_h \|_{L^2(\Gamma)}$', '$\| \lambda - \lambda_h \|_{L^2(\Gamma)}$', ...
497 | '$\| \mathbf{\nu} - \boldmath{\nu}_h \|_{L^2(\Gamma)}$', '$\| \mathbf{\tau} - \mathbf{\tau}_h \|_{L^2(\Gamma)}$',...
498 | '$\| \lambda_h - (-\mathbf{\xi}_h \cdot \nabla_{\Gamma} u_h) \|_{L^2(\Gamma)}$',...
499 | '$O(h^2)$ line', '$O(h^3)$ line'},'Interpreter','latex','FontSize',12,'Location','best');
500 | grid;
501 | ```
502 |
503 | Here is a plot of the convergence rate:
504 |
505 |
506 |
507 | # Conclusion
508 |
509 | The method clearly converges from the numerical tests.
510 |
--------------------------------------------------------------------------------
/tutorials/Laplace_On_Cube_3D_1.md:
--------------------------------------------------------------------------------
1 | Solving the 3-D Laplace Equation
2 | ================================
3 |
4 | This is a tutorial on solving the 3-D Laplace equation with matrix re-assembly, when you have a large number of DoFs.
5 |
6 | # Scalar Laplace's Equation on a Cube (3-D)
7 |
8 | Weak Formulation:
9 |
10 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Laplace_On_Cube_3D_weak_formulation.jpg|width=720|alt=Weak Form 3-D Laplace Eqn]]
11 |
12 | # Input File
13 |
14 | In the MATLAB editor, create the following m-function and name it `MatAssem_Laplace_On_Cube_3D.m`:
15 |
16 | ```matlab
17 | function MATS = MatAssem_Laplace_On_Cube_3D()
18 |
19 | % define domain (3-D volume)
20 | Omega = Domain('tetrahedron');
21 |
22 | % define finite element spaces
23 | P1_Space = Element(Omega,lagrange_deg1_dim3,1);
24 |
25 | % define functions on FE spaces
26 | v = Test(P1_Space);
27 | u = Trial(P1_Space);
28 |
29 | % define FEM matrices
30 | Stiff_Matrix = Bilinear(P1_Space,P1_Space);
31 | Stiff_Matrix = Stiff_Matrix + Integral(Omega, v.grad' * u.grad );
32 |
33 | % set the minimum order of accuracy for the quad rule
34 | Quadrature_Order = 1;
35 | % define geometry representation - Domain, (default to piecewise linear)
36 | G1 = GeoElement(Omega);
37 | % define a set of matrices
38 | MATS = Matrices(Quadrature_Order,G1);
39 |
40 | % collect all of the matrices together
41 | MATS = MATS.Append_Matrix(Stiff_Matrix);
42 |
43 | end
44 | ```
45 |
46 | # Compile It!
47 |
48 | Put the file *MatAssem_Laplace_On_Cube_3D.m* into a directory that is *in your MATLAB path*. Compile it by running:
49 | ```matlab
50 | Convert_Form_Definition_to_MEX(@MatAssem_Laplace_On_Cube_3D,{},'mex_Laplace_On_Cube_3D_assemble');
51 | ```
52 |
53 | See the tutorial [Solve Laplace's Equation](../wiki/Solve_Laplaces_Eqn_1) for more info on this compilation step.
54 |
55 | # Run It!
56 |
57 | We will solve the 3-D Laplace problem. First, type the following commands at the MATLAB prompt (or put them into a separate script file):
58 |
59 | ```matlab
60 | Num_Pts = 20+1;
61 | [Omega_Tet, Omega_Vertex] = regular_tetrahedral_mesh(Num_Pts,Num_Pts,Num_Pts);
62 | Mesh = MeshTetrahedron(Omega_Tet, Omega_Vertex, 'Omega');
63 | clear Omega_Tet Omega_Vertex;
64 | ```
65 |
66 | Next, define the boundary of \Omega:
67 | ```matlab
68 | % define subdomains
69 | Bdy_Faces = Mesh.freeBoundary();
70 | Mesh = Mesh.Append_Subdomain('2D','Bdy',Bdy_Faces);
71 | ```
72 |
73 | Next, define the local-to-global Degree-of-Freedom map (DoF map) for the finite element space. Since we are using piecewise linear continuous basis functions for the solution, we can simply type:
74 | ```matlab
75 | P1_Space_DoFmap = uint32(Mesh.ConnectivityList);
76 | ```
77 |
78 | Now assemble the matrix from *scratch*:
79 | ```matlab
80 | tic
81 | FEM = mex_Laplace_On_Cube_3D_assemble([],Mesh.Points,P1_Space_DoFmap,[],[],P1_Space_DoFmap);
82 | toc
83 | ```
84 |
85 | Now assemble the matrix *again* using the known sparsity structure:
86 | ```matlab
87 | tic
88 | FEM = mex_Laplace_On_Cube_3D_assemble(FEM,Mesh.Points,P1_Space_DoFmap,[],[],P1_Space_DoFmap);
89 | toc
90 | ```
91 | Note that we input the`FEM` struct into the first argument of the matrix assembler code. Also notice that re-assembly is *much faster*. This is useful when solving *time-dependent* problems.
92 |
93 | Next, setup the boundary conditions:
94 | ```matlab
95 | Soln = zeros(Mesh.Num_Vtx,1); % init
96 | A = FEM(1).MAT;
97 | disp('----> set u = sin(2*pi*x) + cos(2*pi*y) + sin(2*pi*z) on Bdy.');
98 | % get Bdy Degrees-of-Freedom (DoF)
99 | Bdy_Nodes = unique(Bdy_Faces(:));
100 | % get Bdy DoF coordinates
101 | Bdy_XC = Mesh.Points(Bdy_Nodes,:);
102 | Soln(Bdy_Nodes,1) = sin(2*pi*Bdy_XC(:,1)) + cos(2*pi*Bdy_XC(:,2)) + sin(2*pi*Bdy_XC(:,3));
103 | RHS = 0*Soln;
104 | RHS = RHS - A * Soln;
105 | ```
106 |
107 | Finally, solve the system:
108 | ```matlab
109 | % get the free nodes of the system
110 | All_Vtx_Indices = (1:1:Mesh.Num_Vtx)';
111 | FreeNodes = setdiff(All_Vtx_Indices,Bdy_Nodes);
112 |
113 | disp(' ');
114 | disp(['Size of A = ', num2str(size(A))]);
115 | disp(['Number of Free DoFs = ', num2str(length(FreeNodes))]);
116 | disp(' ');
117 |
118 | disp('Solve linear system with backslash:');
119 | tic
120 | Soln(FreeNodes,1) = A(FreeNodes,FreeNodes) \ RHS(FreeNodes,1);
121 | toc
122 | ```
123 |
124 | NOTE: solving 3-D problems with backslash is rather limiting. Depending on your machine, you will not be able to make the system matrix too large without running out of memory when executing "backslash". Moreover, even if it does work, backslash is a *slow* solver for large 3-D problems. You need an iterative solver.
125 |
126 | One option is to use AGMG:
127 |
128 | http://homepages.ulb.ac.be/~ynotay/AGMG/
129 |
130 | It has a MATLAB interface. If you have AGMG successfully installed on your system (with the MATLAB interface) then you can do this:
131 | ```matlab
132 | % Note: you must have AGMG installed to run this!
133 | disp(' ');
134 | TOL = 1e-8;
135 | disp(['Solve linear system with AGMG with TOL = ', num2str(TOL,'%1.2G'), ':']);
136 | Soln_AGMG = Soln; % keep boundary conditions
137 | tic
138 | Soln_AGMG(FreeNodes,1) = agmg(A(FreeNodes,FreeNodes),RHS(FreeNodes,1),[],TOL);
139 | toc
140 |
141 | Soln_Diff = max(abs(Soln - Soln_AGMG));
142 | disp(' ');
143 | disp('Max difference between backslash and AGMG:');
144 | Soln_Diff
145 | ```
146 | For a large 3-D elliptic PDE problem, AGMG is *much faster* than backslash. Note: AGMG is *not* included with FELICITY; you must download it and install it separately.
147 |
148 | # Plot It!
149 |
150 | You can plot the solution with the following commands:
151 | ```matlab
152 | figure;
153 | h1 = trisurf(Bdy_Faces,Mesh.Points(:,1),Mesh.Points(:,2),Mesh.Points(:,3),Soln(:,1));
154 | title('Boundary Mesh and Boundary Condition','FontSize',12);
155 | set(gca,'FontSize',14);
156 | AX = [0 1 0 1 0 1];
157 | axis(AX);
158 | axis equal;
159 | axis(AX);
160 | ```
161 |
162 | This tutorial can be found in ".\Demo\Laplace_On_Cube_3D" sub-directory of FELICITY.
--------------------------------------------------------------------------------
/tutorials/Managing_DoFs_1.md:
--------------------------------------------------------------------------------
1 | Managing Degrees-of-Freedom (DoFs): Part 1
2 | ==========================================
3 |
4 | This is a simple tutorial on how to manage and sift through DoFs using the FiniteElementSpace class in FELICITY.
5 |
6 | # Managing Degrees-of-Freedom
7 |
8 | Consider a Degree-of-Freedom (DoF) allocation on a simple two triangle mesh:
9 |
10 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Tutorial_Managing_DoFs_Ex_P2.jpg|width=320|alt=DoF Allocation]]
11 |
12 | ## Scalar-Valued Finite Element Space
13 |
14 | The above figure is a simple mesh of a square domain with a piecewise quadratic finite element space defined over it. The nodal variables are denoted by diamonds. There are six local basis functions on each triangle, so there are six indices associated with each triangle:
15 |
16 | ```matlab
17 | T_1 : 1 2 3 8 6 5
18 | T_2 : 1 3 4 9 7 6
19 | ```
20 |
21 | This is the so-called Degree-of-Freedom map (*DoFmap*) for the finite element space defined on the mesh in the above figure. In this case, the total number of global DoFs is nine.
22 |
23 | DoFmaps are stored as matrices in MATLAB. Each row of the DoFmap corresponds to a mesh element (e.g. triangle), and each column corresponds to the local DoFs for that element. For example, column #2 of the DoFmap gives the global DoF indices for local basis function #2 of all the elements.
24 |
25 | See Section 6.4 of the PDF manual for more info.
26 |
27 | ## Tensor-Valued Finite Element Space
28 |
29 | For a tensor-valued space (such as when you are approximating a vector field), the DoFmap is a little more complicated. Suppose the functions in our finite element space are vector-valued with two components. Then the DoFmap decomposes into two parts:
30 |
31 | ```matlab
32 | % DoFmap for component #1:
33 | T_1 : 1 2 3 8 6 5
34 | T_2 : 1 3 4 9 7 6
35 |
36 | % DoFmap for component #2:
37 | T_1 : 10 11 12 17 15 14
38 | T_2 : 10 12 13 18 16 15
39 | ```
40 | i.e. the DoFmap for component #2 is obtained by adding the number of DoFs in the scalar finite element space to each index of the DoFmap of component #1.
41 |
42 | Thus, for storage purposes, we only need the DoFmap for the first component. The DoFmap for the second component can always be built on-the-fly from the first when needed.
43 |
44 | See Section 6.4 of the PDF manual for more info.
45 |
46 | # The FiniteElementSpace Class
47 |
48 | FELICITY has the class FiniteElementSpace that allows for access to and manipulation of the DoFmap.
49 |
50 | ## Basic DoF Routines
51 |
52 | Let’s do an example. First create the mesh in the figure above by typing the following at the MATLAB prompt:
53 | ```matlab
54 | % define a simple square mesh
55 | Vtx = [0 0; 1 0; 1 1; 0 1];
56 | Tri = [1 2 3; 1 3 4];
57 | % create a mesh object
58 | Mesh = MeshTriangle(Tri,Vtx,'Square');
59 | ```
60 |
61 | Now define several sub-domains of the square (see the mesh chapter 2 in the PDF manual):
62 | ```matlab
63 | % define the boundary of the square
64 | Bdy_Edge = Mesh.freeBoundary;
65 | Mesh = Mesh.Append_Subdomain('1D','Boundary',Bdy_Edge);
66 | % define some more subdomains
67 | Mesh = Mesh.Append_Subdomain('0D','Bottom Left Corner',[1]);
68 | Mesh = Mesh.Append_Subdomain('0D','Bottom Right Corner',[2]);
69 | Mesh = Mesh.Append_Subdomain('0D','Top Right Corner',[3]);
70 | Mesh = Mesh.Append_Subdomain('0D','Top Left Corner',[4]);
71 | Mesh = Mesh.Append_Subdomain('1D','Bottom Side',[1 2]);
72 | Mesh = Mesh.Append_Subdomain('1D','Top Side',[3 4]);
73 | Mesh = Mesh.Append_Subdomain('1D','Diag',[1 3]);
74 | Mesh = Mesh.Append_Subdomain('2D','Bottom Tri',[1]);
75 | ```
76 |
77 | Next, load up the degree 2, Lagrange element of dimension 2 (i.e. defined on a triangle), and create a ReferenceFiniteElement object:
78 | ```matlab
79 | % declare reference element
80 | P2_Elem = lagrange_deg2_dim2();
81 | RE = ReferenceFiniteElement(P2_Elem);
82 | ```
83 |
84 | Now create the FiniteElementSpace object for the FE space, which is a cartesian (tensor) product of two scalar valued FE spaces:
85 | ```matlab
86 | % declare a finite element space
87 | FES = FiniteElementSpace('P2',RE,Mesh,[],2); % 2 components
88 | clear RE; % not needed anymore
89 | ```
90 |
91 | The arguments of FiniteElementSpace (in order) are:
92 |
93 | * A name for the finite element space, e.g. `'P2'`.
94 | * The reference finite element for the space.
95 | * The mesh data for the finite element space.
96 | * The name of the sub-domain that the finite element space is defined on. In the case above, the space is defined on the entire mesh (no sub-domain), so just pass the empty matrix [].
97 | * The number of components of the space, if the space is a cartesian (tensor) product of scalar valued spaces. If this argument is omitted, then the default value is 1 component.
98 |
99 | We must still set the DoFmap for this space. So we store it by the following commands:
100 | ```matlab
101 | % set DoFmap
102 | DFM = [1 2 3 8 6 5;
103 | 1 3 4 9 7 6];
104 | FES = FES.Set_DoFmap(Mesh,DFM);
105 | clear DFM; % not needed anymore
106 | ```
107 | Note: if the mesh has lots of elements, then you may want to automatically generate the code for creating the DoFmap (see [Allocating Degrees-of-Freedom (DoFs)](../wiki/Allocate_DoFs_1)).
108 |
109 | Now lets look at the object. We can get a summary about the finite element space by typing:
110 | ```matlab
111 | FES
112 | ```
113 | We can look at the DoFmap:
114 | ```matlab
115 | FES.DoFmap
116 | ```
117 | We can get a list of the DoF indices for the first component:
118 | ```matlab
119 | DoF_Indices = FES.Get_DoFs
120 | ```
121 |
122 | And we can get the global coordinates of the DoFs (for the first component):
123 | ```matlab
124 | XC = FES.Get_DoF_Coord(Mesh)
125 | ```
126 | Note that the mesh geometry is needed to get the coordinates.
127 |
128 | We can plot the mesh and DoFs from the figure by the following commands:
129 | ```matlab
130 | figure;
131 | Mesh.Plot;
132 | AX = [-0.05 1.05 -0.05 1.05];
133 | axis(AX);
134 | hold on;
135 | for ii = 1:size(XC,1)
136 | plot(XC(ii,1),XC(ii,2),'kd','LineWidth',1.7);
137 | DoFstr = [num2str(ii)];
138 | text(XC(ii,1)+0.03,XC(ii,2)+0.02,DoFstr,'FontSize',16);
139 | end
140 | text(0.75,0.25,'T1');
141 | text(0.25,0.75,'T2');
142 | hold off;
143 | axis equal;
144 | axis(AX);
145 | axis off;
146 | title('P_2 Degrees-of-Freedom (Numbered Diamonds)');
147 | ```
148 |
149 | The DoFs for the second component of the FE space can be retrieved by:
150 | ```matlab
151 | % get the 2nd component DoF indices
152 | FES.Get_DoFs(2)
153 | ```
154 | We can also get a matrix of DoF indices, where the k-th column is a list of the DoF indices for the k-th component:
155 | ```matlab
156 | FES.Get_DoFs('all')
157 | ```
158 | Note: each row of this matrix corresponds to one of the diamonds in the figure, i.e. each row stores the first and second component DoF indices, both of which have the same global coordinates.
159 |
160 | ## DoFs on Sub-domains
161 |
162 | When sub-domains are present, you can retrieve the subset of DoFs that lie on a given sub-domain. For example, we can get the Boundary DoFs by:
163 | ```matlab
164 | % get list of the (1st component) DoFs on the Boundary
165 | Bdy_DoFs = FES.Get_DoFs_On_Subdomain(Mesh,'Boundary')
166 | ```
167 |
168 | You can also get the DoFs for all tensor components on a given sub-domain by passing an additional argument `'all'`, e.g.
169 | ```matlab
170 | % get list of all the (tensor component) DoFs on the Boundary
171 | FES.Get_DoFs_On_Subdomain(Mesh,'Boundary','all')
172 | ```
173 |
174 | Having defined `XC` in the previous section, we can get the coordinates of the DoFs on a sub-domain. For example, the coordinates of the DoFs on `'Boundary'` are obtained by:
175 | ```matlab
176 | % get the coordinates of the Boundary DoFs
177 | XC(Bdy_DoFs,:)
178 | ```
179 |
180 | We can also store in the FES object which sub-domains have their DoFs fixed (i.e. for Dirichlet conditions). For example, we can fix the `'Bottom Side'` and `'Top Side'` sub-domains (defined earlier):
181 | ```matlab
182 | FES = FES.Append_Fixed_Subdomain(Mesh,'Bottom Side');
183 | FES = FES.Append_Fixed_Subdomain(Mesh,'Top Side');
184 | ```
185 | Note: you must pass the mesh because the FiniteElementSpace object does some internal consistency checks to make sure the mesh actually contains the sub-domain.
186 |
187 | You can now list the DoFs that are fixed by:
188 | ```matlab
189 | FES.Get_Fixed_DoFs(Mesh)
190 | FES.Get_Fixed_DoFs(Mesh,2) % if you want the fixed DoFs for component 2
191 | ```
192 |
193 | Similarly, you can get the DoFs that are free by:
194 | ```matlab
195 | FES.Get_Free_DoFs(Mesh)
196 | ```
197 | If you want to clear the list of fixed sub-domains, just type
198 | ```matlab
199 | FES = FES.Set_Fixed_Subdomains(Mesh,{});
200 | ```
201 |
202 | The argument { } is an empty cell array. Alternatively, you could pass a cell array of strings, where each string is a name of a sub-domain. This is another way to set the fixed sub-domains, instead of using Append_Fixed_Subdomain.
--------------------------------------------------------------------------------
/tutorials/Managing_DoFs_2.md:
--------------------------------------------------------------------------------
1 | Managing Degrees-of-Freedom (DoFs): Part 2
2 | ==========================================
3 |
4 | This tutorial describes how to correctly define the DoFmap for finite element spaces that are defined on *sub-domains* of a global mesh.
5 |
6 | # Defining Finite Element Spaces on Sub-domains
7 |
8 | Degree-of-Freedom (DoF) Allocation on the diagonal of a square:
9 |
10 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Tutorial_Managing_DoFs_Ex_P1_On_1D_Subdomain.jpg|width=520|alt=DoF Allocation]]
11 |
12 | ## Introduction
13 |
14 | Finite element spaces can be *defined over sub-domains* of a global mesh. For example, you may have a piecewise linear space on a domain that is a polygonal curve embedded in a triangular mesh. Allocating DoFs in this case is no more difficult than what was done in [Managing DoFs: Part 1](../wiki/Managing_DoFs_1). But there may be some confusion between global mesh vertex indices and FE space DoF indices. This tutorial explains the difference.
15 |
16 | ## Scalar-Valued Finite Element Space
17 |
18 | The above figure is a simple mesh of a square domain with a piecewise linear finite element space defined over the diagonal. The nodal variables are denoted by diamonds. There are two local basis functions on each edge of the diagonal, so there are two indices associated with each edge:
19 | ```matlab
20 | E_1 : 1 2
21 | E_2 : 2 3
22 | ```
23 | This is the DoFmap for the finite element space defined on the diagonal sub-domain of the mesh in the above figure. In this case, the total number of global DoFs is three.
24 |
25 | See Section 6.4.3 of the PDF manual for more info.
26 |
27 | ## Allocating DoFs On An Embedded Sub-domain
28 |
29 | Let’s do an example. First create the mesh in the figure above by typing the following at the MATLAB prompt:
30 | ```matlab
31 | % define a simple square mesh
32 | Vtx = [0 0; 1 0; 1 1; 0 1; 0.5 0.5];
33 | Tri = [1 2 5; 2 3 5; 3 4 5; 4 1 5];
34 | % create a mesh object
35 | Mesh = MeshTriangle(Tri,Vtx,'Square');
36 | ```
37 |
38 | Now define the "diagonal" sub-domain of the square:
39 | ```matlab
40 | % define a subdomain to be one of the diagonals of the square
41 | Mesh = Mesh.Append_Subdomain('1D','Diag',[1 5; 5 3]);
42 | ```
43 |
44 | Next, create a FiniteElementSpace object for the finite element space over the subdomain `'Diag'`:
45 | ```matlab
46 | % declare reference element
47 | P1_Elem = lagrange_deg1_dim1();
48 | RE = ReferenceFiniteElement(P1_Elem);
49 | % declare a finite element space that is defined on ’Diag’
50 | FES = FiniteElementSpace('P1',RE,Mesh,'Diag');
51 | clear RE;
52 | ```
53 | Note that the reference element is 1-D, because Diag has topological dimension 1.
54 |
55 | Now, set the DoFmap for this space. Since the domain consists of only two edges and the space is piecewise linear, we type the following commands:
56 | ```matlab
57 | % set DoFmap
58 | DFM = [1 2;
59 | 2 3];
60 | FES = FES.Set_DoFmap(Mesh,DFM);
61 | clear DFM;
62 | ```
63 | where edges `E_1` and `E_2` share DoF #2 at their common vertex.
64 |
65 | Note: if the mesh has lots of elements, then you may want to automatically generate the code for creating the DoFmap (see [Allocating Degrees-of-Freedom (DoFs)](../wiki/Allocate_DoFs_1)).
66 |
67 | We can plot the mesh and DoFs from the figure by the following commands:
68 | ```matlab
69 | figure;
70 | Mesh.Plot;
71 | AX = [-0.05 1.05 -0.05 1.05];
72 | axis(AX);
73 | hold on;
74 | for ii = 1:size(XC,1)
75 | plot(XC(ii,1),XC(ii,2),'kd','LineWidth',1.7);
76 | DoFstr = [num2str(ii)];
77 | text(XC(ii,1)+0.03,XC(ii,2)+0.0,DoFstr,'FontSize',16);
78 | end
79 | text(0.5,0.2,'T1');
80 | text(0.8,0.5,'T2');
81 | text(0.5,0.8,'T3');
82 | text(0.2,0.5,'T4');
83 | text(0.27,0.25,'E1');
84 | text(0.77,0.75,'E2');
85 | Mesh.Plot_Subdomain('Diag');
86 | hold off;
87 | axis equal;
88 | axis(AX);
89 | axis off;
90 | title('P_1 DoFs of Finite Element Space (Numbered Diamonds)');
91 | ```
92 |
93 | ## Remarks On DoF Numbering
94 |
95 | We emphasize that the DoF indices in the finite element space have *no relation* to the numbering of the vertices in the mesh. In this example, the center mesh vertex of the square has index #5, but the finite element space DoF index at the center vertex is #2.
96 |
97 | When defining any DoFmap, there must not be any "gaps" in the DoF numbering in the DoFmap. For example, we could not use this:
98 | ```matlab
99 | % set DoFmap
100 | DFM = [1 5;
101 | 5 3];
102 | FES = FES.Set_DoFmap(Mesh,DFM);
103 | ```
104 | where `DFM` is simply the connectivity of Diag referenced to the global mesh vertices. If we use this, then FELICITY will give an error message when calling `Set_DoFmap`.
105 |
106 | In conclusion, if `N` is the number of distinct basis functions in the finite element space (i.e. the _dimension_ of the finite element space), then the DoFmap must contain *all indices* from 1 to N.
--------------------------------------------------------------------------------
/tutorials/Mesh_Generation_With_Solving_PDE_1.md:
--------------------------------------------------------------------------------
1 | Mesh Generation and Solving a PDE
2 | =================================
3 |
4 | This tutorial takes you from generating a mesh of a non-trivial domain, to solving a PDE (Laplace's equation) on that mesh with non-trivial boundary conditions.
5 |
6 | # Laplace's Equation on a Disk With Holes (2D)
7 |
8 | Weak Formulation:
9 |
10 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Mesh_Gen_With_PDE_Solve_weak_formulation.jpg|width=760|alt=Weak Form Laplace Eqn with Holes]]
11 |
12 | Domain \Omega:
13 |
14 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Mesh_Gen_With_PDE_Solve_Domain_Fig.jpg|width=670|alt=image of Domain Omega]]
15 |
16 | Note: you should do the tutorial [Mesh Generation With TIGER](../wiki/Mesh_Generation_with_TIGER_1) before proceeding.
17 |
18 | # Input File
19 |
20 | First we need to generate code that will assemble the matrices we need to form the discrete finite element version of the weak formulation described above.
21 |
22 | In the MATLAB editor, create the following m-function and name it `MatAssem_Mesh_Gen_With_Solving_PDE.m`:
23 | ```matlab
24 | function MATS = MatAssem_Mesh_Gen_With_Solving_PDE()
25 |
26 | % define domain (2-D)
27 | Omega = Domain('triangle');
28 | Gamma = Domain('interval') < Omega;
29 | % there will be other boundaries also, but they are not needed here.
30 |
31 | % define finite element spaces
32 | P1_Space = Element(Omega,lagrange_deg1_dim2,1);
33 |
34 | % define functions on FE spaces
35 | v = Test(P1_Space);
36 | u = Trial(P1_Space);
37 |
38 | % define geometric function on \Gamma
39 | gf = GeoFunc(Gamma);
40 |
41 | % define FEM matrices
42 | Stiff_Matrix = Bilinear(P1_Space,P1_Space);
43 | Stiff_Matrix = Stiff_Matrix + Integral(Omega, v.grad' * u.grad );
44 |
45 | Neumann_Matrix = Linear(P1_Space);
46 | Neumann_Matrix = Neumann_Matrix + Integral(Gamma, 2*cos(pi*(gf.X(1) + gf.X(2))) * v.val );
47 |
48 | % set the minimum order of accuracy for the quad rule
49 | Quadrature_Order = 5;
50 | % define geometry representation - Domain, (default to piecewise linear)
51 | G1 = GeoElement(Omega);
52 | % define a set of matrices
53 | MATS = Matrices(Quadrature_Order,G1);
54 |
55 | % collect all of the matrices together
56 | MATS = MATS.Append_Matrix(Stiff_Matrix);
57 | MATS = MATS.Append_Matrix(Neumann_Matrix);
58 |
59 | end
60 | ```
61 |
62 | The Linear form Neumann_Matrix represents the Neumann condition. Note that we implemented the _g_ function (see PDE formulation above) with
63 | ```matlab
64 | 2*cos(pi*(gf.X(1) + gf.X(2)))
65 | ```
66 | where `gf` is a geometric function on `Gamma` that provides access to geometric information:
67 | * `gf.X(i)` is a symbolic variable that represents the `i`-th coordinate restricted to `Gamma`. For example, `gf.X(1)` is really the _x_ coordinate evaluated on `Gamma`.
68 | * `gf.T` is a symbolic variable representing the unit tangent vector on `Gamma`.
69 | * `gf.N` is a symbolic variable representing the unit normal vector on `Gamma`.
70 | * etc...
71 | See chapter 4 of the PDF manual for more info.
72 |
73 | # Compile It!
74 |
75 | Put the file `MatAssem_Mesh_Gen_With_Solving_PDE.m` into a directory that is *in your MATLAB path*. Compile it by running:
76 | ```matlab
77 | Convert_Form_Definition_to_MEX(@MatAssem_Mesh_Gen_With_Solving_PDE,{},'mex_Mesh_Gen_With_Solving_PDE_assemble');
78 | ```
79 |
80 | Here we named the executable `mex_Mesh_Gen_With_Solving_PDE_assemble`. See the tutorial [Solve Laplace's Equation](../wiki/Solve_Laplaces_Eqn_1) for more info on this compilation step.
81 |
82 | # How To Solve The Problem
83 |
84 | We now walk you through creating the mesh, defining the subdomains, defining finite element spaces and boundary conditions, assembling the matrices, solving the system, and plotting the solution.
85 |
86 | Note: you can either type the following commands at the MATLAB prompt or put them into a separate script file.
87 |
88 | ## Create The Mesh
89 |
90 | Start by creating a mesh generator:
91 | ```matlab
92 | % create the (2-D) mesher object
93 | Box_Dim = [-1, 1];
94 | Num_BCC_Points = 61;
95 | Use_Newton = false;
96 | TOL = 1E-3; % tolerance to use in computing the cut points
97 | % BCC mesh of the square [-1,1] x [-1,1]
98 | MG = Mesher2Dmex(Box_Dim,Num_BCC_Points,Use_Newton,TOL);
99 | ```
100 |
101 | Now define a level set function that represents our domain \Omega:
102 | ```matlab
103 | % create a disk shaped domain with 3 holes in it
104 | LS = LS_Many_Ellipses();
105 | LS.Param.rad_x = [0.7, 0.15, 0.15, 0.15];
106 | LS.Param.rad_y = [0.7, 0.15, 0.15, 0.15];
107 | LS.Param.cx = [0, 0, 0.35*(sqrt(3)/2), -0.35*(sqrt(3)/2)];
108 | LS.Param.cy = [0, -0.35, 0.35/2, 0.35/2];
109 | LS.Param.sign = [1, -1, -1, -1];
110 | ```
111 | and create the mesh:
112 | ```matlab
113 | % setup up handle to interpolation routine
114 | Interp_Handle = @(pt) LS.Interpolate(pt);
115 |
116 | % mesh it!
117 | MG = MG.Get_Cut_Info(Interp_Handle);
118 | [TRI, VTX] = MG.run_mex(Interp_Handle);
119 | ```
120 |
121 | For convenience, we shall use a FELICITY mesh class to hold the mesh information:
122 | ```matlab
123 | % create a Mesh object
124 | Mesh = MeshTriangle(TRI,VTX,'Omega');
125 | % remove any vertices that are not referenced by the triangulation
126 | Mesh = Mesh.Remove_Unused_Vertices;
127 | ```
128 | This last line deserves a comment. The output of the mesh generator often contains vertices that are *not* referenced by the triangulation. In fact, you may have seen MATLAB give a warning about this. This is ok. HOWEVER, you must be careful if you want to use the triangulation data as your DoFmap for a piecewise linear finite element space (which we do below). In this case, you should use the `Remove_Unused_Vertices` method to delete any unused vertices and renumber the remaining ones; the new mesh object will contain fewer vertices and all of them will be referenced by the triangulation.
129 |
130 | Now plot the domain to make sure it looks correct:
131 | ```matlab
132 | % plot the domain Omega
133 | figure;
134 | Mesh.Plot;
135 | title('Mesh of Domain \Omega');
136 | AX = 0.8 * [-1 1 -1 1];
137 | axis(AX);
138 | axis equal;
139 | axis(AX);
140 | hold on;
141 | text(0,0.74,'\Gamma');
142 | text(0,-0.25,'\Sigma_1');
143 | text(0.38,0.2,'\Sigma_2');
144 | text(-0.43,0.2,'\Sigma_3');
145 | hold off;
146 | ```
147 |
148 | ## Define Subdomains
149 |
150 | Next, we find and store information for the various subdomains. Note: all of the subdomains here are contained in the boundary of \Omega.
151 |
152 | First, we use basic geometric information to identify `Gamma`:
153 | ```matlab
154 | % find subdomains
155 | Bdy_of_Omega = Mesh.freeBoundary(); % set of edges
156 | % find the outer part: Gamma
157 | Bdy_of_Omega_MidPt = 0.5 * (Mesh.Points(Bdy_of_Omega(:,1),:) + Mesh.Points(Bdy_of_Omega(:,2),:));
158 | Bdy_of_Omega_MAG = sqrt(sum(Bdy_of_Omega_MidPt.^2,2));
159 | Outer_Mask = (Bdy_of_Omega_MAG > 0.65);
160 | Gamma_Edges = Bdy_of_Omega(Outer_Mask,:);
161 | ```
162 | and we apply similar processing to identify `Sigma_i`:
163 | ```matlab
164 | % find the other parts: Sigma_1, Sigma_2, Sigma_3.
165 | Sigma_1_Mask = (Bdy_of_Omega_MidPt(:,2) < 0) & ~Outer_Mask;
166 | Sigma_2_Mask = (Bdy_of_Omega_MidPt(:,1) > 0) & (Bdy_of_Omega_MidPt(:,2) > 0) & ~Outer_Mask;
167 | Sigma_3_Mask = (Bdy_of_Omega_MidPt(:,1) < 0) & (Bdy_of_Omega_MidPt(:,2) > 0) & ~Outer_Mask;
168 | Sigma_1_Edges = Bdy_of_Omega(Sigma_1_Mask,:);
169 | Sigma_2_Edges = Bdy_of_Omega(Sigma_2_Mask,:);
170 | Sigma_3_Edges = Bdy_of_Omega(Sigma_3_Mask,:);
171 | ```
172 |
173 | Then, we store this information in the mesh object:
174 | ```matlab
175 | % now define subdomains
176 | Mesh = Mesh.Append_Subdomain('1D','Gamma',Gamma_Edges);
177 | Mesh = Mesh.Append_Subdomain('1D','Sigma_1',Sigma_1_Edges);
178 | Mesh = Mesh.Append_Subdomain('1D','Sigma_2',Sigma_2_Edges);
179 | Mesh = Mesh.Append_Subdomain('1D','Sigma_3',Sigma_3_Edges);
180 | ```
181 |
182 | Lastly, we create embedding data that will be used by the matrix assembler:
183 | ```matlab
184 | % create subdomain embedding data
185 | DoI_Names = {'Omega'; 'Gamma'};
186 | Subdomain_Embed = Mesh.Generate_Subdomain_Embedding_Data(DoI_Names);
187 | ```
188 |
189 | ## Define The Finite Element Space
190 |
191 | First, define the DoFmap using the triangulation:
192 | ```matlab
193 | P1_Space_DoFmap = uint32(Mesh.ConnectivityList);
194 | ```
195 | Recall the comment before about `Remove_Unused_Vertices`.
196 |
197 | Next, use a FELICITY class to contain information about the finite element space:
198 | ```matlab
199 | % define finite element space object
200 | P1_RefElem = ReferenceFiniteElement(lagrange_deg1_dim2());
201 | P1_Lagrange_Space = FiniteElementSpace('Solution', P1_RefElem, Mesh, 'Omega');
202 | % store DoFmap
203 | P1_Lagrange_Space = P1_Lagrange_Space.Set_DoFmap(Mesh,P1_Space_DoFmap);
204 | ```
205 | See the tutorial [Managing DoFs: Part 1](../wiki/Managing_DoFs_1) for more info on how `FiniteElementSpace` works.
206 |
207 | Then, set subdomains where Dirichlet boundary conditions are set:
208 | ```matlab
209 | P1_Lagrange_Space = P1_Lagrange_Space.Set_Fixed_Subdomains(Mesh,{'Sigma_1', 'Sigma_2', 'Sigma_3'});
210 | ```
211 | "Fixed" refers to the fact that any Degrees-of-Freedom (DoFs) on those subdomains are fixed (given) by some condition.
212 |
213 | ## Assemble Matrices
214 |
215 | Call the executable that you generated before:
216 | ```matlab
217 | FEM = mex_Mesh_Gen_With_Solving_PDE_assemble([],Mesh.Points,P1_Space_DoFmap,[],Subdomain_Embed,P1_Lagrange_Space.DoFmap);
218 | ```
219 |
220 | ## Setup Linear System
221 |
222 | Next, we setup the linear system (with boundary conditions) and solve it.
223 |
224 | Start with:
225 | ```matlab
226 | disp('Setup Laplace equation with Dirichlet conditions on Sigma_1, Sigma_2, Sigma_3:');
227 | Soln = zeros(P1_Lagrange_Space.num_dof,1); % init
228 | A = FEM(2).MAT;
229 | ```
230 | Set the Dirichlet conditions:
231 | ```matlab
232 | disp('----> set u = -1 on Sigma_1.');
233 | Sigma_1_DoFs = P1_Lagrange_Space.Get_DoFs_On_Subdomain(Mesh,'Sigma_1');
234 | Soln(Sigma_1_DoFs,1) = -1;
235 | disp('----> set u = +1 on Sigma_2 and Sigma_3.');
236 | Sigma_2_DoFs = P1_Lagrange_Space.Get_DoFs_On_Subdomain(Mesh,'Sigma_2');
237 | Soln(Sigma_2_DoFs,1) = 1;
238 | Sigma_3_DoFs = P1_Lagrange_Space.Get_DoFs_On_Subdomain(Mesh,'Sigma_3');
239 | Soln(Sigma_3_DoFs,1) = 1;
240 | ```
241 | Create the right-hand-side data (i.e. the ``load'' vector) to contain the Neumann condition and the Dirichlet conditions:
242 | ```matlab
243 | % set right-hand-side (RHS) data
244 | RHS = FEM(1).MAT; % Neumann matrix
245 | RHS = RHS - A * Soln;
246 | ```
247 |
248 | Get a list of the ``Free'' DoFs:
249 | ```matlab
250 | FreeDoFs = P1_Lagrange_Space.Get_Free_DoFs(Mesh);
251 | ```
252 | then solve the system just for those DoFs:
253 | ```matlab
254 | disp('Solve linear system with backslash:');
255 | Soln(FreeDoFs,1) = A(FreeDoFs,FreeDoFs) \ RHS(FreeDoFs,1);
256 | ```
257 |
258 | ## Plot Solution
259 |
260 | Finally, plot the solution:
261 | ```matlab
262 | figure;
263 | h1 = trisurf(Mesh.ConnectivityList,Mesh.Points(:,1),Mesh.Points(:,2),Soln);
264 | shading interp;
265 | title('Solution on \Omega','FontSize',14);
266 | set(gca,'FontSize',14);
267 | colorbar;
268 | view(2);
269 | axis(AX);
270 | axis equal;
271 | axis(AX);
272 | ```
273 |
274 | Note: the solution should look like:
275 |
276 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Mesh_Gen_With_PDE_Solve_Solution_Fig.jpg|width=670|alt=image of solution]]
--------------------------------------------------------------------------------
/tutorials/Mesh_Generation_with_TIGER_1.md:
--------------------------------------------------------------------------------
1 | Mesh Generation With TIGER: Part 1
2 | ==================================
3 |
4 | This is a tutorial on generating unstructured meshes of domains described by iso-surfaces.
5 |
6 | # Introduction
7 |
8 | FELICITY provides an algorithm for generating unstructured (triangle and tetrahedral) meshes with *guaranteed* angle bounds. In 3-D, the dihedral angles are mathematically guaranteed to be between *8.54* degrees and *164.18* degrees. Hence, meshes can be generated _robustly_. And it is very *fast*!
9 |
10 | *Caveat:* The method only creates meshes of _interior_ regions. For example, suppose you have a square domain that contains a circular hole. You can either mesh the part of the square that is outside the hole, or you can mesh the hole alone. But you cannot mesh both simultaneously, such that the resulting mesh has edges that conform to the boundary of the hole. If you try to do this, then the angle bounds are not guaranteed. (Note: for 2-D meshes, it seems you *can* get a conforming mesh of both interior and exterior regions.)
11 |
12 | Also note these meshes are quasi-uniform.
13 |
14 | # Simple Example (2-D)
15 |
16 | Mesh an ellipse.
17 |
18 | ## Initialization
19 |
20 | After you have installed FELICITY, make sure you run `test_FELICITY.m` or `compile_static_codes.m`. This will compile the C++ code that is the backbone of the mesh generator.
21 |
22 | We start simply with an ellipse in 2-D. Type the following at the MATLAB prompt (or put it in a script file):
23 | ```matlab
24 | % create mesher object
25 | Box_Dim = [0, 1];
26 | Num_BCC_Points = 31;
27 | Use_Newton = false;
28 | TOL = 1E-2; % tolerance to use in computing the cut points
29 | % BCC mesh of the unit square [0,1] x [0,1]
30 | MG = Mesher2Dmex(Box_Dim,Num_BCC_Points,Use_Newton,TOL);
31 | ```
32 |
33 | This creates a MATLAB object that runs the mesher and interfaces with a MEX/C++ code. Here, we have created a background mesh with 31 points along the x and y dimensions for a unit square. We have chosen not to use Newton's method for computing cut point intersections of the iso-surface with the mesh edges (default is simple bisection).
34 |
35 | The tolerance dictates how well we approximate the cut points. Here, it is set to `1E-2` and is relative to the mesh edge lengths. Thus it is a relative tolerance (or relative error).
36 |
37 | ## Define Iso-surface
38 |
39 | Next, we define a level set function that represents an ellipse:
40 | ```matlab
41 | LS = LS_Many_Ellipses();
42 | LS.Param.rad_x = [0.4];
43 | LS.Param.rad_y = [0.2];
44 | LS.Param.cx = [0.5];
45 | LS.Param.cy = [0.5];
46 | LS.Param.sign = [1]; % +1 or -1 only
47 | ```
48 | which defines the two radii, the center position of the ellipse, and whether it is a positive or negative ellipse (inside or out). Note: `LS_Many_Ellipses` is a simple MATLAB class (provided by FELICITY) that implements an interpolation routine that defines the iso-surface. Look in the following directory for more info:
49 | ```matlab
50 | ./FELICITY/Static_Codes/Isosurface_Meshing/LevelSets_2D/@LS_Many_Ellipses
51 | ```
52 |
53 | ## Generate Mesh
54 |
55 | Next, we need to pass a function handle for the level set interpolation to the mesh generator. For this example, we do
56 | ```matlab
57 | % setup up handle to interpolation routine
58 | Interp_Handle = @(pt) LS.Interpolate(pt);
59 | ```
60 | In general, the user may provide their own interpolation routine that samples the level set function. The format of the function must be:
61 | ```matlab
62 | [phi, grad_phi] = Interp_Func(point);
63 | ```
64 | where
65 | ```matlab
66 | % phi = the level set function value
67 | % grad_phi = the gradient of the level set function
68 | % point = array of point coordinate to evaluate at
69 | ```
70 |
71 | Then, run the mesher with the following commands.
72 | ```matlab
73 | MG = MG.Get_Cut_Info(Interp_Handle);
74 | [TRI, VTX] = MG.run_mex(Interp_Handle);
75 | ```
76 |
77 | This outputs the triangle connectivity data `TRI` (Mx3 matrix, where M is the number of triangles), and the vertex coordinates `VTX` (Nx2 matrix, where N is the number of vertices).
78 |
79 | Now you can plot the mesh with the MATLAB command trimesh:
80 | ```matlab
81 | trimesh(TRI,VTX(:,1),VTX(:,2));
82 | axis equal;
83 | grid on;
84 | ```
85 | Note: you can also use the FELICITY class `MeshTriangle` to plot it and make other manipulations. See the tutorial [Mesh Classes](../wiki/Tutorial_Meshes_1) for more info.
86 |
87 | # Mesh With Holes (2-D)
88 |
89 | Mesh a disk with ellipse shaped holes.
90 |
91 | ## Define Iso-surface
92 |
93 | Use the same `LS_Many_Ellipses` class to define a domain with holes:
94 | ```matlab
95 | LS = LS_Many_Ellipses();
96 | LS.Param.rad_x = [0.45, 0.1, 0.2];
97 | LS.Param.rad_y = [0.45, 0.2, 0.1];
98 | LS.Param.cx = [0.5, 0.35, 0.65];
99 | LS.Param.cy = [0.5, 0.35, 0.65];
100 | LS.Param.sign = [1, -1, -1];
101 | ```
102 |
103 | All of the parameters are vectors of equal length that define three ellipses. The first ellipse is actually a circle (equal x and y radii) and has +1 sign. The next two ellipses have a negative sign which indicates to subtract them (see the implementation of the `Interpolate` routine in the `LS_Many_Ellipses` class for more info.
104 |
105 | ## Generate Mesh
106 |
107 | Next, generate the mesh exactly how we did before:
108 | ```matlab
109 | % setup up handle to interpolation routine
110 | Interp_Handle = @(pt) LS.Interpolate(pt);
111 |
112 | MG = MG.Get_Cut_Info(Interp_Handle);
113 | [TRI, VTX] = MG.run_mex(Interp_Handle);
114 | ```
115 | and plot it:
116 | ```matlab
117 | trimesh(TRI,VTX(:,1),VTX(:,2));
118 | axis equal;
119 | grid on;
120 | ```
121 |
122 | ## How To Define Other Surfaces
123 |
124 | To define your own domain, simply copy (and rename) the directory
125 | ```matlab
126 | ./FELICITY/Static_Codes/Isosurface_Meshing/LevelSets_2D/@LS_Many_Ellipses
127 | ```
128 | to another name of your choosing. Note that `LS_Many_Ellipses` is sub-classed from the class Abstract_LevelSet, which contains internal variables `Param` and `Grid` for storing the data that defines the bounding surface. Then, you just need to rewrite the `Interpolate` routine to match your domain. Look at the unit tests in
129 | ```matlab
130 | ./FELICITY/Static_Codes/Isosurface_Meshing/Unit_Test/
131 | ```
132 | for more info and examples.
133 |
134 | *Note* you can mesh domains described by a polygon by using MATLAB's `inpolygon` routine to indicate whether a point is inside or outside. However, you can only use bisection to compute the cut points. Also note that the output mesh from the TIGER algorithm will not have the same vertices as the polygonal curve used to describe it.
135 |
136 | # Simple Example (3-D)
137 |
138 | Mesh a sphere.
139 |
140 | ## Initialization
141 |
142 | Initializing the mesh generator is basically the same:
143 | ```matlab
144 | % create mesher object
145 | Cube_Dim = [0, 1];
146 | Num_BCC_Points = 25;
147 | Use_Newton = true;
148 | TOL = 1e-12; % tolerance to use in computing the cut points
149 | % BCC mesh of the unit cube [0,1] x [0,1] x [0,1]
150 | MG = Mesher3Dmex(Cube_Dim,Num_BCC_Points,Use_Newton,TOL);
151 | ```
152 |
153 | Here we use Newton's method for approximating the cut points. Note, this means you must provide the level set function value, as well as its gradient, in the `Interpolate` routine.
154 |
155 | The tolerance is set to 1E-12 and refers to the following convergence criteria:
156 | ```matlab
157 | |f(p)| < TOL = 1E-12.
158 | ```
159 | where `f` is the level set function value. Note: we always assume we are meshing the volume bounded by the *zero* level set.
160 |
161 | ## Define Iso-surface
162 |
163 | We use a FELICITY provided MATLAB class to define the sphere:
164 | ```matlab
165 | LS = LS_Sphere();
166 | LS.Param.cx = 0.5;
167 | LS.Param.cy = 0.5;
168 | LS.Param.cz = 0.5;
169 | LS.Param.rad = 0.1;
170 | LS.Param.sign = 1;
171 | ```
172 | The sphere has radius 0.1, and it is centered at (0.5,0.5,0.5).
173 |
174 | ## Generate Mesh
175 |
176 | Generating the mesh is the same as before:
177 | ```matlab
178 | % setup up handle to interpolation routine
179 | Interp_Handle = @(pt) LS.Interpolate(pt);
180 |
181 | MG = MG.Get_Cut_Info(Interp_Handle);
182 | [TET, VTX] = MG.run_mex(Interp_Handle);
183 | ```
184 |
185 | This outputs the tetrahedron connectivity data `TET` (Mx4 matrix, where M is the number of tetrahedrons), and the vertex coordinates `VTX` (Nx3 matrix, where N is the number of vertices).
186 |
187 | Now you can plot the mesh with the MATLAB command tetramesh:
188 | ```matlab
189 | tetramesh(TET,VTX);
190 | axis equal;
191 | grid on;
192 | ```
193 | Note: you can also use the FELICITY class `MeshTetrahedron` to plot it and make other manipulations. See the tutorial [Mesh Classes](../wiki/Tutorial_Meshes_1) for more info.
194 |
195 | # Volume Data (3-D)
196 |
197 | Mesh volumetric data. This is probably the most common use for the TIGER algorithm.
198 |
199 | ## Initialization
200 |
201 | Initialize the mesh generator:
202 | ```matlab
203 | % create mesher object
204 | Cube_Dim = [0, 1];
205 | Num_BCC_Points = 25;
206 | Use_Newton = false;
207 | TOL = 1e-2; % tolerance to use in computing the cut points
208 | % BCC mesh of the unit cube [0,1] x [0,1] x [0,1]
209 | MG = Mesher3Dmex(Cube_Dim,Num_BCC_Points,Use_Newton,TOL);
210 | ```
211 |
212 | We only use bisection here.
213 |
214 | ## Define Iso-surface
215 |
216 | We use a FELICITY class to store the volumetric data:
217 | ```matlab
218 | LS = LS_Vol();
219 | % define a 3-D cartesian grid for sampling the volume data:
220 | s_vec = linspace(-0.1,1.1,101)';
221 | [X, Y, Z] = meshgrid(s_vec,s_vec,s_vec);
222 | LS.Grid.s_vec = s_vec;
223 |
224 | % define grid data
225 | R = 0.1;
226 | C0 = 0.4;
227 | V_pert = sin(2*pi*X).*sin(2*pi*Y).*cos(4*pi*Z);
228 | LS.Grid.V = R - sqrt((X - 0.5).^2 + (Y - 0.5).^2 + (Z - 0.5).^2) + C0 * V_pert;
229 | ```
230 | In other words, we defined volumetric data whose zero level set is a sphere, but then we added a _perturbation_ to that.
231 |
232 | ## Generate Mesh
233 |
234 | Generating the mesh:
235 | ```matlab
236 | % setup up handle to interpolation routine
237 | Interp_Handle = @(pt) LS.Interpolate(pt);
238 |
239 | MG = MG.Get_Cut_Info(Interp_Handle);
240 | [TET, VTX] = MG.run_mex(Interp_Handle);
241 | ```
242 | and plot it:
243 | ```matlab
244 | tetramesh(TET,VTX);
245 | axis equal;
246 | grid on;
247 | ```
248 |
249 | # Conclusion
250 |
251 | For 2-D and 3-D, the definition of the iso-surface (i.e. zero level set) is made in the Interpolate routine. For an example, look in the directory:
252 | ```matlab
253 | ./FELICITY/Static_Codes/Isosurface_Meshing/LevelSets_3D/@LS_Vol
254 | ```
255 |
256 | See the next tutorial [Mesh Generation With TIGER: Part 2](../wiki/Mesh_Generation_with_TIGER_2) on how to generate bulk meshes from polygons and surface triangulations.
--------------------------------------------------------------------------------
/tutorials/Mesh_Generation_with_TIGER_2.md:
--------------------------------------------------------------------------------
1 | Mesh Generation With TIGER: Part 2
2 | ==================================
3 |
4 | This is a tutorial on generating unstructured meshes of polygons and surface meshes.
5 |
6 | # Introduction
7 |
8 | The TIGER algorithm can also mesh domains described by a closed polygonal curve or a water-tight polyhedral surface.
9 |
10 | Note: the output mesh from the TIGER algorithm will not have the same vertices as the polygon or polyhedral surface used to describe the domain. Also, you can only use bisection to compute the cut points.
11 |
12 | # Example in 2-D
13 |
14 | We shall use the class `Polygon_Mesh` located in
15 | ```matlab
16 | ./FELICITY/Static_Codes/Isosurface_Meshing/LevelSets_2D/@Polygon_Mesh
17 | ```
18 | to create a 2-D mesh of the domain.
19 |
20 | ## Create Polygonal Mesh
21 |
22 | First, sample a parametric curve:
23 | ```matlab
24 | t = linspace(0,1,1001)';
25 | x = (1 + 0.3*sin(5*2*pi*t)) .* cos(2*pi*t);
26 | y = (1 + 0.3*sin(5*2*pi*t)) .* sin(2*pi*t);
27 | ```
28 | Next, create a polygonal mesh:
29 | ```matlab
30 | Vtx = [x,y];
31 | Vtx = Vtx(1:end-1,:);
32 | Ind = (1:1:size(Vtx,1))';
33 | Edge = [Ind(1:end-1,1), Ind(2:end,1); Ind(end,1), Ind(1,1)];
34 | ```
35 | Then, create a Level Set object:
36 | ```matlab
37 | LS = Polygon_Mesh(Vtx,Edge);
38 | ```
39 |
40 | ## Mesh It!
41 |
42 | Create the 2-D Mesher:
43 | ```matlab
44 | Box_Dim = [-2, 2];
45 | Num_BCC_Points = 100;
46 | Use_Newton = false; % cannot use Newton Method here...
47 | TOL = 1E-3;
48 | MG = Mesher2Dmex(Box_Dim,Num_BCC_Points,Use_Newton,TOL);
49 | ```
50 |
51 | Next, we need to pass a function handle for the level set interpolation to the mesh generator. For this example, we do
52 | ```matlab
53 | % setup up handle to interpolation routine
54 | Interp_Handle = @(pt) LS.Interpolate(pt);
55 | ```
56 | In general, the user may provide their own interpolation routine that samples the level set function. The format of the function must be:
57 | ```matlab
58 | [phi, grad_phi] = Interp_Func(point);
59 | ```
60 | where
61 | ```matlab
62 | % phi = the level set function value
63 | % grad_phi = the gradient of the level set function
64 | % point = array of point coordinate to evaluate at
65 | ```
66 |
67 | Generate the mesh:
68 | ```matlab
69 | MG = MG.Get_Cut_Info(Interp_Handle);
70 | [TRI, VTX] = MG.run_mex(Interp_Handle);
71 | ```
72 |
73 | ## View It
74 |
75 | Plot the result:
76 | ```matlab
77 | p1 = trimesh(TRI,VTX(:,1),VTX(:,2),0*VTX(:,2));
78 | set(p1,'EdgeColor','k');
79 | axis equal;
80 | grid off;
81 | AX = 1.5*[-1 1 -1 1];
82 | axis(AX);
83 | ```
84 | It should look like this:
85 |
86 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Star_Mesh_Polygon.jpg|width=500|alt=Star Mesh]]
87 |
88 | # Example in 3-D
89 |
90 | We shall use the class `Surface_Mesh` located in
91 | ```matlab
92 | ./FELICITY/Static_Codes/Isosurface_Meshing/LevelSets_3D/@Surface_Mesh
93 | ```
94 | to create a 3-D mesh of the domain.
95 |
96 | ## Load Surface Mesh
97 |
98 | For simplicity, we will use a surface mesh that is included with FELICITY:
99 | ```matlab
100 | BT = load('Bumpy_Torus.mat','VTX','TRI');
101 | LS = Surface_Mesh(BT.VTX,BT.TRI);
102 | clear BT;
103 | ```
104 |
105 | ## Mesh It!
106 |
107 | Create the 3-D Mesher:
108 | ```matlab
109 | Box_Dim = [0, 1];
110 | Num_BCC_Points = 41;
111 | Use_Newton = false; % cannot use Newton Method here...
112 | TOL = 1E-2;
113 | MG = Mesher3Dmex(Box_Dim,Num_BCC_Points,Use_Newton,TOL);
114 | ```
115 | Generate the mesh:
116 | ```matlab
117 | % setup up handle to interpolation routine
118 | Interp_Handle = @(pt) LS.Interpolate(pt);
119 |
120 | MG = MG.Get_Cut_Info(Interp_Handle);
121 | [TET, VTX] = MG.run_mex(Interp_Handle);
122 | ```
123 |
124 | Extract the surface mesh of the tetrahedral mesh:
125 | ```matlab
126 | % create mesh object
127 | MT = MeshTetrahedron(TET, VTX, 'Bumpy Torus');
128 | FACES = MT.freeBoundary;
129 | ```
130 |
131 | ## View It
132 |
133 | Plot the result:
134 | ```matlab
135 | p1 = trimesh(FACES,VTX(:,1),VTX(:,2),VTX(:,3));
136 | set(p1,'EdgeColor','k');
137 | axis equal;
138 | grid off;
139 | AX = [0 1 0 1 0 1];
140 | axis(AX);
141 | AZ = -80;
142 | EL = 60;
143 | view(AZ,EL);
144 | grid on;
145 | title('Surface of Bumpy Torus Mesh');
146 | ```
147 | It should look like this:
148 |
149 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Bumpy_Torus_Mesh_Surface.jpg|width=670|alt=Bumpy Torus]]
150 |
151 | You can also plot it with some fancy lighting and shading:
152 | ```matlab
153 | % make a nice plot of surface
154 | figure;
155 | p2 = patch('Vertices', MT.Points, 'Faces', FACES);
156 | PURPLE = 2.5*[36 0 84]/255; % brighten it
157 | set(p2,'FaceColor',PURPLE,'EdgeColor','none');
158 | %set(p2,'FaceColor','magenta','EdgeColor','none');
159 | daspect([1,1,1])
160 | view(3); axis tight
161 | AZ1 = -130;
162 | EL1 = 30;
163 | view(AZ1,EL1);
164 | AZ2 = -80;
165 | EL2 = 10;
166 | camlight(AZ2,EL2);
167 | lighting gouraud;
168 | axis equal;
169 | axis(AX);
170 | ```
171 | It should look like this:
172 |
173 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Bumpy_Torus_Mesh_Surface_With_Lighting.jpg|width=670|alt=Bumpy Torus With Lighting]]
174 |
--------------------------------------------------------------------------------
/tutorials/Mesh_Smoothing_1.md:
--------------------------------------------------------------------------------
1 | Smoothing Meshes
2 | ================
3 |
4 | # Introduction
5 |
6 | Some types of finite element computations require one to move the mesh, i.e. the positions of vertices of the mesh are changed. This could be for a time-dependent ALE (Arbitrary-Lagrangian-Eulerian) method.
7 |
8 | Because of this, meshes may become distorted with "bad" element qualities. Usually this means the angles of the elements (triangles or tetrahedrons) are close to being degenerate (near 0 or 180 degrees). For reasons of accuracy and conditioning, it is best to avoid such degenerate elements.
9 |
10 | # Mesh Smoothing by Minimizing E_ODT
11 |
12 | FELICITY provides an implementation of a mesh smoother based on the method described in:
13 |
14 | Alliez, P.; Cohen-Steiner, D.; Yvinec, M. & Desbrun, M. "Variational Tetrahedral Meshing," ACM Trans. Graph., ACM, 2005, 24, 617-625
15 |
16 | and
17 |
18 | Chen, L. "Mesh smoothing schemes based on optimal Delaunay triangulations," 13th International Meshing Roundtable, Sandia National Laboratories, 2004, 109-120
19 |
20 | # Example: Smooth Deformation
21 |
22 | At the MATLAB prompt (or in a script file) type the following:
23 | ```matlab
24 | % create mesh of unit square
25 | N_pts = 30+1;
26 | [Elem, Vtx] = bcc_triangle_mesh(N_pts,N_pts);
27 | TR = triangulation(Elem,Vtx);
28 | fB = TR.freeBoundary;
29 | Bdy_Vtx_Indices = unique(fB(:));
30 | Vtx_Attach = TR.vertexAttachments;
31 | clear TR;
32 |
33 | disp('Number of Vertices and Elements:');
34 | [size(Vtx,1), size(Elem,1)]
35 | ```
36 |
37 | Now we apply a smooth deformation to the mesh vertices:
38 | ```matlab
39 | % deform mesh
40 | STD = 0.1;
41 | %Gauss = @(x,y) exp(-(x.^2 + y.^2)/STD);
42 | Gauss_x = @(xx) -(2/STD) * (xx(:,1) - 0.5) .* exp(-((xx(:,1) - 0.5).^2 + (xx(:,2) - 0.5).^2)/STD);
43 | Gauss_y = @(xx) -(2/STD) * (xx(:,2) - 0.5) .* exp(-((xx(:,1) - 0.5).^2 + (xx(:,2) - 0.5).^2)/STD);
44 | displace = (0.5*STD) * [Gauss_x(Vtx), Gauss_y(Vtx)];
45 | % set the displacement to zero on the boundary
46 | displace(Bdy_Vtx_Indices,1) = 0;
47 | displace(Bdy_Vtx_Indices,2) = 0;
48 | Vtx_displace = Vtx + displace;
49 | ```
50 |
51 | To view the mesh, plot it with these commands:
52 | ```matlab
53 | figure;
54 | subplot(1,2,1);
55 | trimesh(Elem,Vtx_displace(:,1),Vtx_displace(:,2),0*Vtx_displace(:,2));
56 | view(2);
57 | AX = [0 1 0 1];
58 | axis(AX);
59 | axis equal;
60 | axis(AX);
61 | title('Deformed Mesh');
62 | ```
63 |
64 | Next, we smooth the mesh by calling the sub-routine `FEL_Mesh_Smooth`:
65 | ```matlab
66 | % optimize mesh vertices
67 | Num_Sweeps = 30;
68 | Vtx_Indices_To_Update = setdiff((1:1:size(Vtx_displace,1))',Bdy_Vtx_Indices);
69 | Vtx_Smooth_1 = FEL_Mesh_Smooth(Vtx_displace,Elem,Vtx_Attach,Vtx_Indices_To_Update,Num_Sweeps);
70 | ```
71 | Note: calling `FEL_Mesh_Smooth` requires you to give it the mesh connectivity (and initial vertex positions), the elements attached to each vertex (see `Vtx_Attach`), a list of vertex indices to actually move, and the number of times to loop through the vertex list. In the example above, we did *not* update the vertices on the boundary of the mesh.
72 |
73 | Finally, we plot the smoothed (optimized) mesh:
74 | ```matlab
75 | subplot(1,2,2);
76 | trimesh(Elem,Vtx_Smooth_1(:,1),Vtx_Smooth_1(:,2),0*Vtx_Smooth_1(:,2));
77 | view(2);
78 | axis(AX);
79 | axis equal;
80 | axis(AX);
81 | title('Smoothed Mesh After 30 Gauss-Seidel Iterations');
82 | ```
83 | Notice that it took 30 iterations to achieve a reasonably uniform mesh. This is because the Gauss-Seidel method is used.
84 |
85 | # Example: Random Perturbation
86 |
87 | Now lets apply a random displacement of the vertex positions:
88 | ```matlab
89 | % perturb mesh
90 | displace = 0.5 * (1/N_pts) * (rand(size(Vtx,1),2) - 0.5);
91 | % set the displacement to zero on the boundary
92 | displace(Bdy_Vtx_Indices,1) = 0;
93 | displace(Bdy_Vtx_Indices,2) = 0;
94 | Vtx_perturb = Vtx + displace;
95 | ```
96 | and plot it:
97 | ```matlab
98 | figure;
99 | subplot(1,2,1);
100 | trimesh(Elem,Vtx_perturb(:,1),Vtx_perturb(:,2),0*Vtx_perturb(:,2));
101 | view(2);
102 | AX = [0 1 0 1];
103 | axis(AX);
104 | axis equal;
105 | axis(AX);
106 | title('Perturbed Mesh');
107 | ```
108 |
109 | Next, smooth the mesh:
110 | ```matlab
111 | % optimize mesh vertices
112 | Num_Sweeps = 2;
113 | Vtx_Smooth_2 = FEL_Mesh_Smooth(Vtx_perturb,Elem,Vtx_Attach,Vtx_Indices_To_Update,Num_Sweeps);
114 | ```
115 | and plot it:
116 | ```matlab
117 | subplot(1,2,2);
118 | trimesh(Elem,Vtx_Smooth_2(:,1),Vtx_Smooth_2(:,2),0*Vtx_Smooth_2(:,2));
119 | view(2);
120 | axis(AX);
121 | axis equal;
122 | axis(AX);
123 | title('Smoothed Mesh After 2 Gauss-Seidel Iterations');
124 | ```
125 | Notice the smoothed mesh looks very nice even though only two iterations were used. This is because the Gauss-Seidel method is very effective at removing high frequency components from the solution (which a random perturbation necessarily has).
126 |
127 | Note: this method is also implemented for 3-D tetrahedral meshes. The routine `FEL_Mesh_Smooth` is called in _exactly_ the same way.
128 |
129 | This tutorial can be found in ".\Demo\Mesh_Smoothing_2D" sub-directory of FELICITY.
--------------------------------------------------------------------------------
/tutorials/Quadtree_Example_1.md:
--------------------------------------------------------------------------------
1 | Quadtree Implementation
2 | =======================
3 |
4 | # Introduction
5 |
6 | FELICITY provides implementations of binary trees, quadtrees, and octrees for storing points distributed in space. This can be used to query nearest neighbors, and find closest points.
7 |
8 | # Quadtree Example (2-D)
9 |
10 | Use a quadtree to efficiently find k nearest neighbors.
11 |
12 | ## Initialization
13 |
14 | After you have installed FELICITY, make sure you run `test_FELICITY.m` or `compile_static_codes.m`. This will compile the C++ code that implements the search tree.
15 |
16 | Let us store some random 2-D points in a quadtree. Type the following at the MATLAB prompt (or put it in a script file):
17 | ```matlab
18 | % create a set of random 2-D points inside the unit square [0,1] x [0,1]
19 | NUM = 10000;
20 | points = rand(NUM,2);
21 | ```
22 |
23 | Next, create a Quadtree object to store these points. This requires a bounding box that *contains* the points.
24 | ```matlab
25 | % create Quadtree...
26 | BB = [-0.001, 1.001, -0.001, 1.001];
27 | Max_Tree_Levels = 32;
28 | Bucket_Size = 20;
29 | QT = mexQuadtree(points,BB);%,Max_Tree_Levels,Bucket_Size);
30 | clear points;
31 | ```
32 | Note: one can provide additional arguments specifying the max tree depth and the bucket size (max number of points to store in each leaf cell).
33 |
34 | Note: `QT.Points` contains a copy of the matrix `points`.
35 |
36 | This creates a MATLAB object that runs the quadtree and interfaces with a C++ class object.
37 |
38 | ## Query Nearest Neighbors
39 |
40 | Next, for each point in a given set of query points, we want to find the k nearest neighbors amongst the points we stored in the quadtree. Thus, define some random query points:
41 | ```matlab
42 | QP_Num = 100;
43 | query_points = rand(QP_Num,2);
44 | ```
45 |
46 | Now compute the 2 nearest neighbors for each query point:
47 | ```matlab
48 | NN = 2;
49 | [QT_indices, QT_dist] = QT.kNN_Search(query_points,NN);
50 | ```
51 | Here, the ith row of `QT_indices` and `QT_dist` corresponds to the ith row of `query_points`. The number of columns of `QT_indices` and `QT_dist` is 2; the first column corresponds to the closest neighbor, the second column corresponds to the next closest neighbor.
52 |
53 | `QT_indices` lists row indices (into `QT.Points`), i.e. this indicates which of the points in `QT.Points` are the nearest neighbors.
54 |
55 | For example, `QT_indices(i,:)` gives the two nearest neighbors (in `QT.Points`) to the ith point in `query_points`.
56 |
57 | `QT_dist` gives the euclidean distance to the nearest neighbors.
58 |
59 | ## Change The Point Positions
60 |
61 | Suppose you want to change the coordinates of the points stored in the quadtree. This could be because the points are moving in a simulation. Here is how to do it.
62 | ```matlab
63 | % generate new points
64 | new_pts = rand(NUM,2);
65 | % Update the tree with brand new points
66 | QT = QT.Update_Tree(new_pts);
67 | clear new_pts;
68 | ```
69 | Note: `new_pts` must have the same `size` as `QT.Points`.
70 |
71 | Now recompute the nearest neighbors:
72 | ```matlab
73 | [QT_indices, QT_dist] = QT.kNN_Search(query_points,NN);
74 | ```
75 |
76 | Make a plot of the 2 nearest neighbors to `query_points(1,:)`:
77 | ```matlab
78 | figure;
79 | plot(QT.Points(:,1),QT.Points(:,2),'k*');
80 | hold on;
81 | plot(query_points(1,1),query_points(1,2),'bp');
82 | plot(QT.Points(QT_indices(1,:),1),QT.Points(QT_indices(1,:),2),'r.','MarkerSize',20);
83 | hold off;
84 | axis([0 1 0 1]);
85 | axis equal;
86 | ```
87 |
88 | ## Final Cleanup
89 |
90 | When you are done with the object, you *must* delete it:
91 | ```matlab
92 | % delete the C++ object
93 | delete(QT);
94 | ```
95 |
96 | # Conclusion
97 |
98 | See the unit tests in the directory:
99 | ```matlab
100 | ./FELICITY/Static_Codes/Search_Trees/Unit_Test/
101 | ```
102 | for more information and examples.
--------------------------------------------------------------------------------
/tutorials/Solve_Laplaces_Eqn_1.md:
--------------------------------------------------------------------------------
1 | Solving Laplace's Equation in 1-D
2 | =================================
3 |
4 | # Introduction
5 |
6 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Laplace_Var_Form.jpg|width=650|alt=Variational Form of Laplace's Eqn]]
7 |
8 | To solve this numerically, we need to create the discrete operators (i.e. matrices). The philosophy behind FELICITY is to first define the bilinear forms (variational representation of the differential operators) via a user specified input file. Then FELICITY *automatically generates* a special purpose C++/MEX file and compiles it. This becomes an executable file that you can call directly from MATLAB.
9 |
10 | # Input File
11 |
12 | In the MATLAB editor, create the following m-function and name it `Example_1D.m`:
13 |
14 | ```matlab
15 | function MATS = Example_1D()
16 |
17 | % define domain (1-D)
18 | Omega = Domain('interval');
19 |
20 | % define piecewise linear finite element space
21 | Scalar_P1 = Element(Omega,lagrange_deg1_dim1,1);
22 |
23 | v = Test(Scalar_P1); % test function
24 | u = Trial(Scalar_P1); % trial function
25 |
26 | Stiff_Matrix = Bilinear(Scalar_P1,Scalar_P1); % stiffness matrix
27 | I1 = Integral(Omega,v.grad' * u.grad);
28 | Stiff_Matrix = Stiff_Matrix.Add_Integral(I1);
29 |
30 | RHS = Linear(Scalar_P1);
31 | RHS = RHS + Integral(Omega,-1.0 * v.val);
32 |
33 | Quadrature_Order = 3; % order of accuracy for the quad rule
34 | G1 = GeoElement(Omega); % define geometric representation of the domain
35 |
36 | MATS = Matrices(Quadrature_Order,G1); % collect all matrices together
37 | MATS = MATS.Append_Matrix(Stiff_Matrix);
38 | MATS = MATS.Append_Matrix(RHS);
39 |
40 | end
41 | ```
42 |
43 | Don't worry about what it means just yet (the PDF manual explains more).
44 |
45 | # Compile It!
46 |
47 | Put the file `Example_1D.m` into a directory that is *in your MATLAB path*. Now compile it by typing the following command at the MATLAB prompt and press "ENTER":
48 |
49 | ```matlab
50 | Convert_Form_Definition_to_MEX(@Example_1D,{},'Assemble_1D');
51 | ```
52 |
53 | Here we named the executable `Assemble_1D` (see next section).
54 |
55 | # Run It!
56 |
57 | We will solve the 1-D Laplace problem. First, type the following commands at the MATLAB prompt:
58 |
59 | ```matlab
60 | >> Num_Vertices = 101;
61 | >> Indices = (1:1:Num_Vertices)';
62 | >> Omega_Mesh = uint32([Indices(1:end-1,1), Indices(2:end,1)]);
63 | >> Omega_Vertices = linspace(0,1,Num_Vertices)';
64 | ```
65 |
66 | This defines a mesh that partitions [0, 1] into 100 sub-intervals.
67 |
68 | Next, we need the local-to-global Degree-of-Freedom map (DoF map) for the finite element space. Since we are using piecewise linear continuous basis functions, we can simply type:
69 |
70 | ```matlab
71 | >> P1_DoFmap = Omega_Mesh;
72 | ```
73 |
74 | Now assemble the matrix representations of the bilinear (and linear) forms by executing:
75 |
76 | ```matlab
77 | >> FEM = Assemble_1D([],Omega_Vertices,Omega_Mesh,[],[],P1_DoFmap);
78 | ```
79 |
80 | `FEM` is a MATLAB struct that contains the sparse matrices in alphabetical order based on the names used in the `Example_1D.m` file. Finally, we obtain the solution of the PDE by the following commands:
81 |
82 | ```matlab
83 | >> A = FEM(2).MAT; % stiffness matrix
84 |
85 | >> Soln = zeros(size(A,1),1); % init
86 |
87 | >> alpha = 0;
88 | >> RHS = FEM(1).MAT;
89 | >> RHS(end) = RHS(end) + alpha; % add in the Neumann condition at s = 1
90 |
91 | >> % impose Dirichlet condition at s=0
92 | >> Soln(2:end) = A(2:end,2:end) \ RHS(2:end);
93 | ```
94 |
95 | You can plot the solution with the following commands:
96 |
97 | ```matlab
98 | >> figure;
99 | >> h1 = plot(Omega_Vertices,Soln,'b-','LineWidth',2.0);
100 | >> title('Solution of PDE: -d^2/ds^2 u = -1, u(0) = 0, d/ds u(1) = 0','FontSize',14);
101 | >> xlabel('x (domain = [0, 1])','FontSize',14);
102 | >> ylabel('u (solution value)','FontSize',14);
103 | >> set(gca,'FontSize',14);
104 | >> axis equal;
105 | ```
106 |
107 | For more information, see the *Assembling Matrices* chapter in the PDF manual.
--------------------------------------------------------------------------------
/tutorials/Solve_Simple_Elasticity_3D_1.md:
--------------------------------------------------------------------------------
1 | Solve Laplace's Equation in 3-D
2 | ===============================
3 |
4 | # Weak Formulation:
5 |
6 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Simple_Elasticity_3D_weak_formulation.jpg|width=700|alt=Weak Form 3-D Laplace]]
7 |
8 | Note: this is a simple model of elasticity in 3-D.
9 |
10 | # Input File
11 |
12 | In the MATLAB editor, create the following m-function and name it `MatAssem_Simple_Elasticity_3D.m`:
13 |
14 | ```matlab
15 | function MATS = MatAssem_Simple_Elasticity_3D()
16 |
17 | % define domain (3-D volume)
18 | Omega = Domain('tetrahedron'); % ``elastic'' domain
19 | Sigma = Domain('interval') < Omega; % edge of \Omega
20 |
21 | % define finite element spaces
22 | Vector_P1 = Element(Omega,lagrange_deg1_dim3,3); % 3-D vector valued
23 |
24 | % define functions on FE spaces
25 | vv = Test(Vector_P1);
26 | uu = Trial(Vector_P1);
27 |
28 | Displace = Coef(Vector_P1);
29 |
30 | % define geometric function on \Sigma
31 | gSigma = GeoFunc(Sigma);
32 |
33 | % define FEM matrices
34 | Stiff_Matrix = Bilinear(Vector_P1,Vector_P1);
35 | Stiff_Matrix = Stiff_Matrix + Integral(Omega,sum(sum(vv.grad .* uu.grad)));
36 |
37 | Eval_Matrix = Real(1,1);
38 | Eval_Matrix = Eval_Matrix + Integral(Sigma, Displace.val' * gSigma.T );
39 |
40 | % set the minimum order of accuracy for the quad rule
41 | Quadrature_Order = 3;
42 | % define geometry representation - Domain, (default to piecewise linear)
43 | G1 = GeoElement(Omega);
44 |
45 | % collect all of the matrices together
46 | MATS = Matrices(Quadrature_Order,G1);
47 | MATS = MATS.Append_Matrix(Eval_Matrix);
48 | MATS = MATS.Append_Matrix(Stiff_Matrix);
49 |
50 | end
51 | ```
52 |
53 | Here we define the main bilinear form and we define the integral quantity in the "matrix" `Eval_Matrix`. Note that the finite element space is vector-valued. We also introduced a geometric function object (for the sub-domain \Sigma) gSigma. The tangent vector of \Sigma is accessed by gSigma.T.
54 |
55 | # Compile It!
56 |
57 | Put the file `MatAssem_Simple_Elasticity_3D.m` into a directory that is *in your MATLAB path*. Now compile it by typing the following command at the MATLAB prompt and press "ENTER":
58 |
59 | ```matlab
60 | Convert_Form_Definition_to_MEX(@MatAssem_Simple_Elasticity_3D,{},'Assemble_Simple_Elasticity_3D');
61 | ```
62 |
63 | Here we named the executable `Assemble_Simple_Elasticity_3D` (see next section).
64 |
65 | # Run It!
66 |
67 | We will solve the 3-D Laplace problem. First, type the following commands at the MATLAB prompt (or put them into a separate script file):
68 |
69 | ```matlab
70 | % create mesh for elastic column
71 | [Omega_Tet, Omega_Vertex] = regular_tetrahedral_mesh(2+1,2+1,10+1);
72 | % scale it
73 | Omega_Vertex(:,1:2) = (1/5) * Omega_Vertex(:,1:2);
74 | Mesh = MeshTetrahedron(Omega_Tet, Omega_Vertex, 'Omega');
75 | % plot it
76 | Mesh.Plot;
77 | ```
78 |
79 | This defines a tetrahedral mesh for \Omega.
80 |
81 | Next, define various sub-domains of \Omega:
82 | ```matlab
83 | % \partial \Omega
84 | Bdy_Faces = Mesh.freeBoundary();
85 | Mesh = Mesh.Append_Subdomain('2D','Bdy',Bdy_Faces);
86 | % get centroids of boundary triangles
87 | FF_center = (1/3) * (Mesh.Points(Bdy_Faces(:,1),:) + Mesh.Points(Bdy_Faces(:,2),:) + Mesh.Points(Bdy_Faces(:,3),:));
88 | % find faces on top
89 | Top_Mask = (FF_center(:,3) > 1 - 1e-5);
90 | Top_Faces = Bdy_Faces(Top_Mask,:);
91 | Mesh = Mesh.Append_Subdomain('2D','Top',Top_Faces);
92 | % find faces on bottom
93 | Bot_Mask = (FF_center(:,3) < 0 + 1e-5);
94 | Bot_Faces = Bdy_Faces(Bot_Mask,:);
95 | Mesh = Mesh.Append_Subdomain('2D','Bottom',Bot_Faces);
96 | ```
97 |
98 | Also, define the sub-domain \Sigma:
99 | ```matlab
100 | % find vertices on the edge segment: (0,0,1) - (0,0,0)
101 | Col_Edge_Vtx_Mask = (Mesh.Points(:,1) < 0 + 1e-5) & (Mesh.Points(:,2) < 0 + 1e-5);
102 | Col_Edge_Vtx = Mesh.Points(Col_Edge_Vtx_Mask,:);
103 | All_Vtx_Indices = (1:1:Mesh.Num_Vtx)';
104 | Col_Edge_Vtx_Indices = All_Vtx_Indices(Col_Edge_Vtx_Mask);
105 | [Col_Edge_Vtx, I1] = sortrows(Col_Edge_Vtx,3); % sort in ascending order along z
106 | Col_Edge_Vtx_Indices = Col_Edge_Vtx_Indices(I1);
107 | % define the column edges
108 | Col_Edge_Data = [Col_Edge_Vtx_Indices(1:end-1,1), Col_Edge_Vtx_Indices(2:end,1)];
109 | Mesh = Mesh.Append_Subdomain('1D','Sigma',Col_Edge_Data);
110 | ```
111 |
112 | Next, we need the local-to-global Degree-of-Freedom map (DoF map) for the finite element space. Since we are using piecewise linear continuous basis functions, we can simply type:
113 | ```matlab
114 | P1_DoFmap = uint32(Mesh.ConnectivityList);
115 | ```
116 |
117 | Define the values of a vector-valued `Displace' coefficient function:
118 | ```matlab
119 | Displace = 0*Mesh.Points; % the zero function
120 | ```
121 |
122 | Create sub-domain embedding data structures. This is so the matrix assembly code knows how \Sigma is embedded in \Omega:
123 | ```matlab
124 | DoI_Names = {'Omega'; 'Sigma'}; % domains of integration
125 | Subdomain_Embed = Mesh.Generate_Subdomain_Embedding_Data(DoI_Names);
126 | ```
127 |
128 | Now assemble the matrices:
129 |
130 | ```matlab
131 | FEM = Assemble_Simple_Elasticity_3D([],Mesh.Points,uint32(Mesh.ConnectivityList),[],Subdomain_Embed,P1_DoFmap,Displace);
132 | ```
133 |
134 | `FEM` is a MATLAB struct that contains the sparse matrices. Finally, we obtain the solution of the PDE by the following commands.
135 |
136 | Initialize the solution and set boundary conditions:
137 | ```matlab
138 | Soln = zeros(Mesh.Num_Vtx,3); % init the solution
139 | A = FEM(2).MAT;
140 | % Degree-of-Freedom associated with the Top sub-domain
141 | Top_Nodes = unique(Top_Faces(:));
142 | % set Dirichlet conditions
143 | Soln(Top_Nodes,1) = 0.2;
144 | Soln(Top_Nodes,2) = 0.2;
145 | Soln(Top_Nodes,3) = 0.1;
146 | Soln = Soln(:); % collapse to a single column vector
147 | RHS = 0*Soln;
148 | RHS = RHS - A * Soln; % modify right hand side
149 | % get bottom DoF
150 | Bot_Nodes = unique(Bot_Faces(:));
151 | % bottom nodes are already set to zero
152 |
153 | % get the free nodes of the system (for each component)
154 | FreeNodes_X = setdiff(All_Vtx_Indices,[Top_Nodes; Bot_Nodes]);
155 | FreeNodes_Y = [FreeNodes_X + Mesh.Num_Vtx];
156 | FreeNodes_Z = [FreeNodes_Y + Mesh.Num_Vtx];
157 | % combine them all into one array
158 | FreeNodes = [FreeNodes_X; FreeNodes_Y; FreeNodes_Z];
159 | ```
160 |
161 | Now solve it:
162 | ```matlab
163 | Soln(FreeNodes,1) = A(FreeNodes,FreeNodes) \ RHS(FreeNodes,1);
164 | Displace = zeros(Mesh.Num_Vtx,3);
165 | Displace(:) = Soln; % put displacement solution back into 3-D vector form
166 | ```
167 |
168 | Next, compute the integral quantity
169 | ```matlab
170 | Recalc_FEM = Assemble_Simple_Elasticity_3D([],Mesh.Points,uint32(Mesh.ConnectivityList),...
171 | [],Subdomain_Embed,P1_DoFmap,Displace);
172 |
173 | % value of this 1x1 matrix should be 0.05
174 | Integral_Quantity = Recalc_FEM(1).MAT;
175 | ```
176 |
177 | # Plot It
178 |
179 | ```matlab
180 | figure;
181 | subplot(1,2,1);
182 | h1 = Mesh.Plot;
183 | title('Reference Mesh','FontSize',14);
184 | set(gca,'FontSize',14);
185 | AX = [0 0.4 0 0.4 0 1.2];
186 | axis(AX);
187 | axis equal;
188 | axis(AX);
189 |
190 | subplot(1,2,2);
191 | New_Mesh = Mesh.Set_Points(Mesh.Points + Displace); % make a new displaced mesh
192 | h2 = New_Mesh.Plot;
193 | title('Deformed Mesh','FontSize',14);
194 | set(gca,'FontSize',14);
195 | axis(AX);
196 | axis equal;
197 | axis(AX);
198 | ```
199 |
200 | For more information, see the PDF manual and the ".\Demo\Simple_Elasticity_3D\" sub-directory of FELICITY.
--------------------------------------------------------------------------------
/tutorials/Solve_Stokes_2D_1.md:
--------------------------------------------------------------------------------
1 | Solve the Stokes Equations in 2-D
2 | =================================
3 |
4 | # Weak Formulation:
5 |
6 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Demo_Stokes_2D_weak_formulation.jpg|width=800|alt=Weak Form 2-D Stokes Eqn]]
7 |
8 | We will use a mixed finite element method to approximate the solution to this problem, i.e. we have two finite element spaces of different degree. One for the velocity field and one for the pressure field.
9 |
10 | The pressure space will be approximated by a scalar piecewise linear continuous Lagrange space; we did something similar in [Solve Simple Elasticity Model](../wiki/Solve_Simple_Elasticity_3D_1) for the 3-D case.
11 |
12 | However, because pressure is approximated by degree 1 polynomials, we must approximate velocity by degree 2 polynomials (piecewise quadratic). This is to satisfy the LBB (stability) condition.
13 |
14 | # Allocate DoFs for Piecewise Quadratic Polynomials
15 |
16 | You should do the tutorial [Allocating Degrees-of-Freedom (DoFs)](../wiki/Allocate_DoFs_1) before proceeding.
17 |
18 | Allocating the Degrees-of-Freedom (DoFs) for the piecewise quadratic space is not as easy as for the piecewise linear case. This is because some DoFs are associated with vertices, and others are associated with edges of the mesh. You can certainly write a MATLAB code to do this, but it is simpler to just let FELICITY do it for you.
19 |
20 | Type the following at the MATLAB prompt (or put it in a script file):
21 | ```matlab
22 | Main_Dir = 'C:\Your_Favorite_Directory\';
23 | Elem = lagrange_deg2_dim2(); % piecewise quadratic
24 | Create_DoF_Allocator(Elem,'mex_DoF_Lagrange_P2_Allocator_2D',Main_Dir);
25 | ```
26 |
27 | Here we named the executable `mex_DoF_Lagrange_P2_Allocator_2D`. See [Allocating Degrees-of-Freedom (DoFs)](../wiki/Allocate_DoFs_1) for more details.
28 |
29 | # Input File
30 |
31 | In the MATLAB editor, create the following m-function and name it `MatAssem_Stokes_2D.m`:
32 |
33 | ```matlab
34 | function MATS = MatAssem_Stokes_2D()
35 |
36 | % define domain (2-D)
37 | Omega = Domain('triangle'); % fluid domain (unit square)
38 | Outlet = Domain('interval') < Omega; % right side of \Omega
39 |
40 | % define finite element spaces
41 | Vector_P2 = Element(Omega,lagrange_deg2_dim2,2); % 2-D vector valued
42 | Scalar_P1 = Element(Omega,lagrange_deg1_dim2,1);
43 |
44 | % define functions on FE spaces
45 | vv = Test(Vector_P2);
46 | uu = Trial(Vector_P2);
47 |
48 | q = Test(Scalar_P1);
49 | p = Trial(Scalar_P1);
50 |
51 | BC_Out = Coef(Vector_P2);
52 |
53 | % define FEM matrices
54 | BC_Matrix = Linear(Vector_P2);
55 | BC_Matrix = BC_Matrix + Integral(Outlet, BC_Out.val' * vv.val );
56 |
57 | Div_Pressure = Bilinear(Scalar_P1,Vector_P2);
58 | Div_Pressure = Div_Pressure + Integral(Omega,-q.val * (uu.grad(1,1) + uu.grad(2,2)));
59 |
60 | Stress_Matrix = Bilinear(Vector_P2,Vector_P2);
61 | % symmetrized gradient
62 | D_u = uu.grad + uu.grad';
63 | Stress_Matrix = Stress_Matrix + Integral(Omega,sum(sum(D_u .* vv.grad)));
64 |
65 | % set the minimum order of accuracy for the quad rule
66 | Quadrature_Order = 4;
67 | % define geometry representation - Domain, (default to piecewise linear)
68 | G1 = GeoElement(Omega);
69 | % define a set of matrices
70 | MATS = Matrices(Quadrature_Order,G1);
71 |
72 | % collect all of the matrices together
73 | MATS = MATS.Append_Matrix(BC_Matrix);
74 | MATS = MATS.Append_Matrix(Div_Pressure);
75 | MATS = MATS.Append_Matrix(Stress_Matrix);
76 |
77 | end
78 | ```
79 |
80 | The space `Vector_P2` is for the velocity field. `Scalar_P1` is for the pressure.
81 |
82 | The Linear form `BC_Matrix` represents the stress boundary condition. The `Stress_Matrix` is a symmetric Bilinear form and is the first diagonal block of the saddle point system. `Div_Pressure` is a non-symmetric Bilinear form and is the off-diagonal block that captures the divergence free condition.
83 |
84 | `BC_Out` is a coefficient function that will contain the stress boundary condition.
85 |
86 | # Compile It!
87 |
88 | Put the file `MatAssem_Stokes_2D.m` into a directory that is *in your MATLAB path*. Compile it by running:
89 | ```matlab
90 | Convert_Form_Definition_to_MEX(@MatAssem_Stokes_2D,{},'mex_Stokes_2D_assemble');
91 | ```
92 |
93 | Here we named the executable `mex_Stokes_2D_assemble`. See the tutorial [Solve Laplace's Equation](../wiki/Solve_Laplaces_Eqn_1) for more info on this compilation step.
94 |
95 | # Run It!
96 |
97 | We will solve the 2-D Stokes problem. First, type the following commands at the MATLAB prompt (or put them into a separate script file):
98 |
99 | ```matlab
100 | % define square mesh [0, 1] x [0, 1]
101 | [Omega_Tri, Omega_Vertex] = bcc_triangle_mesh(5,5);
102 | Mesh = MeshTriangle(Omega_Tri, Omega_Vertex, 'Omega');
103 | clear Omega_Tri Omega_Vertex;
104 | Mesh = Mesh.Refine;
105 | % plot it
106 | Mesh.Plot;
107 | ```
108 |
109 | Next, define various sub-domains of \Omega:
110 | ```matlab
111 | % define \partial \Omega
112 | Bdy_Edges = Mesh.freeBoundary();
113 | Mesh = Mesh.Append_Subdomain('1D','Bdy',Bdy_Edges);
114 |
115 | % get centroids of boundary edges
116 | EE_center = (1/2) * (Mesh.Points(Bdy_Edges(:,1),:) + Mesh.Points(Bdy_Edges(:,2),:));
117 |
118 | % find edges on top
119 | Top_Mask = (EE_center(:,2) > 1 - 1e-5);
120 | Top_Edges = Bdy_Edges(Top_Mask,:);
121 | Mesh = Mesh.Append_Subdomain('1D','Top',Top_Edges);
122 | % find edges on bottom
123 | Bot_Mask = (EE_center(:,2) < 0 + 1e-5);
124 | Bot_Edges = Bdy_Edges(Bot_Mask,:);
125 | Mesh = Mesh.Append_Subdomain('1D','Bottom',Bot_Edges);
126 | % find edges on inlet
127 | In_Mask = (EE_center(:,1) < 0 + 1e-5);
128 | In_Edges = Bdy_Edges(In_Mask,:);
129 | Mesh = Mesh.Append_Subdomain('1D','Inlet',In_Edges);
130 | % find edges on outlet
131 | Out_Mask = (EE_center(:,1) > 1 - 1e-5);
132 | Out_Edges = Bdy_Edges(Out_Mask,:);
133 | Mesh = Mesh.Append_Subdomain('1D','Outlet',Out_Edges);
134 | ```
135 |
136 | Next, we need the local-to-global Degree-of-Freedom map (DoF map) for the finite element spaces. Since we are using piecewise linear continuous basis functions for the pressure, we can simply type:
137 | ```matlab
138 | Pressure_DoFmap = uint32(Mesh.ConnectivityList);
139 | ```
140 |
141 | For the velocity space, it will behoove us to use another FELICITY class `FiniteElementSpace`. Enter the following into MATLAB:
142 | ```matlab
143 | P2_RefElem = ReferenceFiniteElement(lagrange_deg2_dim2());
144 | P2_Lagrange_Space = FiniteElementSpace('Velocity', P2_RefElem, Mesh, 'Omega', 2); % 2 components
145 | ```
146 | This class allows us to extract DoFs that are attached to embedded sub-domains.
147 |
148 | Now, allocate the DoFs for the velocity field and store them in the `P2_Lagrange_Space` object:
149 | ```matlab
150 | Lag_P2_DoFmap = uint32(mex_DoF_Lagrange_P2_Allocator_2D(uint32(Mesh.ConnectivityList)));
151 | P2_Lagrange_Space = P2_Lagrange_Space.Set_DoFmap(Mesh,Lag_P2_DoFmap);
152 | ```
153 | Note: only the first component (x-component) of the velocity Degrees-of-Freedom (DoFs) are explicitly stored. Obtaining the y-component DoFs is easy: just shift the indices by the number of x-component nodes (see the PDE solve part of this tutorial).
154 |
155 | Create sub-domain embedding data structures. This is so the matrix assembly code knows how Outlet is embedded in \Omega:
156 | ```matlab
157 | DoI_Names = {'Omega'; 'Outlet'}; % domains of integration
158 | Subdomain_Embed = Mesh.Generate_Subdomain_Embedding_Data(DoI_Names);
159 | ```
160 |
161 | Next, define the stress boundary condition:
162 | ```matlab
163 | % get P2_Lagrange Node coordinates
164 | P2_X = P2_Lagrange_Space.Get_DoF_Coord(Mesh);
165 | % set stress boundary condition
166 | BC_Out_Values = 0*P2_X; % set to the zero vector
167 | % use the following if you want the stress boundary condition
168 | % to be (0, sin(2*pi*y))
169 | %BC_Out_Values(:,2) = sin(2*pi*P2_X(:,2));
170 | ```
171 |
172 | Now assemble the matrices:
173 | ```matlab
174 | FEM = mex_Stokes_2D_assemble([],Mesh.Points,uint32(Mesh.ConnectivityList),[],Subdomain_Embed,...
175 | Pressure_DoFmap,P2_Lagrange_Space.DoFmap,BC_Out_Values);
176 | ```
177 | and put the `FEM` data into a special object to allow for easy access of the sparse matrix data:
178 | ```matlab
179 | Stokes_Matrices = FEMatrixAccessor('Stokes',FEM);
180 | ```
181 |
182 | Finally, we obtain the solution of the Stokes system by the following commands.
183 |
184 | Get DoFs:
185 | ```matlab
186 | Vel_DoFs = unique(P2_Lagrange_Space.DoFmap(:));
187 | Pressure_DoFs = unique(Pressure_DoFmap(:));
188 | Num_Scalar_Vel_DoF = length(Vel_DoFs);
189 | Num_Pressure_DoF = length(Pressure_DoFs);
190 | ```
191 | Note: `Vel_DoFs` refers to the x-component of the velocity. The y-component DoFs are obtained by shifting:
192 | ```matlab
193 | Vel_DoFs + Num_Scalar_Vel_DoF
194 | ```
195 |
196 | Initialize solution arrays:
197 | ```matlab
198 | Vel_Soln = zeros(Num_Scalar_Vel_DoF,2); % 2 columns <-> 2-D vector
199 | Pressure_Soln = zeros(Num_Pressure_DoF,1);
200 | ```
201 | Access finite element matrices:
202 | ```matlab
203 | A = Stokes_Matrices.Get_Matrix('Stress_Matrix');
204 | B = Stokes_Matrices.Get_Matrix('Div_Pressure');
205 | ```
206 | Note: by using `Stokes_Matrices`, we do not need to know the *order* of the matrices in the `FEM` struct. This is especially useful if you must modify the input file `MatAssem_Stokes_2D` to define *additional* bilinear or linear forms (matrices).
207 |
208 | Now setup the system matrix:
209 | ```matlab
210 | % create saddle point system
211 | Saddle = [A, B'; B, sparse(Num_Pressure_DoF,Num_Pressure_DoF)];
212 | ```
213 | Note: because of the way that we ordered the matrices here, the global pressure DoFs are actually
214 | ```matlab
215 | Pressure_DoFs + 2*Num_Scalar_Vel_DoF
216 | ```
217 |
218 | Set the boundary conditions:
219 | ```matlab
220 | % compute the parabolic profile over all velocity DoFs
221 | Parabolic_Profile = P2_X(:,2).*(1 - P2_X(:,2)); % y * (1 - y)
222 | % get the DoFs associated with the Inlet
223 | Inlet_DoFs = P2_Lagrange_Space.Get_DoFs_On_Subdomain(Mesh,'Inlet');
224 | % set the x-component of the velocity solution
225 | Vel_Soln(Inlet_DoFs,1) = Parabolic_Profile(Inlet_DoFs,1);
226 | % zero velocity on top and bottom is already set
227 |
228 | % put the velocity and pressure solution into a single column vector
229 | Soln = [Vel_Soln(:); Pressure_Soln];
230 | ```
231 |
232 | Set the right-hand-side data for the matrix system
233 | ```matlab
234 | % concatenate stress boundary condition and zero divergence data
235 | R1 = Stokes_Matrices.Get_Matrix('BC_Matrix');
236 | RHS = [R1; zeros(Num_Pressure_DoF,1)];
237 | % include boundary conditions
238 | RHS = RHS - Saddle * Soln;
239 | ```
240 |
241 | Eliminate the fixed DoFs of the system:
242 | ```matlab
243 | Top_DoFs = P2_Lagrange_Space.Get_DoFs_On_Subdomain(Mesh,'Top');
244 | Bot_DoFs = P2_Lagrange_Space.Get_DoFs_On_Subdomain(Mesh,'Bottom');
245 | % use unique b/c some of the nodes may be shared between Top and Inlet for example
246 | Fixed_Scalar_Nodes = unique([Inlet_DoFs; Top_DoFs; Bot_DoFs]);
247 | % get the global velocity nodes that are fixed (Dirichlet condition)
248 | Fixed_Vector_Nodes = [Fixed_Scalar_Nodes; Fixed_Scalar_Nodes + Num_Scalar_Vel_DoF];
249 | % get the remaining free nodes of the system
250 | FreeNodes = setdiff((1:1:length(Soln))', Fixed_Vector_Nodes);
251 | ```
252 |
253 | Now solve it:
254 | ```matlab
255 | % Use backslash to solve
256 | Soln(FreeNodes,1) = Saddle(FreeNodes,FreeNodes) \ RHS(FreeNodes,1);
257 | % now parse the solution back into the velocity and pressure variables
258 | Vel_Soln(:) = Soln(1:2*Num_Scalar_Vel_DoF,1);
259 | Pressure_Soln = Soln(2*Num_Scalar_Vel_DoF+1:end,1);
260 | ```
261 |
262 | # Plot It
263 |
264 | ```matlab
265 | figure;
266 | subplot(2,2,1);
267 | h1 = Mesh.Plot;
268 | title('Omega','FontSize',14);
269 | set(gca,'FontSize',14);
270 | AX = [0 1 0 1];
271 | axis(AX);
272 | axis equal;
273 | axis(AX);
274 |
275 | subplot(2,2,2);
276 | h2 = quiver(P2_X(:,1),P2_X(:,2),Vel_Soln(:,1),Vel_Soln(:,2));
277 | title('Vector Velocity Solution','FontSize',14);
278 | set(gca,'FontSize',14);
279 | axis(AX);
280 | axis equal;
281 | axis(AX);
282 |
283 | subplot(2,2,3);
284 | h3 = trisurf(Mesh.ConnectivityList,Mesh.Points(:,1),Mesh.Points(:,2),Pressure_Soln);
285 | shading interp;
286 | title('Pressure Solution','FontSize',14);
287 | set(gca,'FontSize',14);
288 | colorbar;
289 | axis(AX);
290 | axis equal;
291 | axis(AX);
292 | ```
293 |
294 | This tutorial can be found in ".\Demo\Stokes_2D" sub-directory of FELICITY.
--------------------------------------------------------------------------------
/tutorials/Tutorial_Interpolation_1.md:
--------------------------------------------------------------------------------
1 | Interpolating Finite Element Data
2 | =================================
3 |
4 | This is a tutorial on generating code to perform interpolation of finite element data.
5 |
6 | # Introduction
7 |
8 | [[https://github.com/walkersw/felicity-finite-element-toolbox/blob/master/images/Tutorial_Interpolation_Ex_P2.jpg|width=800|alt=Interpolating P2 Finite Element Data]]
9 |
10 | FELICITY can *automatically generate* a special purpose C++/MEX file and compile it for most interpolation purposes. This becomes an executable file that you can call directly from MATLAB.
11 |
12 | # Input File
13 |
14 | In the MATLAB editor, create the following m-function and name it `Interpolate_Grad_P_X_2D.m`:
15 |
16 | ```matlab
17 | function INTERP = Interpolate_Grad_P_X_2D()
18 |
19 | % define domain
20 | Omega = Domain('triangle');
21 |
22 | % define finite element spaces
23 | Scalar_P2 = Element(Omega,lagrange_deg2_dim2,1);
24 |
25 | % define functions on FE spaces
26 | p = Coef(Scalar_P2);
27 |
28 | % define geometric function on 'Omega' domain
29 | gf = GeoFunc(Omega);
30 |
31 | % define expressions to interpolate
32 | I_grad_p_X = Interpolate(Omega,p.grad' * gf.X);
33 |
34 | % define geometry representation - Domain, reference element
35 | G1 = GeoElement(Omega);
36 |
37 | % define a set of interpolations to perform
38 | INTERP = Interpolations(G1);
39 |
40 | % collect all of the interpolations together
41 | INTERP = INTERP.Append_Interpolation(I_grad_p_X);
42 |
43 | end
44 | ```
45 |
46 | Don't worry about what it means just yet (the PDF manual explains more; see Chapter 7).
47 |
48 | # Compile It!
49 |
50 | Put the file `Interpolate_Grad_P_X_2D.m` into a directory that is *in your MATLAB path*. Now compile it by typing the following command at the MATLAB prompt and press "ENTER":
51 |
52 | ```matlab
53 | Convert_Interp_Definition_to_MEX(@Interpolate_Grad_P_X_2D,{},'Interp_2D');
54 | ```
55 |
56 | Here we named the executable `Interp_2D` (see next section).
57 |
58 | # Run It!
59 |
60 | First, create the domain (triangulation) for the unit square. At the MATLAB prompt (or in a script) type the following commands:
61 | ```matlab
62 | % create mesh (Omega is the unit square)
63 | Vtx = [0 0; 1 0; 1 1; 0 1];
64 | Tri = [1 2 3; 1 3 4];
65 | Mesh = MeshTriangle(Tri, Vtx, 'Omega');
66 | ```
67 | i.e. the mesh consists of two triangles. Next, define the piecewise quadratic finite element space:
68 | ```matlab
69 | % define FE space
70 | P2_RefElem = ReferenceFiniteElement(lagrange_deg2_dim2());
71 | P2_Lagrange_Space = FiniteElementSpace('Scalar_P2', P2_RefElem, Mesh, 'Omega');
72 | P2_DoFmap = uint32(Setup_Lagrange_P2_DoFmap(Mesh.ConnectivityList,[]));
73 | P2_Lagrange_Space = P2_Lagrange_Space.Set_DoFmap(Mesh,P2_DoFmap);
74 | P2_X = P2_Lagrange_Space.Get_DoF_Coord(Mesh); % get coordinates of P2 DoFs
75 | ```
76 | See [Managing DoFs: Part 1](../wiki/Managing_DoFs_1) for more info on how the FiniteElementSpace class works.
77 |
78 | Set the coefficient function `p(x, y) = sin(x + y)`:
79 | ```matlab
80 | % define coefficient function
81 | p_func = @(x,y) sin(x + y);
82 | px_func = @(x,y) cos(x + y);
83 | py_func = @(x,y) cos(x + y);
84 | p_val = p_func(P2_X(:,1),P2_X(:,2)); % coefficient values
85 | ```
86 |
87 | Next, define an analytic function for the expression ∇p · x:
88 | ```matlab
89 | % exact interpolant function
90 | I_grad_p_X = @(x,y) px_func(x,y) .* x + py_func(x,y) .* y;
91 | ```
92 |
93 | ## Interpolate At Two Points
94 |
95 | Now, define the coordinates of the interpolation points. In this example, we will only have two interpolation points:
96 | ```matlab
97 | % define interpolation points
98 |
99 | % specify the triangle indices to interpolate within
100 | Tri_Indices = [1;
101 | 2];
102 | % specify interpolation coordinates w.r.t. reference triangle
103 | Ref_Coord = [(1/3), (1/3);
104 | (1/3), (1/3)];
105 | % collect into a cell array
106 | Omega_Interp_Data = {uint32(Tri_Indices), Ref_Coord};
107 | ```
108 |
109 | The format requires the interpolation point data to be stored in a MATLAB cell array:
110 | * The first element of Omega_Interp_Data is a MATLAB vector of unsigned integers, of length `R`, which are triangle indices. Note: in order to interpolate, you must specify the mesh elements to interpolate within.
111 | * The second element of Omega_Interp_Data is a MATLAB matrix of size `R×T`; `R` is the number of interpolation points and `T` is the topological dimension of the mesh element. In this example, `T = 2`.
112 |
113 | Now use the executable that was generated before. Run the following at the MATLAB prompt (or insert after the above code in a script):
114 | ```matlab
115 | INTERP = Interp_2D(Mesh.Points,uint32(Mesh.ConnectivityList),[],[],Omega_Interp_Data,P2_DoFmap,p_val);
116 | ```
117 | Recall that we named the MEX file `Interp_2D`. In general, if you try to run the MEX file with the incorrect number of arguments, it will give you an error message but it will also tell you what the inputs should be.
118 |
119 | `INTERP` is a MATLAB struct that contains the interpolation data in alphabetical order based on the names used in the `Interpolate_Grad_P_X_2D.m` file:
120 | ```matlab
121 | INTERP(1).Name : 'I_grad_p_X'
122 | INTERP(1).DATA : cell array containing interpolation data for the expression
123 | ```
124 |
125 | ## Interpolate At Many Points
126 |
127 | Interpolating two points is not so complicated. Lets create a grid of points on the unit square and interpolate at those:
128 | ```matlab
129 | % now interpolate at a lot more points
130 | x_vec = (0:0.1:1);
131 | y_vec = (0:0.1:1);
132 | [XX, YY] = meshgrid(x_vec,y_vec);
133 | Interp_Pts_2 = [XX(:), YY(:)];
134 | ```
135 |
136 | Remember that interpolating at a given point requires we know the mesh element that the point belongs to. Thus, we must identify which triangle the above grid points belong to:
137 | ```matlab
138 | % find which triangle the points belong to
139 | BOT_TRI = (Interp_Pts_2(:,1) >= Interp_Pts_2(:,2));
140 | Tri_Indices_2 = zeros(size(Interp_Pts_2,1),1);
141 | Tri_Indices_2(BOT_TRI) = 1; % bottom triangle cell index
142 | Tri_Indices_2(~BOT_TRI) = 2; % top triangle cell index
143 | ```
144 |
145 | Then, we must convert the global grid coordinates to local reference coordinates:
146 | ```matlab
147 | Ref_Coord_2 = Mesh.cartesianToReference(Tri_Indices_2,Interp_Pts_2);
148 | Omega_Interp_Data_2 = {uint32(Tri_Indices_2), Ref_Coord_2};
149 | ```
150 |
151 | Execute the interpolation code again:
152 | ```matlab
153 | % interpolate!
154 | INTERP_2 = Interp_2D(Mesh.Points,uint32(Mesh.ConnectivityList),[],[],Omega_Interp_Data_2,P2_DoFmap,p_val);
155 | Interp_Values = INTERP_2(1).DATA{1,1};
156 | ```
157 |
158 | Now plot the grid data:
159 | ```matlab
160 | figure;
161 | surf(XX, YY, I_grad_p_X(XX,YY)); % plot "exact" surface
162 | hold on;
163 | % plot the FE interpolated data as black dots
164 | plot3(Interp_Pts_2(:,1),Interp_Pts_2(:,2),Interp_Values,'k.','MarkerSize',18);
165 | hold off;
166 | axis equal;
167 | AZ = 70;
168 | EL = 10;
169 | view(AZ,EL);
170 | title('Surface is Exact. Points Are Interpolated From FE Approximation','FontSize',12);
171 | xlabel('x','FontSize',12);
172 | ylabel('y','FontSize',12);
173 | set(gca,'FontSize',12);
174 | ```
175 | Note that the black dots do not appear exactly on the surface. This makes sense because we are interpolating a finite element *approximation* of the surface.
176 |
177 | For more information, see the *Interpolating Finite Element Data* chapter in the PDF manual.
--------------------------------------------------------------------------------
/tutorials/Tutorial_Meshes_1.md:
--------------------------------------------------------------------------------
1 | Mesh classes in FELICITY
2 | ========================
3 |
4 | FELICITY has classes that are convenient for _manipulating_ meshes. Once FELICITY is installed (and is in the MATLAB path!), type the following at the MATLAB prompt:
5 |
6 | ```matlab
7 | >> Vtx = [0 0 0; 1 0 0; 0 1 1]; % 3-D coordinates
8 | >> Tri = [1 2 3]; % triangle connectivity
9 | >> Mesh = MeshTriangle(Tri,Vtx,'Omega');
10 | ```
11 |
12 | This creates a `MeshTriangle` object. Now type the following and press "ENTER":
13 |
14 | ```matlab
15 | >> Mesh
16 | ```
17 |
18 | This causes MATLAB to display the members of the object. Also, try entering:
19 |
20 | ```matlab
21 | >> methods(Mesh)
22 | ```
23 |
24 | This will display the various methods available for that object. One important method is uniform refinement:
25 |
26 | ```matlab
27 | >> Mesh = Mesh.Refine;
28 | ```
29 |
30 | This refines the (initial) single triangle into four self-similar triangles. You can view it with the command:
31 |
32 | ```matlab
33 | >> Mesh.Plot;
34 | ```
35 |
36 | You can also refine the mesh *selectively* using Rivara's bisection algorithm. Define an array of _marked_ triangles and input that to the refinement method:
37 |
38 | ```matlab
39 | >> Marked = [1; 3; 4];
40 | >> Mesh = Mesh.Refine('bisection',Marked);
41 | ```
42 |
43 | This will cause the first, third, and fourth triangles in the mesh to be bisected along their longest edge. The algorithm will then bisect any other triangles necessary to maintain a conforming mesh.
44 |
45 | For more information, see the *Basic Mesh Manipulation* chapter in the PDF manual.
--------------------------------------------------------------------------------
/tutorials/Tutorials_TOC.md:
--------------------------------------------------------------------------------
1 | FELICITY Tutorials
2 | ==================
3 |
4 | Introduction
5 | ------------
6 |
7 | This page gives a brief how-to on using the FELICITY toolbox. Also see the FELICITY (PDF) Manual in the .zip file, which can be downloaded [here](http://www.mathworks.com/matlabcentral/fileexchange/31141-felicity).
8 |
9 | NOTE: users should post questions or comments about the toolbox to the Discussion Forum.
10 |
11 | # Basic Tutorials
12 |
13 | * [Mesh Classes](../wiki/Tutorial_Meshes_1).
14 | * [Solve Laplace's Equation](../wiki/Solve_Laplaces_Eqn_1).
15 | * [Allocating Degrees-of-Freedom (DoFs)](../wiki/Allocate_DoFs_1).
16 | * [Smoothing Meshes](../wiki/Mesh_Smoothing_1).
17 |
18 | # Advanced Tutorials
19 |
20 | * [Solve Simple Elasticity Model](../wiki/Solve_Simple_Elasticity_3D_1).
21 | * [Solve the Stokes Equations in 2-D](../wiki/Solve_Stokes_2D_1).
22 | * [Managing Degrees-of-Freedom (DoFs): Part 1](../wiki/Managing_DoFs_1).
23 | * [Managing Degrees-of-Freedom (DoFs): Part 2](../wiki/Managing_DoFs_2).
24 | * [Interpolating Finite Element Data](../wiki/Tutorial_Interpolation_1).
25 | * [Solving the 3-D Laplace Equation](../wiki/Laplace_On_Cube_3D_1).
26 | * [Mesh Generation and Solving a PDE](../wiki/Mesh_Generation_With_Solving_PDE_1).
27 | * [A Finite Element Space on a Higher Order Mesh](../wiki/FE_Space_on_Higher_Order_Mesh_1).
28 | * [Solving Laplace-Beltrami on a Higher Order Mesh](../wiki/Laplace_Beltrami_Open_Surface_1).
29 |
30 | # Miscellaneous Utilities
31 |
32 | * [Mesh Generation With TIGER: Part 1](../wiki/Mesh_Generation_with_TIGER_1).
33 | * [Mesh Generation With TIGER: Part 2](../wiki/Mesh_Generation_with_TIGER_2).
34 | * [Quadtree Implementation](../wiki/Quadtree_Example_1).
35 | * [Find Closest Points on a Surface Mesh](../wiki/Computing_Closest_Points_To_Surface_Mesh_1).
36 |
--------------------------------------------------------------------------------