├── 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 | Curved Domain Diagram 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 | Curved Domain FE Forms 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 | Curved Domain Conv. Rate 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 | Saddle Domain Diagram 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 | Discrete Formulation 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 | Bilinear Forms 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 | Numerical Errors 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 | Laplace-Beltrami Solution u_h 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 | Laplace-Beltrami Bdy Solution lambda_h 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 | Laplace-Beltrami Conv. Rate 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 | --------------------------------------------------------------------------------