diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..db899980c4c3e089f1ef08478d1a079ef5bc1d9f --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# list of ignored files/folders + +build/ +*~ +*.o +*.obj +*.bak +.vscode/ +*.msh +gmsh-sdk/ +eigen/ +*.tgz +*.zip +*.swp +a.out + +# latex +*.aux +*.log +*.nav +*.out +#*.pdf +*.snm +*.synctex* +*.toc +*.fls +*.fdb_latexmk + +Backup_of* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..d0212f44044034adb745c31677465c14b844acf6 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,13 @@ +stages: + - build + +build_and_test: + image: rboman/waves + stage: build + script: + - source ./envs/linux-macos.sh + - cd lib && ./get_gmsh.sh && cd .. + - mkdir build + - cd build + - cmake .. + - make -j 4 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..90d81b21640dd301feb9b5b20f303fddf9c9b7c4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +PROJECT(MATH0471 CXX) +CMAKE_MINIMUM_REQUIRED(VERSION 3.12) + +ADD_SUBDIRECTORY( examples/gmsh_api ) +ADD_SUBDIRECTORY( examples/sparse_solve ) +ADD_SUBDIRECTORY( srcs ) diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d1ccf35319f9a33ff93c6074ae9e905bf3afebb1 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,3 @@ +# math0471/doc + +This folder contains the latest version of the presentation about thermoelasticity. \ No newline at end of file diff --git a/doc/bem/bem.cdr b/doc/bem/bem.cdr new file mode 100644 index 0000000000000000000000000000000000000000..c2d0f1c1695211913860e67752883388f79dcbff Binary files /dev/null and b/doc/bem/bem.cdr differ diff --git a/doc/bem/bem.pdf b/doc/bem/bem.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c8251a3b316f830c83a73ded2b959f49c0227f28 Binary files /dev/null and b/doc/bem/bem.pdf differ diff --git a/doc/bem/bem.tex b/doc/bem/bem.tex new file mode 100644 index 0000000000000000000000000000000000000000..9ba97c65614ab0294c7c175051bca2ef0e66fe7a --- /dev/null +++ b/doc/bem/bem.tex @@ -0,0 +1,471 @@ +% !TeX spellcheck = en_GB + +\documentclass[10pt,xcolor=pdftex,dvipsnames,table]{beamer} + +\usetheme{Boadilla} + +\usepackage{lmodern} +\usepackage[T1]{fontenc} % caracteres accentues +\usepackage[utf8]{inputenc} % encodage unicode des sources .tex +\usepackage{amsmath,amssymb,amsbsy,dsfont} + +% pour \mybox... +\usepackage{xcolor} +\setlength{\fboxrule}{0.1pt} +\newcommand{\mybox}[2]{\fcolorbox{#1}{white}{$\displaystyle#2$}} + + +\title{Boundary Element Method} +\author{ MATH-0471 } +%\institute{University of Li\`ege} +\date{\today} + +%\AtBeginSection[] +%{ +% \begin{frame} +% \frametitle{Outline} +% \tableofcontents[currentsection] +% \end{frame} +%} + + +\begin{document} + +\frame{\titlepage} + +%\section*{Outline} + +%\begin{frame} +% \frametitle{Outline} +% \tableofcontents +%\end{frame} + +% --------------------------------------------------------------------------------------- +%\section{ FEM for 2D Linear Elasticity } +% --------------------------------------------------------------------------------------- +\begin{frame} + \frametitle{ Boundary Element Method } + \framesubtitle{ Potential problem } + + \textbf{Potential problem} in $\Omega$: + + \begin{equation*} + \left\{ + \begin{aligned} + &\Delta u = 0 &&\text{ in } \Omega && \\ + &u = \bar{u} && \text{ on } \Gamma_D && \text{ (Dirichlet)}\\ + &\frac{\partial u}{\partial \boldsymbol{n}} = \bar{u}_n && \text{ on } \Gamma_N && \text{ (Neumann)} \\ + \end{aligned} + \right. + \end{equation*} + +where +\begin{itemize} + \item $u$ is the scalar unknown, + \item $\Gamma$ is the boundary of $\Omega$ and $\Gamma_D \cup \Gamma_N = \Gamma$ and $\Gamma_D \cap \Gamma_N = \emptyset$, + \item $\frac{\partial u}{\partial \boldsymbol{n}} = \boldsymbol{\nabla} u \cdot \boldsymbol{n}$, where $\boldsymbol{n}$ is the outward normal to $\Omega$. +\end{itemize} + \vspace{\stretch{1}} + +Note that the PDE is homogeneous but the boundary conditions are not. + \vspace{\stretch{1}} + + We follow here the notations from the book of \cite{Katsikadelis2016}, which is freely available from the \href{https://explore.lib.uliege.be/discovery/fulldisplay?docid=alma9919484598002321&context=L&vid=32ULG_INST:ULIEGE&lang=fr&search_scope=ULIEGE&adaptor=Local}{\color{blue}ULiege library website} (use VPN). + +\end{frame} + +\begin{frame} + \frametitle{ Boundary Element Method } + \framesubtitle{ Potential problem } + + \centerline{\includegraphics[width=\textwidth]{fig_domain}} + \vspace{\stretch{1}} + The domain can be finite (left) or infinite (right). +\end{frame} + +\begin{frame} + \frametitle{ Boundary Element Method } + \framesubtitle{ Fundamental solution / Free space Green's function in 2D} + + If we ignore the boundary conditions, a \textbf{fundamental solution} $v$ of the Laplace problem defined in $\mathds{R}^2$ can be found by solving the PDE with a Dirac impulse as right-hand side. + + \begin{equation*} + \Delta v = \delta(\boldsymbol{Q}-\boldsymbol{P}) + \end{equation*} + where $\boldsymbol{P} = \boldsymbol{P}(x,y)$ is the position of the Dirac impulse and $\boldsymbol{Q} = \boldsymbol{Q}(\xi,\eta)$ is the position where $v$ is evaluated + + \vspace{\stretch{1}} + We obtain + \begin{equation*} + v(\boldsymbol{Q}, \boldsymbol{P}) = \frac{1}{2\pi}\ln r + \end{equation*} + + with $r = \sqrt{(\xi-x)^2+(\eta-y)^2} = || \boldsymbol{Q}-\boldsymbol{P} ||$ + + \vspace{\stretch{1}} + + The fundamental solution is singular at $\boldsymbol{Q}=\boldsymbol{P}$. + +\end{frame} + + + + + + + + + + +\begin{frame} + \frametitle{ Boundary Element Method } + \framesubtitle{ Representation formula in the domain $\Omega$ } + + \textbf{Green's second identity} (also called Green's reciprocal identity): + + \begin{equation*} + \int_{\Omega} \left( v\,\Delta u - u\,\Delta v\, \right) d\Omega + = + \int_{\Gamma} \left( v \frac{\partial u}{\partial \boldsymbol{n}} - u \frac{\partial v}{\partial \boldsymbol{n}} \right)\, ds + \end{equation*} + + + + \vspace{\stretch{1}} + + From this equality, if we choose $v$ as the fundamental solution introduced earlier, we can write a \textbf{representation formula} for the solution $u$ of the Laplace equation in $\Omega$ (boundary excluded): + + \begin{equation*} + u(\boldsymbol{P}) = - \int_{\Gamma} \left( v(\boldsymbol{P},\boldsymbol{q}) \frac{\partial u(\boldsymbol{q})}{\partial \boldsymbol{n}_q} - u(\boldsymbol{q}) \frac{\partial v(\boldsymbol{P},\boldsymbol{q})}{\partial \boldsymbol{n}_q} \right)\, ds_q + \end{equation*} + + \vspace{\stretch{1}} + + Important remark concerning the notations: + \begin{itemize} + \item A capital letter $(\boldsymbol{P}, \boldsymbol{Q})$ represents a point inside the domain $\Omega$, + \item A lowercase letter $(\boldsymbol{p},\boldsymbol{q})$ represents a point on the boundary of $\Omega$. + \end{itemize} + + \vspace{\stretch{1}} + This formula can be used to compute $u$ in $\Omega$ if we know both $u(\boldsymbol{q})$ and $\frac{\partial u(\boldsymbol{q})}{\partial \boldsymbol{n}_q}$ on the boundary (but we don't: we have either one or the other). + +\end{frame} + + +\begin{frame} + \frametitle{ Boundary Element Method } + \framesubtitle{ Representation formula on the boundary $\Gamma$ } + + From the Green's second identity, it is also possible to deduce a representation formula for the solution $u$ lying on $\Gamma$: + + \begin{equation*} + \frac{\alpha}{2\pi} u(\boldsymbol{p}) = - \int_{\Gamma} \left( v(\boldsymbol{p},\boldsymbol{q}) \frac{\partial u(\boldsymbol{q})}{\partial \boldsymbol{n}_q} - u(\boldsymbol{q}) \frac{\partial v(\boldsymbol{p},\boldsymbol{q})}{\partial \boldsymbol{n}_q} \right)\, ds_q + \end{equation*} + + where $\alpha$ is the angle of the corner made by the boundary at $\boldsymbol{p}$. + + \vspace{\stretch{1}} + \centerline{\includegraphics[width=0.5\textwidth]{fig_alpha}} + + If the boundary is smooth, $\alpha=\pi$ and the coefficient in front of $u(\boldsymbol{p})$ is $\frac{1}{2}$. + + \vspace{\stretch{1}} + This \textbf{boundary integral equation} can be used to calculate $u$ and its derivative on the boundary. + +\end{frame} + + +\begin{frame} + \frametitle{ Boundary Element Method } + \framesubtitle{ Principle of the BEM } + + \begin{itemize} + \item The Boundary Element Method consists in meshing the boundary of the problem with a series of $N$ ``boundary elements''. + \item On each element, the solution $u$ is approximated by simple polynomials (constant, linear, etc.). + \item Injecting this approximation into the boundary integral equation and taking into account the boundary conditions allows us to calculate the solution and its derivative on the whole boundary. + \item Eventually, the solution inside the domain can be computed from the values on the boundary with the help of the representation formula. + \end{itemize} + +\end{frame} + + + +\begin{frame} + \frametitle{ Boundary Element Method } + \framesubtitle{ Constant boundary elements } + + \begin{itemize} + \item The simplest way to approximate the solution is to use a piecewise-linear approximation of the boundary (a series of oriented straight segments). + + \item On each element the solution $u$ and its derivative $u_n$ is considered constant (thus discontinuous across elements). + + \item We also define a node at the centre of each segment. + \end{itemize} + + \vspace{\stretch{1}} + \centerline{\includegraphics[width=0.4\textwidth]{fig_mesh}} + +\end{frame} + + + +\begin{frame} + \frametitle{ Boundary Element Method } + \framesubtitle{ Solving the boundary } + + Discretized Boundary Integral Equation: + \begin{equation*} + \frac{1}{2} u_i = - \sum_{j=1}^{N} \int_{\Gamma_j} v(\boldsymbol{p}_i, \boldsymbol{q}) + \underbrace{ \frac{\partial u(\boldsymbol{q})}{\partial \boldsymbol{n}_q} }_{u_n^j} + \,ds_q + + \sum_{j=1}^{N} \int_{\Gamma_j} + \underbrace{u(\boldsymbol{q})}_{u^j} + \frac{\partial v(\boldsymbol{p}_i,\boldsymbol{q})}{\partial \boldsymbol{n}_q}\,ds_q + \end{equation*} + + Thanks to the node position centred in the middle of each segment, the boundary is locally smooth and $\alpha=\frac{1}{2}$. + + \vspace{\stretch{1}} + + Reordering the terms: + \begin{equation*} + -\frac{1}{2} u^i + + \sum_{j=1}^{N} + \underbrace{ + \left[\int_{\Gamma_j} \frac{\partial v(\boldsymbol{p}_i,\boldsymbol{q})}{\partial \boldsymbol{n}_q}\,ds_q\right] + }_{\hat{H}_{ij}} + u^j + = \sum_{j=1}^{N} + \underbrace{ + \left[ \int_{\Gamma_j} v(\boldsymbol{p}_i, \boldsymbol{q}) \,ds_q \right] + }_{G_{ij}} + u_n^j + \end{equation*} + +\end{frame} + + + +\begin{frame} + \frametitle{ Boundary Element Method } + \framesubtitle{ Solving the boundary } + + \begin{equation*} + \sum_{j=1}^{N} + H_{ij} + u^j + = \sum_{j=1}^{N} + G_{ij} + u_n^j + \qquad + \text{ with } + \left\{ + \begin{aligned} + &H_{ij} = \int_{\Gamma_j} \frac{\partial v(\boldsymbol{p}_i,\boldsymbol{q})}{\partial \boldsymbol{n}_q}\,ds_q -\frac{1}{2}\delta_{ij} \\ + &G_{ij} = \int_{\Gamma_j} v(\boldsymbol{p}_i, \boldsymbol{q}) \,ds_q + \end{aligned} + \right. + \end{equation*} + + %\vspace{\stretch{1}} + \centerline{\includegraphics[width=0.75\textwidth]{fig_integration}} + + Difficulties are expected when $i=j$ (singular integrand). +\end{frame} + + +\begin{frame} + \frametitle{ Boundary Element Method } + \framesubtitle{ Solving the boundary } + + In matrix form + \begin{equation*} + [H] \{u\} = [G] \{u_n\} + \end{equation*} + + Equations can be sorted according to the type of boundary condition prescribed at the corresponding node (e.g. Dirichlet first, then Neumann): + + \begin{equation*} + \begin{bmatrix} + \qquad\\ + [H_{D}] & [H_N] \\ + \qquad\\ + \end{bmatrix} + \begin{Bmatrix} + \{u\}_D \\ + \{u\}_N \\ + \end{Bmatrix} + = + \begin{bmatrix} + \qquad\\ + [G_{D}] & [G_N] \\ + \qquad\\ + \end{bmatrix} + \begin{Bmatrix} + \{u_n\}_D \\ + \{u_n\}_N \\ + \end{Bmatrix} + \end{equation*} + Since $\{u\}_D = \{\bar{u}\}$ and $\{u_n\}_N = \{\bar{u}_n\}$, we can gather the unknowns in the left-hand side: + + \begin{equation*} + \underbrace{ + \begin{bmatrix} + \qquad\\ + [H_N] & -[G_D] \\ + \qquad\\ + \end{bmatrix} +}_{[A]} + \underbrace{ + \begin{Bmatrix} + \{u\}_N \\ + \{u_n\}_D \\ + \end{Bmatrix} +}_{\{x\}} + = + \underbrace{ + \begin{bmatrix} + \qquad\\ + -[H_{D}] & [G_N] \\ + \qquad\\ + \end{bmatrix} + \begin{Bmatrix} + \{\bar{u}\} \\ + \{\bar{u}_n\} \\ + \end{Bmatrix} +}_{ \{b\}} +\end{equation*} +This system is then solved using a linear solver. + +\end{frame} + + + +\begin{frame} + \frametitle{ Boundary Element Method } + \framesubtitle{ Calculation of $[G]$ } + + $\bullet$ $G_{ij}$ with $i\neq j$ (Gauss quadrature): + \begin{equation*} + G_{ij} = \int_{\Gamma_j} v(\boldsymbol{p}_i, \boldsymbol{q}) \,ds_q = \int_{-1}^1 \frac{1}{2\pi} \ln (r(\xi))\,\frac{l_j}{2}\,d\xi + = \frac{l_j}{4\pi} \sum_{k=1}^{N^{GP}} \ln (r(\xi_k))\, w_k + \end{equation*} + where $l_j$ is the length of element $j$ and $N^{GP}$ is the number of Gauss points with are located at $\xi_k$ with weight $w_k$. + + \vspace{\stretch{1}} + + $\bullet$ $G_{ii}$ (analytical integration): + \begin{equation*} + G_{ii} = \frac{l_i}{2\pi} \left( \ln\frac{l_i}{2}-1\right) +\end{equation*} + +\end{frame} + + +\begin{frame} + \frametitle{ Boundary Element Method } + \framesubtitle{ Calculation of $[H]$ } + + $\bullet$ $H_{ij}$ with $i\neq j$ (analytical integration): + \begin{equation*} + H_{ij} = \int_{\Gamma_j} \frac{\partial v(\boldsymbol{p}_i,\boldsymbol{q})}{\partial \boldsymbol{n}_q}\,ds_q + = \frac{a_{j+1}-a_j}{2\pi} + \end{equation*} + + %\vspace{\stretch{1}} + \centerline{\includegraphics[width=0.45\textwidth]{fig_hij}} + + \vspace{\stretch{1}} + + $\bullet$ $H_{ii}$ (analytical integration): + \begin{equation*} + \hat{H}_{ii} = 0 \qquad \Rightarrow + H_{ii} = -\frac{1}{2} + \end{equation*} + +\end{frame} + + +\begin{frame} + \frametitle{ Boundary Element Method } + \framesubtitle{ Calculation of the solution inside $\Omega$ } + + From the representation formula: + \begin{equation*} + u(\boldsymbol{P}) = - \int_{\Gamma} \left( v(\boldsymbol{P},\boldsymbol{q}) \frac{\partial u(\boldsymbol{q})}{\partial \boldsymbol{n}_q} - u(\boldsymbol{q}) \frac{\partial v(\boldsymbol{P},\boldsymbol{q})}{\partial \boldsymbol{n}_q} \right)\, ds_q + \end{equation*} + + we can write: + \begin{equation*} + u(\boldsymbol{P}) = -\sum_{j=1}^{N} + \left[ \int_{\Gamma_j} v(\boldsymbol{P}, \boldsymbol{q}) \,ds_q \right] + u_n^j + + \sum_{j=1}^{N} + \left[\int_{\Gamma_j} \frac{\partial v(\boldsymbol{P},\boldsymbol{q})}{\partial \boldsymbol{n}_q}\,ds_q\right] + u^j +\end{equation*} + +\begin{itemize} + \item The solution inside $\Omega$ requires the computation of boundary integrals with regular integrands (the singularity is located at $\boldsymbol{P}$). Refer to the expressions of $H_{ij}$ and $G_{ij}$ for $i\neq j$. + \item If the solution at several points need to be computed, it can be easily performed in parallel. +\end{itemize} + + +\end{frame} + + + +\begin{frame} + \frametitle{ Boundary Element Method } + \framesubtitle{ Summary } + + Advantages of the BEM: + \begin{itemize} + \item Only the surface of the problem should be meshed (the problem dimension is decreased by 1 compared to FEM). + \item The domain can be infinite. + \item The solution and its derivative can be easily evaluated anywhere in the domain. + \item Works well for stationary, linear and homogeneous PDEs with non-homogeneous boundary conditions. + \end{itemize} + + \vspace{\stretch{1}} + + Typical applications: + \begin{itemize} + \item Potential problems: stationary heat conduction, electrostatics, irrotational flows, Darcy flows, + \item Acoustics (Helmholtz equation), + \item Slow viscous flows (Stokes equations), + \item Elasticity (Lamé equations). + \end{itemize} + +\end{frame} + + + +\begin{frame} + \frametitle{ Boundary Element Method } + \framesubtitle{ Summary } + + Issues of the BEM: + \begin{itemize} + \item The method requires an \textbf{integral representation} of the solution and a fundamental solution to be calculated, which is sometimes impossible (e.g. nonlinear equations). + \item Integrals of \textbf{singular functions} must be evaluated very precisely with special techniques for obtaining accurate results. + \item The method relies on \textbf{non-sparse, non-symmetric matrices} which lead large memory requirement and CPU time. Thus, complicated acceleration techniques (fast multipole method, hierarchical matrices) are needed for 3D problems. + \end{itemize} +\end{frame} + +%------------------------------------------------ + +\begin{frame} + \frametitle{References} + \footnotesize{ + \begin{thebibliography}{99} % Beamer does not support BibTeX so references must be inserted manually as below + \bibitem[Katsikadelis, 2016]{Katsikadelis2016} John T. Katsikadelis (2016) + \newblock The Boundary Element Method for Engineers and Scientists -- Theory and Applications + \newblock Elsevier, \emph{Second edition}, ISBN: 9780128044933 + \end{thebibliography} + } +\end{frame} + + +\end{document} diff --git a/doc/bem/fig_alpha.pdf b/doc/bem/fig_alpha.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f493a1fe2bf4a165259fe1144107b566a975243e Binary files /dev/null and b/doc/bem/fig_alpha.pdf differ diff --git a/doc/bem/fig_domain.pdf b/doc/bem/fig_domain.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0fe59acfe29bfc7f6a455cf9fd935fd411ff088d Binary files /dev/null and b/doc/bem/fig_domain.pdf differ diff --git a/doc/bem/fig_hij.pdf b/doc/bem/fig_hij.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1f2935ba1abade3e3854a8cf63ea8a5d2ba027ac Binary files /dev/null and b/doc/bem/fig_hij.pdf differ diff --git a/doc/bem/fig_integration.pdf b/doc/bem/fig_integration.pdf new file mode 100644 index 0000000000000000000000000000000000000000..39cbc4431419d48d3cef66348051ebfb5fac70e8 Binary files /dev/null and b/doc/bem/fig_integration.pdf differ diff --git a/doc/bem/fig_mesh.pdf b/doc/bem/fig_mesh.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d5375e1c218e6c5a2aa1f9d1ee0e42a916eae7a7 Binary files /dev/null and b/doc/bem/fig_mesh.pdf differ diff --git a/doc/elasticity/elasticity.cdr b/doc/elasticity/elasticity.cdr new file mode 100644 index 0000000000000000000000000000000000000000..b8ef3c0e9e2fbe6ddbea49726ab4d868a5fdce40 Binary files /dev/null and b/doc/elasticity/elasticity.cdr differ diff --git a/doc/elasticity/elasticity.pdf b/doc/elasticity/elasticity.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1892d920ed3ea1e632990f7f9740f6d6321f3638 Binary files /dev/null and b/doc/elasticity/elasticity.pdf differ diff --git a/doc/elasticity/elasticity.tex b/doc/elasticity/elasticity.tex new file mode 100644 index 0000000000000000000000000000000000000000..4a79b995b9e91963381f9684e5e42a94ebc5d7ea --- /dev/null +++ b/doc/elasticity/elasticity.tex @@ -0,0 +1,625 @@ +% !TeX spellcheck = en_GB + +\documentclass[10pt,xcolor=pdftex,dvipsnames,table]{beamer} + +\usetheme{Boadilla} + +\usepackage{lmodern} +\usepackage[T1]{fontenc} % caracteres accentues +\usepackage[utf8]{inputenc} % encodage unicode des sources .tex +\usepackage{amsmath,amssymb,amsbsy,dsfont} + +% pour \mybox... +\usepackage{xcolor} +\setlength{\fboxrule}{0.1pt} +\newcommand{\mybox}[2]{\fcolorbox{#1}{white}{$\displaystyle#2$}} + + +\title{FEM for 2D Linear Elasticity} +\author{ MATH-0471 } +%\institute{University of Li\`ege} +\date{\today} + +%\AtBeginSection[] +%{ +% \begin{frame} +% \frametitle{Outline} +% \tableofcontents[currentsection] +% \end{frame} +%} + + +\begin{document} + +\frame{\titlepage} + +%\section*{Outline} + +%\begin{frame} +% \frametitle{Outline} +% \tableofcontents +%\end{frame} + +% --------------------------------------------------------------------------------------- +%\section{ FEM for 2D Linear Elasticity } +% --------------------------------------------------------------------------------------- +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ Summary of equations } + + \textbf{Mechanical equilibrium} in $\Omega$: + + \begin{equation*} + \left\{ + \begin{aligned} + \boldsymbol{\nabla}\cdot\boldsymbol{\sigma} + \rho\boldsymbol{b} &= 0 + \qquad &&\text{ (translation) } \\ + \boldsymbol{\sigma} &= \boldsymbol{\sigma}^T &&\text{ (rotation) } + \end{aligned} + \right. + \end{equation*} + + where $\boldsymbol{\sigma}$ is the Cauchy stress tensor, $\rho$ is the density and $\boldsymbol{b}$ represents the mechanical volumic forces. + + \vspace{\stretch{1}} + + +and the \textbf{boundary conditions} on $\Gamma$, the boundary of $\Omega$: + +\begin{equation*} + \left\{ + \begin{aligned} + & \boldsymbol{u} = \bar{\boldsymbol{u}} & \qquad \text { on } \Gamma_{\bar{\boldsymbol{u}}} & \qquad \text{ : prescribed displacement (Dirichlet)} \\ + & \boldsymbol{\sigma}\cdot\boldsymbol{n} = \bar{\boldsymbol{t}} & \text { on } \Gamma_{\bar{\boldsymbol{t}}} & \qquad \text{ : prescribed surface traction (Neumann)} \\ + \end{aligned} + \right. +\end{equation*} +where $\boldsymbol{u}$ is the displacement, $\boldsymbol{n}$ is the outward unit normal to the boundary, $\Gamma_{\bar{\boldsymbol{u}}} \cup \Gamma_{\bar{\boldsymbol{t}}} = \Gamma$ and $\Gamma_{\bar{\boldsymbol{u}}} \cap \Gamma_{\bar{\boldsymbol{t}}} = \emptyset$ + + +\end{frame} + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ Summary of equations } + + \textbf{Strain-displacement relationship} (infinitesimal strains): + \begin{equation*} + \boldsymbol{\varepsilon} + = \tfrac{1}{2} \left( \boldsymbol{\nabla}\boldsymbol{u} + (\boldsymbol{\nabla}\boldsymbol{u})^T \right) + \end{equation*} + where small strains and small displacements are assumed. + + \vspace{\stretch{1}} + + \textbf{Constitutive law} (Hooke's law): + \begin{equation*} + \boldsymbol{\sigma} = \mathbb{H}:\boldsymbol{\varepsilon} + \end{equation*} + with the 4$^{th}$ order tensor + \begin{equation*} + \mathbb{H}_{ijkl} = \frac{E}{2(1+\nu)} \delta_{ik}\delta_{jl} + + \frac{\nu\,E}{(1+\nu)(1-2\nu)}\delta_{ij}\delta_{kl} + \quad\text{ with } + \left\{ + \begin{aligned} + &E : \text{Young's modulus} \\ + &\nu : \text{Poisson's ratio} \\ + \end{aligned} + \right. + \end{equation*} + for an isotropic material. +\end{frame} + + + + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ Weak formulation } + + A \textbf{weak formulation} is obtained by multiplying the PDE by a function $\boldsymbol{w}(\boldsymbol{x})$ and by integrating over $\Omega$: + + \begin{equation*} + \int_{\Omega} \boldsymbol{w}\cdot \left(\boldsymbol{\nabla}\cdot\boldsymbol{\sigma}\right)\, dV + + \int_{\Omega} \rho \boldsymbol{w}\cdot \boldsymbol{b}\, dV = 0 + \end{equation*} + + \vspace{\stretch{1}} + + The first term of the left-hand side can be integrated by parts: + \begin{equation*} + \int_{\Omega} \boldsymbol{w}\cdot \left(\boldsymbol{\nabla}\cdot\boldsymbol{\sigma}\right)\, dV + = \int_{\Gamma_{\bar{\boldsymbol{u}}}} \boldsymbol{w}\cdot \boldsymbol{\sigma} \cdot \boldsymbol{n}\, dS + + \int_{\Gamma_{\bar{\boldsymbol{t}}}} \boldsymbol{w}\cdot \underbrace{\boldsymbol{\sigma} \cdot \boldsymbol{n}}_{\boldsymbol{\bar{t}}}\, dS + - \int_{\Omega} \boldsymbol{\nabla}\boldsymbol{w} : \boldsymbol{\sigma}\, dV + \end{equation*} + + The integral on $\Gamma_{\bar{u}}$ can disappear if we choose test functions $\boldsymbol{w}(\boldsymbol{x})$ that vanish on this boundary. +\end{frame} + + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ Weak formulation } + + Moreover, since $\boldsymbol{\sigma} = \boldsymbol{\sigma}^T$, we can write + + \begin{equation*} + \boldsymbol{\nabla}\boldsymbol{w} : \boldsymbol{\sigma} = \underbrace{\tfrac{1}{2} \left( \boldsymbol{\nabla}\boldsymbol{w} + (\boldsymbol{\nabla}\boldsymbol{w})^T \right)}_{\text{similar to } \boldsymbol{\varepsilon}(\boldsymbol{u})} : \boldsymbol{\sigma} + \end{equation*} + + + \vspace{\stretch{1}} + + The weak formulation becomes\footnote{This is also called ``the principle of virtual work''. \\ + Notations in \cite{ponthot2020}: $\boldsymbol{w} \leftrightarrow \boldsymbol{\delta u}$ and $\boldsymbol{\delta \varepsilon} \leftrightarrow \tfrac{1}{2} \left( \boldsymbol{\nabla}\boldsymbol{w} + (\boldsymbol{\nabla}\boldsymbol{w})^T \right)$) \\ + and $\boldsymbol{u}+\boldsymbol{\delta u}=\boldsymbol{\bar{u}}$ on $\Gamma_{\bar{u}}$ (``kinematically admissible virtual displacement'').\\}: + + \begin{block}{Weak formulation} + Find $\boldsymbol{u}$ with $\boldsymbol{u} = \bar{\boldsymbol{u}}$ on $\Gamma_{\bar{u}}$ such that + \begin{equation*} + \int_{\Gamma_{\bar{\boldsymbol{t}}}} \boldsymbol{w}\cdot \boldsymbol{\bar{t}}\, dS + - \int_{\Omega} \tfrac{1}{2} \left( \boldsymbol{\nabla}\boldsymbol{w} + (\boldsymbol{\nabla}\boldsymbol{w})^T \right) : \boldsymbol{\sigma}\, dV + + \int_{\Omega} \rho\boldsymbol{w} \cdot \boldsymbol{b}\, dV = 0 + \end{equation*} + for all test functions $\boldsymbol{w}$ which are 0 on $\Gamma_{\bar{u}}$. + \end{block} + +\end{frame} + + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ Spatial discretization - Finite Element approximation } + + The discretization procedure is similar to the one followed for the Laplace/Poisson equation in MATH0024, except that the unknown field $\boldsymbol{u}(\boldsymbol{x}, t)$ is now a vector instead of a scalar. The equations are detailed here for the 2D case. + \vspace{\stretch{1}} + + Using Cartesian coordinates, each component of $\boldsymbol{u}(\boldsymbol{x}, t)$ and $\boldsymbol{w}(\boldsymbol{x})$ are approximated using well-chosen shape functions $N^k$: + \begin{equation*} + \left\{ + \begin{aligned} + &u_1(\boldsymbol{x}, t) = \sum_k N^k(\boldsymbol{x})\,u_1^k(t) \\ + &u_2(\boldsymbol{x}, t) = \sum_k N^k(\boldsymbol{x})\,u_2^k(t) \\ + \end{aligned} + \right. + \quad + \text{ and } + \left\{ + \begin{aligned} + &w_1(\boldsymbol{x}) = \sum_k N^k(\boldsymbol{x})\,w_1^k \\ + &w_2(\boldsymbol{x}) = \sum_k N^k(\boldsymbol{x})\,w_2^k \\ + \end{aligned} + \right. + \end{equation*} + where $u_i^k$ is the $i^{\text{th}}$ coordinate of node $k$ (also called ``degree of freedom''). +\end{frame} + + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ Spatial discretization - Finite Element approximation } + + In matrix form (assuming a mesh of $n$ nodes in a 2D space): + \begin{equation*} + \boldsymbol{u}(\boldsymbol{x},t) = \mathbf{N}(\boldsymbol{x})\,\boldsymbol{d}(t) + \end{equation*} + with + \begin{equation*} + \mathbf{N}(\boldsymbol{x}) = + \begin{bmatrix} + N_1 & 0 & N_2 & 0 & \dots & N_n & 0 \\ + 0 & N_1 & 0 & N_2 & \dots & 0 & N_n \\ + \end{bmatrix} + \end{equation*} + \begin{equation*} + \boldsymbol{d}^T = + \begin{bmatrix} + u_1^1 & u_2^1 & u_1^2 & u_2^2 & \dots & u_1^n & u_2^n + \end{bmatrix} + \end{equation*} + + \vspace{\stretch{1}} + + Similarly, for the test functions: + \begin{equation*} + \boldsymbol{w}(\boldsymbol{x}) = \mathbf{N}(\boldsymbol{x})\,\boldsymbol{h} + \end{equation*} +\end{frame} + + + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ Isoparametric Finite Elements } + + + \centerline{\includegraphics[width=0.7\textwidth]{fig_isoparametric}} + + \vspace{\stretch{1}} + + \textbf{Isoparametric finite elements} ($m$ nodes) are a classical choice. Their geometry is interpolated with the same shape functions\footnote{$N^k$: superscript $k$ is related nodes where $N^k=1$} + as the unknown field $\boldsymbol{u}(\boldsymbol{x}, t)$ and the test functions $\boldsymbol{w}(\boldsymbol{x})$. + \begin{equation*} + \boldsymbol{x}(\boldsymbol{\xi}) = \sum_{k=1}^m N^k(\boldsymbol{\xi})\,\boldsymbol{x}^k + \end{equation*} + where $\boldsymbol{\xi}$ are reduced coordinates (usually $\xi_i\in[-1,1]$) +\end{frame} + + + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ 3D Hooke's law (matrix form) } + + \textbf{Voigt's notation}: in a practical implementation, $4^{\text{th}}$ and $2^{\text{nd}}$-order tensors are replaced by matrices and vectors respectively. + \begin{equation*} + \begin{bmatrix} + \sigma_{11} \\ + \sigma_{22} \\ + \sigma_{33} \\ + \sigma_{12} \\ + \sigma_{13} \\ + \sigma_{23} \\ + \end{bmatrix} + = \frac{E}{(1+\nu)(1-2\nu)} + \begin{bmatrix} + 1-\nu & \nu & \nu & 0 & 0 & 0 \\ + \nu & 1-\nu & \nu & 0 & 0 & 0 \\ + \nu & \nu & 1-\nu & 0 & 0 & 0 \\ + 0 &0 & 0 & \frac{1-2\nu}{2} & 0 & 0 \\ + 0 &0 & 0 & 0 &\frac{1-2\nu}{2} & 0 \\ + 0 &0 & 0 & 0 & 0 & \frac{1-2\nu}{2} \\ + \end{bmatrix} + \begin{bmatrix} + \varepsilon_{11} \\ + \varepsilon_{22} \\ + \varepsilon_{33} \\ + 2\varepsilon_{12} \\ + 2\varepsilon_{13} \\ + 2\varepsilon_{23} \\ + \end{bmatrix} + \end{equation*} + such that + \begin{equation*} + \boldsymbol{\varepsilon}:\boldsymbol{\sigma} = \boldsymbol{\varepsilon}^v\cdot \boldsymbol{\sigma}^v + \end{equation*} + + Hooke's law becomes: + \begin{equation*} + \boldsymbol{\sigma}^v = \mathbf{H}\,\boldsymbol{\varepsilon}^v + \end{equation*} + + \vspace{\stretch{1}} + + Remark: $\boldsymbol{\varepsilon}^v$ involves ${\color{red}2\times}\varepsilon_{ij}$ = $\gamma_{ij}$ (shear strain/angle) for $i\neq j$. + +\end{frame} + + + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ 2D Hooke's law (matrix form) } + + A very common 2D assumption is the \textbf{plane-stress hypothesis} which is valid for thin structures ($\sigma_{33}=\sigma_{13}=\sigma_{23}=0$): + + \begin{equation*} + \begin{bmatrix} + \varepsilon_{11} \\ + \varepsilon_{22} \\ + \varepsilon_{33} \\ + 2\varepsilon_{12} \\ + 2\varepsilon_{13} \\ + 2\varepsilon_{23} \\ + \end{bmatrix} + = \underbrace{\frac{1}{E} + \begin{bmatrix} + 1 & -\nu & -\nu & 0 & 0 & 0 \\ + -\nu & 1 & -\nu & 0 & 0 & 0 \\ + -\nu & -\nu & 1 & 0 & 0 & 0 \\ + 0 &0 & 0 & \frac{1+\nu}{2} & 0 & 0 \\ + 0 &0 & 0 & 0 &\frac{1+\nu}{2} & 0 \\ + 0 &0 & 0 & 0 & 0 & \frac{1+\nu}{2} \\ + \end{bmatrix}}_{\boldsymbol{H}^{-1}} + \begin{bmatrix} + \sigma_{11} \\ + \sigma_{22} \\ + \color{red}0 \\ + \sigma_{12} \\ + \color{red}0 \\ + \color{red}0 \\ + \end{bmatrix} + \end{equation*} + + \vspace{\stretch{1}} + + The 2D Hooke's law is obtained by inverting this relationship: + \begin{equation*} + \begin{bmatrix} + \sigma_{11} \\ + \sigma_{22} \\ + \sigma_{12} \\ + \end{bmatrix} + = \underbrace{\frac{E}{1-\nu^2} + \begin{bmatrix} + 1 & \nu & 0 \\ + \nu & 1 & 0 \\ + 0 & 0 & \frac{1-\nu}{2} \\ + \end{bmatrix} }_{\boldsymbol{H}_{2D}} + \begin{bmatrix} + \varepsilon_{11} \\ + \varepsilon_{22} \\ + 2\varepsilon_{12} \\ + \end{bmatrix} + \end{equation*} + This $3\times 3$ matrix is used as $\boldsymbol{H}$ in the following equations. + +\end{frame} + + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ Strain-displacement relationship } + + Using Voigt's notation, the strain-displacement relationship becomes: + + \begin{equation*} + \begin{bmatrix} + \varepsilon_{11} \\ + \varepsilon_{22} \\ + 2\varepsilon_{12} \\ + \end{bmatrix} + = + \begin{bmatrix} + \frac{\partial}{\partial x_1} & 0 \\ + 0 &\frac{\partial}{\partial x_2} \\ + \frac{\partial}{\partial x_2} & \frac{\partial}{\partial x_1} \\ + \end{bmatrix} + \begin{bmatrix} + u_1 \\ + u_2 \\ + \end{bmatrix} + \end{equation*} + + or, symbolically + \begin{equation*} + \boldsymbol{\varepsilon}^v = \boldsymbol{\partial}\,\boldsymbol{u} + \end{equation*} + + \vspace{\stretch{1}} + + Remarks: + \begin{itemize} + \item $\varepsilon_{33}$ does not appear in the 2D equations. However, it can be computed, if needed, from the stresses: + \begin{equation*} + \varepsilon_{33}=-\frac{\nu}{E}\left(\sigma_{11}+\sigma_{22}\right) + \end{equation*} + \item Strains (and stresses) are discontinuous across finite elements boundaries unless $\boldsymbol{u}(\boldsymbol{x})$ is $C^1$, which is almost never the case in practise. + \end{itemize} +\end{frame} + + + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ Strains/stresses as a function of the unknowns} + + Computation of \textbf{strains} from the nodal displacements $\boldsymbol{d}$: + \begin{equation*} + \boldsymbol{\varepsilon}^v = \boldsymbol{\partial}\,\boldsymbol{u} = \boldsymbol{\partial}\,\mathbf{N}\,\boldsymbol{d} = \mathbf{B}\,\boldsymbol{d} + \end{equation*} + with + \begin{equation*} + \mathbf{B} = + \begin{bmatrix} + \frac{\partial}{\partial x_1} & 0 \\ + 0 &\frac{\partial}{\partial x_2} \\ + \frac{\partial}{\partial x_2} & \frac{\partial}{\partial x_1} \\ + \end{bmatrix} + \begin{bmatrix} + N^1 & 0 & \dots & N^m & 0 \\ + 0 & N^1 & \dots & 0 & N^m \\ + \end{bmatrix} + \end{equation*} + + + \vspace{\stretch{1}} + + The \textbf{stresses} can also be obtained from the nodal displacement $\boldsymbol{d}$: + + \begin{equation*} + \boldsymbol{\sigma}^v = \mathbf{H}\,\mathbf{B}\,\boldsymbol{d} + \end{equation*} + +\end{frame} + + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ Discretized weak formulation } + + Back to the \textbf{weak formulation} + \begin{equation*} + \int_{\Gamma_{\bar{\boldsymbol{t}}}} \boldsymbol{w}\cdot \boldsymbol{\bar{t}}\, dS + - \int_{\Omega} + \underbrace{\frac{1}{2} \left( \boldsymbol{\nabla}\boldsymbol{w} + (\boldsymbol{\nabla}\boldsymbol{w})^T \right) + : \boldsymbol{\sigma}}_{(\mathbf{B}\boldsymbol{h})\cdot\boldsymbol{\sigma}^v }\, dV + + \int_{\Omega} \rho \boldsymbol{w} \cdot \boldsymbol{b}\, dV = 0 + \end{equation*} + + + \vspace{\stretch{1}} + + Replacing $\boldsymbol{w}=\mathbf{N}\boldsymbol{h}$ and $\boldsymbol{u}=\mathbf{N}\boldsymbol{d}$ and $\boldsymbol{\sigma}^v = \mathbf{H}\,\mathbf{B}\,\boldsymbol{d}$ + + + \begin{equation*} + \boldsymbol{h}^T \left( \int_{\Gamma_{\bar{\boldsymbol{t}}}} \mathbf{N}^T\boldsymbol{\bar{t}}\, dS \right) - \boldsymbol{h}^T \left(\int_{\Omega}\mathbf{B}^T\mathbf{H}\mathbf{B} \, dV \right) \boldsymbol{d} + + \boldsymbol{h}^T \left( \int_{\Omega} \rho\mathbf{N}^T\boldsymbol{b}\, dV \right) = 0 + \end{equation*} + + This relationship should be satisfied for any $\boldsymbol{h}$. + +\end{frame} + + + + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ Discretized FE equations } + + This leads to the system of discretized equations: + \begin{equation*} + \underbrace{\left(\int_{\Omega}\mathbf{B}^T\mathbf{H}\mathbf{B} \, dV \right)}_{\mathbf{K}} \boldsymbol{d} + = + \underbrace{\left( \int_{\Gamma_{\bar{\boldsymbol{t}}}} \mathbf{N}^T\boldsymbol{\bar{t}}\, dS \right) + + \left( \int_{\Omega} \rho\,\mathbf{N}^T\boldsymbol{b}\, dV \right)}_{\boldsymbol{f}} + \end{equation*} + + \vspace{\stretch{1}} + + \begin{block}{Discretized equations} + \begin{equation*} + \mathbf{K}\boldsymbol{d}=\boldsymbol{f} + \end{equation*} + \end{block} + + +\end{frame} + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ Finite Elements } + + \textbf{Assembly}: + \begin{itemize} + \item The integrals involved in the calculation of $\mathbf{K}$ and $\boldsymbol{f}$ are expressed as a sum of integrals over each finite element. These elemental contributions are then assembled (summed) in a large and structural matrix and vector. + \item $\mathbf{K}$ is a symmetric sparse matrix and this property should be exploited to reduce the required storage and to efficiently solve the linear system of equations. + \end{itemize} + + \vspace{\stretch{1}} + + \textbf{Boundary conditions}: + \begin{itemize} + \item The equations of the system corresponding to nodes where Dirichlet boundary conditions are prescribed should be discarded and replaced by equations enforcing these conditions ($\boldsymbol{u}=\bar{\boldsymbol{u}}$) at these nodes. + \item Homogeneous Dirichlet boundary conditions ($\boldsymbol{u}=0$) lead to remove the lines and columns of the matrices corresponding to the components of the displacement where these conditions are prescribed. + \end{itemize} +\end{frame} + + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ Practical calculation of the elemental stiffness matrices } + + \begin{equation*} + K_{ij} = \int_{\Omega} B_{ki}(\boldsymbol{x})\,H_{kl}\,B_{lj}(\boldsymbol{x}) \, dV + \end{equation*} + \vspace{\stretch{1}} + + Isoparametric finite elements: + \begin{equation*} + K_{ij} = \int_{-1}^1\int_{-1}^1\int_{-1}^1 B_{ki}(\boldsymbol{\xi})\,H_{kl}\,B_{lj}(\boldsymbol{\xi})\, \underbrace{\det (\frac{\partial{\boldsymbol{x}} }{\partial\boldsymbol{\xi}})}_{\text{jacobian}} \, d\boldsymbol{\xi} + \end{equation*} + + \vspace{\stretch{1}} + + \centerline{\includegraphics[width=0.8\textwidth]{fig_isoparametric2}} + +\end{frame} + + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ Gauss quadrature } + + This integral is calculated using a Gauss quadrature: ($N^{\text{GP}}$ chosen $\rightarrow$ $\boldsymbol{\xi}^p$, $w_p$) + + \begin{equation*} + K_{ij} \approx \sum_{p=1}^{N^{\text{GP}}} \underbrace{ + B_{ki}(\boldsymbol{\xi})\,H_{kl}\,B_{lj}(\boldsymbol{\xi}) + \det \mathbf{J} + }_{\text{all the factors evaluated at } \boldsymbol{\xi}=\boldsymbol{\xi}^p} \,w_p + \end{equation*} + with $N^{\text{GP}}$, the number of Gauss points, $\boldsymbol{\xi}^p$ the positions and $w_p$, the weights. + + \vspace{\stretch{1}} + \centerline{\includegraphics[width=0.4\textwidth]{fig_gauss}} + +\end{frame} + + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ Jacobian of isoparametric elements at each Gauss points} + + \textbf{Jacobian matrix}\footnote{transposed in \cite{ponthot2020}}: let $\boldsymbol{x}=(x_1, x_2, x_3)$ and $\boldsymbol{\xi}=(\xi_1, \xi_2, \xi_3)$ + \begin{equation*} + J_{ij} (\boldsymbol{\xi}) =\frac{\partial x_i}{\partial \xi_j} = \frac{\partial N^k (\boldsymbol{\xi})}{\partial \xi_j} x_i^k + \end{equation*} + where $x_i^k$ is the $i^{\text{th}}$ coordinate of the $k^{\text{th}}$ node of the finite element. + + \vspace{\stretch{1}} + The Jacobian matrix and its determinant must be evaluated at each Gauss point ($\boldsymbol{\xi}=\boldsymbol{\xi}^p$) of each finite element. + + \vspace{\stretch{1}} + + Note: the derivatives of the shape functions evaluated at the Gauss points $\frac{\partial N^k}{\partial \xi_j} (\boldsymbol{\xi}^p)$ are the same for all elements and can be computed once. + +\end{frame} + + + + +\begin{frame} + \frametitle{ FEM for 2D Linear Elasticity } + \framesubtitle{ Derivatives of the shape functions } + + \begin{equation*} + \mathbf{B} = + \begin{bmatrix} + \frac{\partial}{\partial x_1} & 0 \\ + 0 &\frac{\partial}{\partial x_2} \\ + \frac{\partial}{\partial x_2} & \frac{\partial}{\partial x_1} \\ + \end{bmatrix} + \begin{bmatrix} + N^1 & 0 & \dots & N^m & 0 \\ + 0 & N^1 & \dots & 0 & N^m \\ + \end{bmatrix} + \end{equation*} + + The matrix $\mathbf{B}$ contains the derivatives of the shape functions with respect to $\boldsymbol{x}$ which should be transformed into derivatives of the shape functions with respect to $\boldsymbol{\xi}$ using the Jacobian matrix: + + \begin{equation*} + \frac{\partial}{\partial\xi_i} = \left( \frac{\partial x_j}{\partial \xi_i} \right) \frac{\partial}{\partial x_j} = J_{ji} \frac{\partial}{\partial x_j} \qquad \Rightarrow \boldsymbol{\nabla}_{\color{red}\boldsymbol{x}} = \mathbf{J}^{-T} \boldsymbol{\nabla}_{\color{red}\boldsymbol{\xi}} + \end{equation*} + + \vspace{\stretch{1}} + + Thus, the derivative of $N^i$ with respect to $\boldsymbol{x}$ can be computed by this formula: + + \begin{equation*} + \boldsymbol{\nabla}_{\boldsymbol{x}} N^i = \mathbf{J}^{-T} \boldsymbol{\nabla}_{\boldsymbol{\xi}} N^i + \end{equation*} + +\end{frame} + + + +%------------------------------------------------ + +\begin{frame} + \frametitle{References} + \footnotesize{ + \begin{thebibliography}{99} % Beamer does not support BibTeX so references must be inserted manually as below + \bibitem[Ponthot, 2020]{ponthot2020} J.-P. Ponthot (2020) + \newblock An Introduction to the Finite Element Method + \newblock \emph{Lecture notes}, chapters 10 -- 11. + \end{thebibliography} + } +\end{frame} + + +\end{document} diff --git a/doc/elasticity/fig_gauss.pdf b/doc/elasticity/fig_gauss.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c8b0c11008c4258deaba565da6267664a502b6b3 Binary files /dev/null and b/doc/elasticity/fig_gauss.pdf differ diff --git a/doc/elasticity/fig_isoparametric.pdf b/doc/elasticity/fig_isoparametric.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d901cf183acf0eee3f9676e67afcc835d8f104a2 Binary files /dev/null and b/doc/elasticity/fig_isoparametric.pdf differ diff --git a/doc/elasticity/fig_isoparametric2.pdf b/doc/elasticity/fig_isoparametric2.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5386a4424cfb7dd2c4c6b89864d1c51bc620ef1f Binary files /dev/null and b/doc/elasticity/fig_isoparametric2.pdf differ diff --git a/envs/README.md b/envs/README.md new file mode 100644 index 0000000000000000000000000000000000000000..fbcc703be3add5fc2189f220f7045ddb46b4249e --- /dev/null +++ b/envs/README.md @@ -0,0 +1,14 @@ +# math0471/envs + +This folder contains 2 scripts for the correct definition of environment variables of your system so that +* Your c++ compiler (g++) is available from the command line, +* Eigen and gmsh libraries and header files can be found by cmake, +* The gmsh executable can be used in the command line (by just typing `gmsh`). +* `cmake` and `make` can be used on Windows as if you were on linux/mac (see `cmake.cmd` and `make.cmd` for more information) + +Use `windows.cmd` on Windows (run it or double-click on it) + +Use `linux-macos.sh` on linux or macOS with the command: +``` +source linux-macos.sh +``` diff --git a/envs/cmake.cmd b/envs/cmake.cmd new file mode 100644 index 0000000000000000000000000000000000000000..662f965de4ed89c8dc9e7a0c9e1d1b411cb4e274 --- /dev/null +++ b/envs/cmake.cmd @@ -0,0 +1,2 @@ +@echo off +cmake.exe -G"MinGW Makefiles" %* \ No newline at end of file diff --git a/envs/compiler b/envs/compiler new file mode 100644 index 0000000000000000000000000000000000000000..820ba5b04e70545315afb8673c465dfeda418112 --- /dev/null +++ b/envs/compiler @@ -0,0 +1 @@ + = NOT found! diff --git a/envs/example_of_launch_mac.json b/envs/example_of_launch_mac.json new file mode 100644 index 0000000000000000000000000000000000000000..e4079da034c6ccbdff1f5a9d505ca5df90233b00 --- /dev/null +++ b/envs/example_of_launch_mac.json @@ -0,0 +1,26 @@ +{ +"version": "0.2.0", +"configurations": [ + { + "name": "clang++ - Build and debug active file", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/bin/advection", + "args": [], + "stopAtEntry": true, + "cwd": "${workspaceFolder}", + "environment": [ + { + "name": "PATH", + "value": "${env:PATH}:${workspaceFolder}/gmsh-sdk/bin:${workspaceFolder}/gmsh-sdk/lib" + }, + { + "name": "DYLD_LIBRARY_PATH", + "value": "${env:DYLD_LIBRARY_PATH}:${workspaceFolder}/gmsh-sdk/bin:${workspaceFolder}/gmsh-sdk/lib" + } + ], + "externalConsole": false, + "MIMode": "lldb" + } + ] +} \ No newline at end of file diff --git a/envs/example_of_launch_win.json b/envs/example_of_launch_win.json new file mode 100644 index 0000000000000000000000000000000000000000..55b3cb34cedd671c557462bd8cea714a66950c1d --- /dev/null +++ b/envs/example_of_launch_win.json @@ -0,0 +1,33 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Launch edges.exe", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/bin/edges.exe", + "args": [], + "stopAtEntry": true, + "cwd": "${workspaceFolder}", + "environment": [ + { + "name": "PATH", + "value": "${env:PATH};C:\\mingw-w64\\mingw64\\bin;${workspaceFolder}\\gmsh-sdk\\bin;${workspaceFolder}\\gmsh-sdk\\lib" + } + ], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "C:/mingw-w64/mingw64/bin/gdb.exe", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/envs/linux-macos.sh b/envs/linux-macos.sh new file mode 100644 index 0000000000000000000000000000000000000000..9970673d782322100e11d4a020740a14a792be0c --- /dev/null +++ b/envs/linux-macos.sh @@ -0,0 +1,32 @@ +# This file setup an environment which allows you to compile the code +# on Linux or macOS using the default compiler (gcc or clang) +# +# HOW TO USE THIS FILE? +# open a terminal +# source ./envs/linux-macos.sh +# mkdir build +# cd build +# cmake .. +# make +# [executables are built in the bin/ folder] +# ctest + +# try to get the absolute path of root folder +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)"; DIR="${DIR%x}" +abspath=$(dirname $DIR ) +#echo "abspath=${abspath}" + +# set the location of gmsh SDK ( **MODIFY THIS LINE FOR YOUR SYSTEM** ) +GMSHSDK=${abspath}/lib/gmsh-sdk +EIGEN=${abspath}/lib/eigen + +# where are gmsh and gmsh-**.so ? +export PATH=${GMSHSDK}/bin:${GMSHSDK}/lib:${PATH} +# where is gmsh.h ? +export INCLUDE=${EIGEN}:${GMSHSDK}/include:${INCLUDE} +# where is gmsh.lib ? +export LIB=${GMSHSDK}/lib:${LIB} +# where is gmsh.py ? (required only if you want to use the python API) +export PYTHONPATH=${GMSHSDK}/lib:${PYTHONPATH} +# the following command is only useful for macOS +export DYLD_LIBRARY_PATH=${GMSHSDK}/lib:${DYLD_LIBRARY_PATH} diff --git a/envs/make.cmd b/envs/make.cmd new file mode 100644 index 0000000000000000000000000000000000000000..cd6247a535b99591f68b00b81cc7d587239cb7ab --- /dev/null +++ b/envs/make.cmd @@ -0,0 +1,2 @@ +@echo off +mingw32-make.exe %* \ No newline at end of file diff --git a/envs/windows.cmd b/envs/windows.cmd new file mode 100644 index 0000000000000000000000000000000000000000..f3de67ad128b07f806c1a8bcb10d319faa00d5fa --- /dev/null +++ b/envs/windows.cmd @@ -0,0 +1,82 @@ +@echo off +:: This file opens a terminal which allows you to compile the code with +:: a 64-bit mingw compiler +:: https://mingw-w64.org/ +:: https://www.msys2.org/ +:: +:: HOW TO USE THIS FILE? +:: [check the PATHs below] +:: [run this file] +:: mkdir build +:: cd build +:: cmake .. +:: make +:: ctest +:: +:: How to clean the "build" folder using cmd line? +:: cd build +:: rd /q /s . + +echo setting MinGW64 environment... + +:: set the location of gmsh SDK / mingw compiler +set GMSHSDK=%~dp0..\lib\gmsh-sdk +set EIGEN=%~dp0..\lib\eigen +set COMPILERPATHS=C:\msys64\mingw64\bin;C:\mingw-w64\mingw64\bin + +:: perform some tests... +IF NOT EXIST %GMSHSDK% ( + ECHO - gmsh-sdk NOT found in %GMSHSDK%!! + @REM PAUSE + @REM EXIT /B +) ELSE ( + ECHO - gmsh-sdk found in %GMSHSDK%. +) + +IF NOT EXIST %EIGEN% ( + ECHO - eigen NOT found in %EIGEN%!! + @REM PAUSE + @REM EXIT /B +) ELSE ( + ECHO - eigen found in %EIGEN%. +) + +FOR %%a IN (%COMPILERPATHS%) DO ( + IF EXIST %%a ( + ECHO - compiler found in %%a. + SET COMPILERPATH=%%a + ) ELSE ( + IF NOT DEFINED COMPILERPATH ( + ECHO - compiler not found in %%a. + ) + ) +) +IF NOT DEFINED COMPILERPATH ( + ECHO => compiler NOT found! + PAUSE + EXIT /B +) + +:: where is gmsh.exe and gmsh-**.dll ? +set PATH=%GMSHSDK%\bin;%GMSHSDK%\lib;%PATH% +:: where is gmsh.h ? +set INCLUDE=%EIGEN%;%GMSHSDK%\include;%INCLUDE% +:: where is gmsh.lib ? +set LIB=%GMSHSDK%\lib;%LIB% +:: where is gmsh.py ? (required only if you want to use the python API) +set PYTHONPATH=%GMSHSDK%\lib;%PYTHONPATH% +:: add path to the compiler to the PATH +set PATH=%COMPILERPATH%;%PATH% +:: add current folder to PATH for cmake/make aliases +set PATH=%~dp0;%PATH% + +:: clear local vars +set GMSHSDK= +set EIGEN= +set COMPILERPATHS= +set COMPILERPATH= + +:: open terminal +CD /d "%~dp0" +CD .. +%comspec% /K diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c59b02e0e589b89e2bee185063832e7eab654225 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,3 @@ +# math0471/examples + +This folder contains code examples that are useful for the project. diff --git a/examples/gmsh_api/CMakeLists.txt b/examples/gmsh_api/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7b47598dc342cdb1e17dd1cdc74c6802cc36c7cb --- /dev/null +++ b/examples/gmsh_api/CMakeLists.txt @@ -0,0 +1,56 @@ +PROJECT(MATH0471 CXX) +CMAKE_MINIMUM_REQUIRED(VERSION 3.12) + +# ------------------------------------------------------------------------------ +# Find libraries and setup compiler +# ------------------------------------------------------------------------------ + +# build type is "" by default in Linux +IF(NOT CMAKE_BUILD_TYPE) + SET( CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE) +ENDIF() + +# enable C++11 +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +IF(APPLE) + # on macOS, do not give priority to frameworks/apps + SET(CMAKE_FIND_APPBUNDLE LAST) + SET(CMAKE_FIND_FRAMEWORK LAST) +ENDIF() + +# find gmsh-sdk +# gmsh.h +FIND_PATH(GMSH_INCLUDE_DIRS NAMES "gmsh.h") +MESSAGE(STATUS "GMSH_INCLUDE_DIRS=" ${GMSH_INCLUDE_DIRS}) +if(NOT GMSH_INCLUDE_DIRS) + MESSAGE(FATAL_ERROR "gmsh.h not found!") +ENDIF() +INCLUDE_DIRECTORIES(${GMSH_INCLUDE_DIRS}) + +# libgmsh.so +FIND_LIBRARY(GMSH_LIBRARIES gmsh) +MESSAGE(STATUS "GMSH_LIBRARIES=" ${GMSH_LIBRARIES}) +IF(NOT GMSH_LIBRARIES) + MESSAGE(FATAL_ERROR "gmsh library not found!") +ENDIF() + +# find Eigen +find_path(EIGEN_INCLUDE_DIRS "Eigen/Dense" + PATHS "${PROJECT_SOURCE_DIR}/lib/eigen" "/usr/include/eigen3") +MESSAGE(STATUS "EIGEN_INCLUDE_DIRS=" ${EIGEN_INCLUDE_DIRS}) +IF(NOT EIGEN_INCLUDE_DIRS) + MESSAGE(FATAL_ERROR "Eigen include dir not found!") +ENDIF() +INCLUDE_DIRECTORIES(${EIGEN_INCLUDE_DIRS}) + +# ------------------------------------------------------------------------------ + +# compile each .cpp into an exe +FILE(GLOB SRCS *.cpp) +FOREACH(SFILE ${SRCS}) + GET_FILENAME_COMPONENT(PROG_NAME ${SFILE} NAME_WE) + ADD_EXECUTABLE(${PROG_NAME} ${SFILE}) + TARGET_LINK_LIBRARIES(${PROG_NAME} ${GMSH_LIBRARIES}) +ENDFOREACH() diff --git a/examples/gmsh_api/README.md b/examples/gmsh_api/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e1283e8ad6d2f103f973c3c8f3ffc6ae65f34988 --- /dev/null +++ b/examples/gmsh_api/README.md @@ -0,0 +1,9 @@ +# gmsh_api + +This folder contains programs related to the presentation named "First use of Gmsh and Gmsh SDK". + +You can copy any example/tutorial from gmsh into this folder so that it gets included in the build process when you run cmake and make. + +Examples can be found in the following folders: +* `gmsh-sdk/share/doc/gmsh/demos/api/` +* `gmsh-sdk/share/doc/gmsh/tutorial/c++/` \ No newline at end of file diff --git a/examples/gmsh_api/code1_loadgeo.cpp b/examples/gmsh_api/code1_loadgeo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..adbac2853fcd37b735bed8e5a9261c1117e88df8 --- /dev/null +++ b/examples/gmsh_api/code1_loadgeo.cpp @@ -0,0 +1,36 @@ +// This program takes a .geo file as argument, generates the mesh of the +// surfaces and displays the result in the Gmsh window. +// +// This example introduces: +// - the gmsh.h header +// - initialize()/finalize() functions +// - C++ namespaces / standard library (std::cout) +// +// How to build/run? (windows) +// [in examples/gmsh_api] +// mkdir build +// cd build +// cmake .. && make && code1_loadgeo.exe ..\rectangle.geo + +#include <gmsh.h> // gmsh main header (READ THIS FILE) +#include <iostream> // for std::cout + +int main(int argc, char **argv) +{ + // the program requires 1 argument: a .geo file. + if (argc < 2) + { + std::cout << "Usage: " << argv[0] << " <geo_file>\n"; + return 0; + } + + gmsh::initialize(); // first thing to do before using any gmsh SDK function + + gmsh::open(argv[1]); // similar to "File / Open" in the GUI + gmsh::model::mesh::generate(2); // similar to "Mesh / 2D" in the GUI + + gmsh::fltk::run(); // opens the gmsh window + + gmsh::finalize(); // clean-up things before ending the program + return 0; +} diff --git a/examples/gmsh_api/code2_getgroups.cpp b/examples/gmsh_api/code2_getgroups.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a917e1aaa72dd9a16a407abd5ffb79a614ece299 --- /dev/null +++ b/examples/gmsh_api/code2_getgroups.cpp @@ -0,0 +1,57 @@ +// This program loops over the physical groups defined in a .geo file +// and prints their names, dimensions and tags. +// +// This example introduces: +// - (dim,tags) +// - std::pair<T1,T2> +// - std::vector<T> +// - gmsh::vectorpair == std::vector<std::pair<int,int>> +// - references / const references +// - how to call a gmsh function (input vs output arguments) +// - std::string +// +// How to build/run? (windows) +// [in examples/gmsh_api] +// mkdir build +// cd build +// cmake .. && make && code2_getgroups.exe ..\rectangle.geo + +#include <gmsh.h> +#include <iostream> + +int main(int argc, char **argv) +{ + if (argc < 2) + { + std::cout << "Usage: " << argv[0] << " <geo_file>\n"; + return 0; + } + + gmsh::initialize(); + + gmsh::open(argv[1]); + gmsh::model::mesh::generate(2); + + // -- code added to code1.cpp (begin) -------------------------------------- + + gmsh::vectorpair dimTags; // output parameter of "getPhysicalGroups", defining an empty vector of pairs + gmsh::model::getPhysicalGroups(dimTags); // vector is filled + + for(int i=0; i<dimTags.size(); ++i) // size gives size of the vector + { + int dim = dimTags[i].first; // dimTags[i] is a std::pair<int,int> + int tag = dimTags[i].second; + + std::string name; // output parameter of "getPhysicalName" + gmsh::model::getPhysicalName(dim, tag, name); + + // prints the data to the console + std::cout << "Physical group (" << dim << "D) " + << "named '"<< name << "' tag=" << tag << '\n'; + } + + // -- code added to code1.cpp (end) ---------------------------------------- + + gmsh::finalize(); + return 0; +} diff --git a/examples/gmsh_api/code3_buildphymap.cpp b/examples/gmsh_api/code3_buildphymap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f6671dad41e428b32a2fe467d3e764a085b7884e --- /dev/null +++ b/examples/gmsh_api/code3_buildphymap.cpp @@ -0,0 +1,69 @@ +// This program loops over the physical groups defined in a .geo file +// and prints their names, dimensions and tags. +// +// This example introduces: +// - std::map +// - Physical Group +// => Entities +// => Element types +// => Elements/nodes hierarchy +// +// How to build/run? (windows) +// [in example/gmsh_api] +// mkdir build +// cd build +// cmake .. && make && code3_buildphymap.exe ..\rectangle.geo + +#include <gmsh.h> +#include <iostream> +#include <map> + +int main(int argc, char **argv) +{ + if (argc < 2) + { + std::cout << "Usage: " << argv[0] << " <geo_file>\n"; + return 0; + } + + gmsh::initialize(); + + gmsh::open(argv[1]); + gmsh::model::mesh::generate(2); + + // -- code added to previous code (begin) ---------------------------------- + // definition of the map: "group_name" => (dim,tag) + std::map< std::string, std::pair<int,int> > groups; + // -- code added to previous code (end) ------------------------------------ + + gmsh::vectorpair dimTags; + gmsh::model::getPhysicalGroups(dimTags); + + for(int i=0; i<dimTags.size(); ++i) + { + int dim = dimTags[i].first; + int tag = dimTags[i].second; + + std::string name; + gmsh::model::getPhysicalName(dim, tag, name); + + std::cout << "Physical group (" << dim << "D) " + << "named '"<< name << "' tag=" << tag << '\n'; + + // -- code added to previous code (begin) ------------------------------ + // set value (dim, tag) for key 'name' + groups[name] = {dim, tag}; + // -- code added to previous code (end) -------------------------------- + } + + // -- code added to previous code (begin) ---------------------------------- + // print map + std::cout << "Physical Groups:\n"; + for (auto &x : groups) + std::cout << "\t '" << x.first << "' => (" << x.second.first << ',' + << x.second.second << ")\n"; + // -- code added to previous code (end) ------------------------------------ + + gmsh::finalize(); + return 0; +} diff --git a/examples/gmsh_api/code4_hierarchy.cpp b/examples/gmsh_api/code4_hierarchy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5247ad261e6cedb042d0973045f8e38d1f32030b --- /dev/null +++ b/examples/gmsh_api/code4_hierarchy.cpp @@ -0,0 +1,94 @@ +// This program loops over the physical groups defined in a .geo file +// and prints their names, dimensions and tags. +// +// This example loops over the gmsh data structure hierarchy: +// - Physical Group +// => contains 1 or more "Entities" +// => contains 1 or more "Element types" +// => access to Elements +// +// How to build/run? (windows) +// [in example/gmsh_api] +// mkdir build +// cd build +// cmake .. && make && code4_hierarchy.exe ..\rectangle.geo + +#include <gmsh.h> +#include <iostream> +#include <map> + +int main(int argc, char **argv) +{ + if (argc < 2) + { + std::cout << "Usage: " << argv[0] << " <geo_file>\n"; + return 0; + } + + gmsh::initialize(); + + gmsh::open(argv[1]); + gmsh::model::mesh::generate(2); + + std::map<std::string, std::pair<int, int>> groups; + gmsh::vectorpair dimTags; + gmsh::model::getPhysicalGroups(dimTags); + for (int i = 0; i < dimTags.size(); ++i) + { + int dim = dimTags[i].first; + int tag = dimTags[i].second; + std::string name; + gmsh::model::getPhysicalName(dim, tag, name); + groups[name] = {dim, tag}; + } + + // -- code added to previous code (begin) ---------------------------------- + + std::string groupname = "left_edge"; + + int dim = groups[groupname].first; + int tag = groups[groupname].second; + if (tag == 0) + { + std::cerr << "Group '" << groupname << "' does not exist!\n"; + return 1; + } + + // get entities of the chosen physical group + std::vector<int> tags; + gmsh::model::getEntitiesForPhysicalGroup(dim, tag, tags); + + std::cout << "Entities in group named '" << groupname << "': "; + for (int i = 0; i < tags.size(); ++i) + std::cout << tags[i] << " "; + std::cout << '\n'; + + // loop over entities + for (int k = 0; k < tags.size(); ++k) + { + std::cout << "Entity (" << dim << "," << tags[k] << "):\n"; + std::vector<int> elementTypes; + std::vector<std::vector<std::size_t>> elementTags; + std::vector<std::vector<std::size_t>> nodeTags; + gmsh::model::mesh::getElements(elementTypes, elementTags, + nodeTags, dim, tags[k]); + + // loop over element types + for (int i = 0; i < elementTypes.size(); ++i) + { + std::cout << "\telement type " << elementTypes[i] << ":\n"; + std::cout << "\t\telements: "; + for (int j = 0; j < elementTags[i].size(); ++j) + std::cout << elementTags[i][j] << " "; + std::cout << '\n'; + std::cout << "\t\tnodes: "; + for (int j = 0; j < nodeTags[i].size(); ++j) + std::cout << nodeTags[i][j] << " "; + std::cout << '\n'; + } + } + // -- code added to previous code (end) ------------------------------------ + + gmsh::finalize(); + return 0; +} diff --git a/examples/gmsh_api/code5_elprops.cpp b/examples/gmsh_api/code5_elprops.cpp new file mode 100644 index 0000000000000000000000000000000000000000..218001b7c5731aa258bb57bc159127cdf842452b --- /dev/null +++ b/examples/gmsh_api/code5_elprops.cpp @@ -0,0 +1,45 @@ +// This program displays info about "element type 1". +// +// How to build/run? (windows) +// [in example/gmsh_api] +// mkdir build +// cd build +// cmake .. && make && code5_elprops.exe + +#include <gmsh.h> +#include <iostream> +#include <map> + +int main(int argc, char **argv) +{ + gmsh::initialize(); + + // input parameter + int elementType = 1; + + // outputs + std::string elementName; + int dim, order, numNodes, numPrimaryNodes; + std::vector<double> localNodeCoord; + + // gmsh call + gmsh::model::mesh::getElementProperties(elementType, + elementName, dim, order, + numNodes, localNodeCoord, + numPrimaryNodes); + + // display data returned by gmsh + std::cout << "Element type " << elementType << ":\n"; + std::cout << "\tname = '" << elementName << "'\n"; + std::cout << "\tdim = " << dim << '\n'; + std::cout << "\torder = " << order << '\n'; + std::cout << "\tnumNodes = " << numNodes << '\n'; + std::cout << "\tlocalNodeCoord = "; + for(int i=0; i<localNodeCoord.size(); ++i) + std::cout << localNodeCoord[i] << " "; + std::cout << '\n'; + std::cout << "\tnumPrimaryNodes = " << numPrimaryNodes << '\n'; + + gmsh::finalize(); + return 0; +} diff --git a/examples/gmsh_api/code6_allnodes.cpp b/examples/gmsh_api/code6_allnodes.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c79187886f0af7d7137a950ee733a865ae87af48 --- /dev/null +++ b/examples/gmsh_api/code6_allnodes.cpp @@ -0,0 +1,50 @@ +// This program gets all the node tags and the coordinates of the model. +// +// How to build/run? (windows) +// [in example/gmsh_api] +// mkdir build +// cd build +// cmake .. && make && code6_allnodes.exe ..\rectangle.geo + +#include <gmsh.h> +#include <iostream> + +int main(int argc, char **argv) +{ + if (argc < 2) + { + std::cout << "Usage: " << argv[0] << " <geo_file>\n"; + return 0; + } + + gmsh::initialize(); + + gmsh::open(argv[1]); + gmsh::model::mesh::generate(2); + + // Renumber the node tags in a continuous sequence. + gmsh::model::mesh::renumberNodes(); + + // output parameters + std::vector<std::size_t> nodeTags; + std::vector<double> coord; + std::vector<double> parametricCoord; + + // gmsh call + gmsh::model::mesh::getNodes(nodeTags, coord, parametricCoord); + + + // display the vectors + std::cout << "\tnodeTags = "; + for(int i=0; i<nodeTags.size(); ++i) + std::cout << nodeTags[i] << " "; + std::cout << '\n'; + + std::cout << "\tcoord = "; + for(int i=0; i<coord.size(); ++i) + std::cout << coord[i] << " "; + std::cout << '\n'; + + gmsh::finalize(); + return 0; +} diff --git a/examples/gmsh_api/code7_parameters.cpp b/examples/gmsh_api/code7_parameters.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6fb94c42c09565875dc7fdf7fc0a2d4a5e829c24 --- /dev/null +++ b/examples/gmsh_api/code7_parameters.cpp @@ -0,0 +1,62 @@ +// This program takes a .geo file as argument, and reads the onelab parameters +// defined by the "SetNumber" command. +// This technique can be used to define additional parameters for the solver +// such as the values for the boundary conditions or material parameters. +// +// How to build/run? (windows) +// [in example/gmsh_api] +// mkdir build +// cd build +// cmake .. && make && code7_parameters.exe ..\rectangle.geo + +#include <gmsh.h> +#include <iostream> +#include <sstream> // for std::stringstream + +int main(int argc, char **argv) +{ + // the program requires 1 argument: a .geo file. + if (argc < 2) + { + std::cout << "Usage: " << argv[0] << " <geo_file>\n"; + return 0; + } + + gmsh::initialize(); + + gmsh::open(argv[1]); + + // get selected keys from the onelab database + std::vector<std::string> keys; + gmsh::onelab::getNames(keys, "(Boundary Conditions|Materials).+"); //see rectangle.geo file, retrieves boundary conditions and mat + for (auto &key : keys) + { + // get corresponding value + std::vector<double> value; + gmsh::onelab::getNumber(key, value); + + // print received data + std::cout << key << ":"; + for (auto &v : value) + std::cout << " " << v; + std::cout << '\n'; + + // expected key structure is "type/group_name/field" + // => split the key string into components + std::stringstream ss(key); + std::vector<std::string> words; + std::string word; + while(std::getline(ss, word, '/')) // read string until '/' + words.push_back(word); + if(words.size()==3) + { + std::cout << "\t.type of data = '" << words[0] << "'\n"; + std::cout << "\t.physical group name = '" << words[1] << "'\n"; + std::cout << "\t.field = '" << words[2] << "'\n"; + std::cout << "\t.value = " << value[0] << '\n'; + } + } + + gmsh::finalize(); + return 0; +} diff --git a/examples/gmsh_api/code8_jacobians.cpp b/examples/gmsh_api/code8_jacobians.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1add737d73b727c851b275af29ad33042ac8c92a --- /dev/null +++ b/examples/gmsh_api/code8_jacobians.cpp @@ -0,0 +1,167 @@ +// This program demonstrates how to convert gmsh vectors to Eigen matrices. +// It also shows: +// - how to select a quadrature type and get the positions and weights of +// the Gauss points. +// - how to compute the inverse of the Jacobians. +// - how to get the values of the shape functions and their derivatives at +// the Gauss points. +// +// How to build/run? (windows) +// [in example/gmsh_api] +// mkdir build +// cd build +// cmake .. && make && code8_jacobians.exe ..\mono.geo + +#include <gmsh.h> +#include <iostream> +#include <map> +#include <Eigen/Dense> // <= new header + +int main(int argc, char **argv) +{ + if (argc < 2) + { + std::cout << "Usage: " << argv[0] << " <geo_file>\n"; + return 0; + } + + gmsh::initialize(); + + gmsh::open(argv[1]); + gmsh::model::mesh::generate(2); + + std::map<std::string, std::pair<int, int>> groups; + gmsh::vectorpair dimTags; + gmsh::model::getPhysicalGroups(dimTags); + for (int i = 0; i < dimTags.size(); ++i) + { + int dim = dimTags[i].first; + int tag = dimTags[i].second; + std::string name; + gmsh::model::getPhysicalName(dim, tag, name); + groups[name] = {dim, tag}; + } + + std::string groupname = "domain"; // <= we choose a physical group here! + + int dim = groups[groupname].first; + int tag = groups[groupname].second; + if (tag == 0) + { + std::cerr << "Group '" << groupname << "' does not exist!\n"; + return 1; + } + + // get entities of the chosen physical group + std::vector<int> tags; + gmsh::model::getEntitiesForPhysicalGroup(dim, tag, tags); + + std::cout << "Entities in group named '" << groupname << "': "; + for (int i = 0; i < tags.size(); ++i) + std::cout << tags[i] << " "; + std::cout << '\n'; + + // loop over entities + for (int k = 0; k < tags.size(); ++k) + { + std::cout << "Entity (" << dim << "," << tags[k] << "):\n"; + std::vector<int> elementTypes; + std::vector<std::vector<std::size_t>> elementTags; + std::vector<std::vector<std::size_t>> nodeTags; + gmsh::model::mesh::getElements(elementTypes, elementTags, + nodeTags, dim, tags[k]); + + // loop over element types + for (int i = 0; i < elementTypes.size(); ++i) + { + // -- CODE ADDED TO PREVIOUS EXAMPLE (begin) ----------------------- + + // get element name as a string + std::string elementName; + int edim, eorder, enumNodes, enumPrimaryNodes; + std::vector<double> localNodeCoord; + gmsh::model::mesh::getElementProperties(elementTypes[i], + elementName, dim, eorder, + enumNodes, localNodeCoord, + enumPrimaryNodes); + std::cout << '\t' << elementName << " order=" << eorder + << " nodes=" << enumNodes << '\n'; + + // get Gauss points coordinates and weights + std::vector<double> localCoords, weights; + gmsh::model::mesh::getIntegrationPoints(elementTypes[i], + "CompositeGauss2", // "Gauss2" + localCoords, weights); + std::cout << "\t" << weights.size() << " integration points\n"; + + std::cout << "\tweights: "; + for (int j = 0; j < weights.size(); ++j) + std::cout << weights[j] << " "; + std::cout << '\n'; + + std::cout << "\tlocalCoords (gmsh): "; + for (int j = 0; j < localCoords.size(); ++j) + std::cout << localCoords[j] << " "; + std::cout << '\n'; + + // convert to Eigen format + Eigen::Map<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>> + localCoords_E(&localCoords[0], localCoords.size()/3, 3); + std::cout << "\tlocalCoords (Eigen):\n"; + Eigen::IOFormat fmt(4, 0, ", ", "\n", "\t\t[", "]"); + std::cout << localCoords_E.format(fmt) << '\n'; + + // get Jacobians + std::vector<double> jacobians, determinants, coords; + gmsh::model::mesh::getJacobians(elementTypes[i], + localCoords, jacobians, + determinants, coords); + std::cout << "\tGot " << determinants.size() + << " Jacobians for type " << elementTypes[i] << "\n"; + + std::cout << "\tjacobians (gmsh): "; + for (int j = 0; j < jacobians.size(); ++j) + std::cout << jacobians[j] << " "; + std::cout << '\n'; + + // convert first jacobian to Eigen format + Eigen::Map<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> > + jacob(&jacobians[0], 3, 3); + std::cout << "\tfirst jacobian (Eigen):\n" + << jacob.format(fmt) << '\n'; + + // compute the inverse with Eigen + Eigen::Matrix<double, 3, 3> jacobinv = jacob.inverse(); + std::cout << "\tinverse of the first jacobian (Eigen):\n" + << jacobinv.format(fmt) << "\n"; + + // Compute J*Jinv with Eigen "*" operator + std::cout << "\tVerification (J*Jinv):\n" + << (jacob*jacobinv).format(fmt) << '\n'; + + // shape functions and derivatives at the Gauss points + int numComponents, numOrientations; + std::vector<double> basisFunctions; + gmsh::model::mesh::getBasisFunctions(elementTypes[i], localCoords, + "Lagrange", numComponents, + basisFunctions, + numOrientations); + std::cout << "\tGot " << basisFunctions.size() + << " basis functions for type " + << elementTypes[i] << '\n'; + + gmsh::model::mesh::getBasisFunctions(elementTypes[i], localCoords, + "GradLagrange", numComponents, + basisFunctions, + numOrientations); + std::cout << "\tGot " << basisFunctions.size() + << " basis function gradients for type " + << elementTypes[i] << '\n'; + + // -- CODE ADDED TO PREVIOUS EXAMPLE (end) ------------------------- + } + } + + gmsh::finalize(); + return 0; +} diff --git a/examples/gmsh_api/code9_views.cpp b/examples/gmsh_api/code9_views.cpp new file mode 100644 index 0000000000000000000000000000000000000000..20531988035b46a7e350d79cbb60c1d9bcd79bb2 --- /dev/null +++ b/examples/gmsh_api/code9_views.cpp @@ -0,0 +1,164 @@ +// This example generate some results over a mesh +// +// How to use this example? +// +// 1.build the code: +// cd build +// cmake .. +// make +// 2.generate a mesh from a geo file: +// gmsh -2 -order 3 ..\src\mymesh.geo +// 3.run the program with the msh as argument +// bin\myview.exe ..\src\mymesh.msh +// 4.display the generated msh data +// gmsh results.msh + +#include <gmsh.h> +#include <iostream> + +int main(int argc, char **argv) +{ + if (argc < 2) + { + std::cout << "Usage: " << argv[0] << " <geo_file>\n"; + return 0; + } + + gmsh::initialize(); + + gmsh::open(argv[1]); + gmsh::model::mesh::generate(2); + + std::vector<std::string> names; + gmsh::model::list(names); + std::cout << "the file contains " << names.size() << " model names\n"; + + // get all the nodes + std::vector<std::size_t> nodeTags; + std::vector<double> coords; + std::vector<double> parametricCoord; + gmsh::model::mesh::getNodes(nodeTags, coords, parametricCoord); + + std::cout << "the mesh has " << nodeTags.size() << " nodes\n"; + + // get all the elements for dim=2 + std::vector<int> elementTypes; + std::vector<std::vector<std::size_t>> elementTags; + std::vector<std::vector<std::size_t>> elnodeTags; + gmsh::model::mesh::getElements(elementTypes, elementTags, elnodeTags, 2); + + // compute total number of 2D elements + int nbelems=0; + for(std::size_t i=0; i< elementTags.size(); ++i) + nbelems+=elementTags[i].size(); + std::cout << "the mesh has " << nbelems << " elements\n"; + + // Create a new post-processing view + int viewtag1 = gmsh::view::add("NodeData"); + int viewtag2 = gmsh::view::add("ElementNodeData"); + + std::size_t nstep = 20; + double Lx = 20.; + double v = 10.0; + double tend = Lx / v; + for (std::size_t step = 0; step < nstep; step++) + { + double time = tend * ((double)step / nstep); + + // ** NodeData + // loop over the nodes and compute some values from their coordinates + std::vector<std::vector<double>> data1(nodeTags.size()); // check this size!! + for (std::size_t i = 0; i < nodeTags.size(); ++i) + { + std::size_t tag = nodeTags[i]; + double x = coords[i * 3 + 0]; + double y = coords[i * 3 + 1]; + double z = coords[i * 3 + 2]; + + double val = sin(2 * M_PI * (x - v * time) / Lx * 2) + y; + data1[i].resize(1); + data1[i][0] = val; + } + gmsh::view::addModelData(viewtag1, step, names[0], "NodeData", + nodeTags, data1, time); + + // ** ElementNodeData + // loop over the elements and compute some values at the coords of its nodes + + std::vector<std::vector<double>> data2(nbelems); + std::vector<std::size_t> elems2D(nbelems); + + // for each element type + //std::cout << "there are " << elementTypes.size() << " element type(s)\n"; + int k=0; + for (std::size_t ityp = 0; ityp < elementTypes.size(); ++ityp) + { + // get info about this type of element + std::string name; + int dim, order, numNodes, numPrimaryNodes; + std::vector<double> paramCoord; + gmsh::model::mesh::getElementProperties(elementTypes[ityp], name, dim, order, + numNodes, paramCoord, numPrimaryNodes); + //std::cout << "element type has " << numNodes << " nodes\n"; + + // select element/node tags for this type of element + auto &etags = elementTags[ityp]; + auto &enods = elnodeTags[ityp]; + + // loop over elements of type "ityp" + //std::cout << "loop over the " << etags.size() << " elements\n"; + for (std::size_t i = 0; i < etags.size(); ++i) + { + elems2D[k] = etags[i]; + + double offset = (elems2D[k]%2) *0.5; // so that fields are discontinious across elements + + data2[k].resize(numNodes); + // loop over nodes of the element + for (std::size_t j = 0; j < numNodes; ++j) + { + int nodeTag = enods[numNodes * i + j]; + + std::vector<double> nodecoord; + std::vector<double> nodepcoord; + int dim, tag; + gmsh::model::mesh::getNode(nodeTag, nodecoord, nodepcoord, dim, tag); + double x = nodecoord[0]; + double y = nodecoord[1]; + double z = nodecoord[2]; +/* + double x = coords[nod * 3 + 0]; + double y = coords[nod * 3 + 1]; + double z = coords[nod * 3 + 2]; +*/ + double val = sin(2 * M_PI * (x - v * time) / Lx * 2) + y; + data2[k][j] = val + offset; + } + k++; + } + } + gmsh::view::addModelData(viewtag2, step, names[0], "ElementNodeData", + elems2D, data2, time, 1); // the last ,1 is important! + + } + + // save the results to disk + gmsh::view::write(viewtag1, "results.msh"); + gmsh::view::write(viewtag2, "results.msh", true); + + // set display options & view results + gmsh::option::setNumber("View[0].IntervalsType", 3); + gmsh::option::setNumber("View[0].AdaptVisualizationGrid", 1); + gmsh::option::setNumber("View[0].MaxRecursionLevel", 3); + gmsh::option::setNumber("View[0].TargetError", -0.0001); + + gmsh::option::setNumber("View[1].IntervalsType", 3); + gmsh::option::setNumber("View[1].AdaptVisualizationGrid", 1); + gmsh::option::setNumber("View[1].MaxRecursionLevel", 3); + gmsh::option::setNumber("View[1].TargetError", -0.0001); + + gmsh::fltk::run(); + + gmsh::finalize(); + return 0; +} diff --git a/examples/gmsh_api/mono.geo b/examples/gmsh_api/mono.geo new file mode 100644 index 0000000000000000000000000000000000000000..aa4b82811cfaccd441476cb733ea10af6508a25d --- /dev/null +++ b/examples/gmsh_api/mono.geo @@ -0,0 +1,22 @@ +Lx = 10; +Ly = 10; +nx = 1; +ny = 1; + +Point(1) = {0, 0, 0, 1.0}; +Point(2) = {Lx, 0, 0, 1.0}; +Point(3) = {Lx, Ly, 0, 1.0}; +Point(4) = {0, Ly, 0, 1.0}; +Line(1) = {1, 2}; +Line(2) = {2, 3}; +Line(3) = {3, 4}; +Line(4) = {4, 1}; +Curve Loop(1) = {4, 1, 2, 3}; +Plane Surface(1) = {1}; +Transfinite Curve {3, 1} = nx+1 Using Progression 1; +Transfinite Curve {4, 2} = ny+1 Using Progression 1; +Transfinite Surface {1}; + +Recombine Surface {1}; // quads instead of triangles + +Physical Surface("domain", 6) = {1}; diff --git a/examples/gmsh_api/my_code.cpp b/examples/gmsh_api/my_code.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1cf01494c7ef97de5fcc074e0b094abf2913cfab --- /dev/null +++ b/examples/gmsh_api/my_code.cpp @@ -0,0 +1,216 @@ +// This program takes a .geo file as argument, generates the mesh of the +// surfaces and displays the result in the Gmsh window. +// +// This example introduces: +// - the gmsh.h header +// - initialize()/finalize() functions +// - C++ namespaces / standard library (std::cout) +// +// How to build/run? (windows) +// [in examples/gmsh_api] +// mkdir build +// cd build +// cmake .. && make && code1_loadgeo.exe ..\rectangle.geo + +#include <gmsh.h> // gmsh main header (READ THIS FILE) +#include <iostream> // for std::cout +#include <map> +#include <sstream> // for std::stringstream +#include <Eigen/Dense> // Eigen library + +int main(int argc, char **argv) +{ + /*--------------INIT----------------*/ + // the program requires 1 argument: a .geo file. + if (argc < 2) + { + std::cout << "Usage: " << argv[0] << " <geo_file>\n"; + return 0; + } + + gmsh::initialize(); // first thing to do before using any gmsh SDK function + + gmsh::open(argv[1]); // similar to "File / Open" in the GUI + gmsh::model::mesh::generate(2); // similar to "Mesh / 2D" in the GUI + + /*--------get physical groups (more particularly: the domain)--------------*/ + std::map<std::string, std::pair<int, int>> groups; + gmsh::vectorpair dimTags; + gmsh::model::getPhysicalGroups(dimTags); + for (int i = 0; i < dimTags.size(); ++i) + { + int dim = dimTags[i].first; + int tag = dimTags[i].second; + std::string name; + gmsh::model::getPhysicalName(dim, tag, name); + groups[name] = {dim, tag}; + } + + /*-----------get physical properties of domain----------------------*/ + std::vector<std::string> keys; + gmsh::onelab::getNames(keys, "(Materials).+"); + double E; + double nu; + for (auto &key : keys) + { + // get corresponding value + std::vector<double> value; + gmsh::onelab::getNumber(key, value); + // expected key structure is "type/group_name/field" + // => split the key string into components + std::stringstream ss(key); + std::vector<std::string> words; + std::string word; + while(std::getline(ss, word, '/')) // read string until '/' + words.push_back(word); + if(words.size()==3) + { + if(words[2].compare("Young") == 0){ + E = value[0]; + } + if(words[2].compare("Poisson") == 0){ + nu = value[0]; + } + } + } + // std::cout << "E = " << E << "\n"; + // std::cout << "nu = " << nu << "\n"; + + /*-----------------H matrix------------------*/ + Eigen::Matrix<double, 3, 3> H_matrix; + H_matrix(0,0) = 1.; + H_matrix(1,0) = nu; + H_matrix(2,0) = 0.; + H_matrix(0,1) = nu; + H_matrix(1,1) = 1.; + H_matrix(2,1) = 0.; + H_matrix(0,2) = 0.; + H_matrix(1,2) = 0.; + H_matrix(2,2) = (1-nu)/2; + H_matrix = E/(1-nu*nu)*H_matrix; + // std::cout << H_matrix << '\n'; + + /*------------loop over elements of domain------------------------*/ + std::string groupname = "domain"; + + int dim = groups[groupname].first; + int tag = groups[groupname].second; + if (tag == 0) + { + std::cerr << "Group '" << groupname << "' does not exist!\n"; + return 1; + } + + // get entities of the domain + std::vector<int> tags; + gmsh::model::getEntitiesForPhysicalGroup(dim, tag, tags); + + std::cout << "Entities in group named '" << groupname << "': "; + for (int i = 0; i < tags.size(); ++i) + std::cout << tags[i] << " "; + std::cout << '\n'; + + // loop over entities + for (int k = 0; k < tags.size(); ++k) + { + std::cout << "Entity (" << dim << "," << tags[k] << "):\n"; + std::vector<int> elementTypes; + std::vector<std::vector<std::size_t>> elementTags; + std::vector<std::vector<std::size_t>> nodeTags; + gmsh::model::mesh::getElements(elementTypes, elementTags, + nodeTags, dim, tags[k]); + + // loop over element types + for (int i = 0; i < elementTypes.size(); ++i) + { + std::cout << "\telement type " << elementTypes[i] << ":\n"; + std::cout << "\t\telements: "; + for (int j = 0; j < elementTags[i].size(); ++j) + std::cout << elementTags[i][j] << " "; + std::cout << '\n'; + + // get Gauss points coordinates and weights + std::vector<double> localCoords, weights; + gmsh::model::mesh::getIntegrationPoints(elementTypes[i], + "CompositeGauss2", // "Gauss2" + localCoords, weights); + std::cout << "\t" << weights.size() << " integration points\n"; + + std::cout << "\tweights: "; + for (int j = 0; j < weights.size(); ++j) + std::cout << weights[j] << " "; + std::cout << '\n'; + + std::cout << "\tlocalCoords (gmsh): "; + for (int j = 0; j < localCoords.size(); ++j) + std::cout << localCoords[j] << " "; + std::cout << '\n'; + + // convert to Eigen format + Eigen::Map<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>> + localCoords_E(&localCoords[0], localCoords.size()/3, 3); + std::cout << "\tlocalCoords (Eigen):\n"; + Eigen::IOFormat fmt(4, 0, ", ", "\n", "\t\t[", "]"); + std::cout << localCoords_E.format(fmt) << '\n'; + + // get determinants and Jacobians + std::vector<double> jacobians, determinants, coords; + gmsh::model::mesh::getJacobians(elementTypes[i], + localCoords, jacobians, + determinants, coords); + std::cout << "\tGot " << determinants.size() + << " Jacobians for type " << elementTypes[i] << "\n"; + + std::cout << "\tdeterminants (gmsh): "; + for (int j = 0; j < determinants.size(); ++j) + std::cout << determinants[j] << " "; + std::cout << '\n'; + + std::cout << "\tjacobians (gmsh): "; + for (int j = 0; j < jacobians.size(); ++j) + std::cout << jacobians[j] << " "; + std::cout << '\n'; + + // derivatives of the shape functions at the Gauss points of the reference element + // we are here working with derivatives in the reference space, same for all elements + int numComponents, numOrientations; + std::vector<double> basisFunctions; + gmsh::model::mesh::getBasisFunctions(elementTypes[i], localCoords, + "GradLagrange", numComponents, + basisFunctions, + numOrientations); + std::cout << "\tbasis functions (gmsh): "; + for (int j = 0; j < basisFunctions.size(); ++j) + std::cout << basisFunctions[j] << " "; + std::cout << '\n'; + + /*---------computing the B matrix of the first element's (i.e elementTags[i][0]) first GP:------*/ + // ne serait-ça pas plutôt utile de directement convertir les Jacobiens en 2D ? travailler en full 2D + // et faire pareil pour l'évaluation des gradients des basis functions ci-dessus ? + std::vector<double> b_matrix; + + // convert first jacobian to Eigen format + Eigen::Map<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> > + jacob(&jacobians[0], 3, 3); + std::cout << "\tfirst jacobian (Eigen):\n" + << jacob.format(fmt) << '\n'; + + // compute the inverse with Eigen + Eigen::Matrix<double, 3, 3> jacobinv = jacob.inverse(); + std::cout << "\tinverse of the first jacobian (Eigen):\n" + << jacobinv.format(fmt) << "\n"; + + // compute the transpose of the inverse with Eigen + Eigen::Matrix<double, 3,3> jacobinvtrans = jacobinv.transpose(); + std::cout << "\ttranspose of the inverse of the first jacobian (Eigen):\n" + << jacobinvtrans.format(fmt) << "\n"; + + for (int j = 0; j < weights.size(); ++j){ // looping over the number of gauss points ( = number of shape functions (A VERIF)) + + } + } + } + + gmsh::finalize(); // clean-up things before ending the program + return 0; +} diff --git a/examples/gmsh_api/my_geo.geo b/examples/gmsh_api/my_geo.geo new file mode 100644 index 0000000000000000000000000000000000000000..24307d2d5b28048c8ec38faf100ebb7faff4aef3 --- /dev/null +++ b/examples/gmsh_api/my_geo.geo @@ -0,0 +1,32 @@ +Lx = 5; +Ly = 2; +nx = 5; +ny = 1; + +Point(1) = {0, 0, 0, 1.0}; +Point(2) = {Lx, 0, 0, 1.0}; +Point(3) = {Lx, Ly, 0, 1.0}; +Point(4) = {0, Ly, 0, 1.0}; +Line(1) = {1, 2}; +Line(2) = {2, 3}; +Line(3) = {3, 4}; +Line(4) = {4, 1}; +Curve Loop(1) = {4, 1, 2, 3}; +Plane Surface(1) = {1}; +Transfinite Curve {3, 1} = nx+1 Using Progression 1; +Transfinite Curve {4, 2} = ny+1 Using Progression 1; +Transfinite Surface {1}; + +Recombine Surface {1}; // quads instead of triangles + +Physical Curve("left_edge", 5) = {4}; +Physical Surface("domain", 6) = {1}; +Physical Curve("top_edge",7) = {3}; + +// additional parameters given to the solver +SetNumber("Boundary Conditions/left_edge/ux", 0.); +SetNumber("Boundary Conditions/left_edge/uy", 0.); +SetNumber("Materials/domain/Young", 210e3); +SetNumber("Materials/domain/Poisson", 0.3); +SetNumber("Boundary Conditions/top_edge/tx", 0.); +SetNumber("Boundary Conditions/top_edge/ty", -100.); \ No newline at end of file diff --git a/examples/gmsh_api/rectangle.db b/examples/gmsh_api/rectangle.db new file mode 100644 index 0000000000000000000000000000000000000000..2c3d83363d0063aefb0053171b65346027c353ad Binary files /dev/null and b/examples/gmsh_api/rectangle.db differ diff --git a/examples/gmsh_api/rectangle.geo b/examples/gmsh_api/rectangle.geo new file mode 100644 index 0000000000000000000000000000000000000000..a0666de562275a53648095e91cc179a9ca36d4fa --- /dev/null +++ b/examples/gmsh_api/rectangle.geo @@ -0,0 +1,32 @@ +Lx = 10; +Ly = 2; +nx = 5; +ny = 1; + +Point(1) = {0, 0, 0, 1.0}; +Point(2) = {Lx, 0, 0, 1.0}; +Point(3) = {Lx, Ly, 0, 1.0}; +Point(4) = {0, Ly, 0, 1.0}; +Line(1) = {1, 2}; +Line(2) = {2, 3}; +Line(3) = {3, 4}; +Line(4) = {4, 1}; +Curve Loop(1) = {4, 1, 2, 3}; +Plane Surface(1) = {1}; +Transfinite Curve {3, 1} = nx+1 Using Progression 1; +Transfinite Curve {4, 2} = ny+1 Using Progression 1; +Transfinite Surface {1}; + +Recombine Surface {1}; // quads instead of triangles + +Physical Curve("left_edge", 5) = {4}; +Physical Surface("domain", 6) = {1}; +Physical Curve("top_edge",7) = {3}; + +// additional parameters given to the solver +SetNumber("Boundary Conditions/left_edge/ux", 0.); +SetNumber("Boundary Conditions/left_edge/uy", 0.5); +SetNumber("Materials/domain/Young", 210e3); +SetNumber("Materials/domain/Poisson", 0.3); +SetNumber("Boundary Conditions/top_edge/tx", 0.); +SetNumber("Boundary Conditions/top_edge/ty", -100.); \ No newline at end of file diff --git a/examples/sparse_solve/CMakeLists.txt b/examples/sparse_solve/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..c0c4e0c802b630d3aea205cbcee1b970c9775aba --- /dev/null +++ b/examples/sparse_solve/CMakeLists.txt @@ -0,0 +1,52 @@ +PROJECT(SPARSE_SOLVE CXX) +CMAKE_MINIMUM_REQUIRED(VERSION 3.12) + +# ------------------------------------------------------------------------------ +# Find libraries and setup compiler +# ------------------------------------------------------------------------------ + +# build type is "" by default in Linux +IF(NOT CMAKE_BUILD_TYPE) + SET( CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE) +ENDIF() + +# enable C++11 +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +IF(APPLE) + # on macOS, do not give priority to frameworks/apps + SET(CMAKE_FIND_APPBUNDLE LAST) + SET(CMAKE_FIND_FRAMEWORK LAST) +ENDIF() + +# find gmsh-sdk +# gmsh.h +FIND_PATH(GMSH_INCLUDE_DIRS NAMES "gmsh.h") +MESSAGE(STATUS "GMSH_INCLUDE_DIRS=" ${GMSH_INCLUDE_DIRS}) +if(NOT GMSH_INCLUDE_DIRS) + MESSAGE(FATAL_ERROR "gmsh.h not found!") +ENDIF() +INCLUDE_DIRECTORIES(${GMSH_INCLUDE_DIRS}) + +# libgmsh.so +FIND_LIBRARY(GMSH_LIBRARIES gmsh) +MESSAGE(STATUS "GMSH_LIBRARIES=" ${GMSH_LIBRARIES}) +IF(NOT GMSH_LIBRARIES) + MESSAGE(FATAL_ERROR "gmsh library not found!") +ENDIF() + +# find Eigen +find_path(EIGEN_INCLUDE_DIRS "Eigen/Dense" + PATHS "${PROJECT_SOURCE_DIR}/lib/eigen" "/usr/include/eigen3") +MESSAGE(STATUS "EIGEN_INCLUDE_DIRS=" ${EIGEN_INCLUDE_DIRS}) +IF(NOT EIGEN_INCLUDE_DIRS) + MESSAGE(FATAL_ERROR "Eigen include dir not found!") +ENDIF() +INCLUDE_DIRECTORIES(${EIGEN_INCLUDE_DIRS}) + +# ------------------------------------------------------------------------------ + +SET(SRCS Tutorial_sparse_example.cpp Tutorial_sparse_example_details.cpp) +ADD_EXECUTABLE(solve_sparse ${SRCS}) +TARGET_LINK_LIBRARIES(solve_sparse ${GMSH_LIBRARIES}) diff --git a/examples/sparse_solve/README.md b/examples/sparse_solve/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3531cbb942b9fa3cd97b229fbade080bbd5ae45f --- /dev/null +++ b/examples/sparse_solve/README.md @@ -0,0 +1,5 @@ +# sparse_solve + +This program solves a sparse linear system with Eigen. + +The files are directly taken from the Eigen source tree and put here without any modification. diff --git a/examples/sparse_solve/Tutorial_sparse_example.cpp b/examples/sparse_solve/Tutorial_sparse_example.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f3587e82e3c8f4f2adbb72434e726c312a230eae --- /dev/null +++ b/examples/sparse_solve/Tutorial_sparse_example.cpp @@ -0,0 +1,38 @@ +#include <Eigen/Sparse> +#include <vector> +#include <iostream> + +typedef Eigen::SparseMatrix<double> SpMat; // declares a column-major sparse matrix type of double +typedef Eigen::Triplet<double> T; + +void buildProblem(std::vector<T>& coefficients, Eigen::VectorXd& b, int n); +void saveAsBitmap(const Eigen::VectorXd& x, int n, const char* filename); + +int main(int argc, char** argv) +{ + // if(argc!=2) { + // std::cerr << "Error: expected one and only one argument.\n"; + // return -1; + // } + + int n = 300; // size of the image + int m = n*n; // number of unknows (=number of pixels) + + // Assembly: + std::vector<T> coefficients; // list of non-zeros coefficients + Eigen::VectorXd b(m); // the right hand side-vector resulting from the constraints + buildProblem(coefficients, b, n); + + SpMat A(m,m); + A.setFromTriplets(coefficients.begin(), coefficients.end()); + + // Solving: + Eigen::SimplicialCholesky<SpMat> chol(A); // performs a Cholesky factorization of A + Eigen::VectorXd x = chol.solve(b); // use the factorization to solve for the given right hand side + + // Export the result to a file: + // saveAsBitmap(x, n, argv[1]); + + return 0; +} + diff --git a/examples/sparse_solve/Tutorial_sparse_example_details.cpp b/examples/sparse_solve/Tutorial_sparse_example_details.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2e628363b65f4a57eecbb7c137cfd263e255a8c8 --- /dev/null +++ b/examples/sparse_solve/Tutorial_sparse_example_details.cpp @@ -0,0 +1,44 @@ +#include <Eigen/Sparse> +#include <vector> +// #include <QImage> + +typedef Eigen::SparseMatrix<double> SpMat; // declares a column-major sparse matrix type of double +typedef Eigen::Triplet<double> T; + +void insertCoefficient(int id, int i, int j, double w, std::vector<T>& coeffs, + Eigen::VectorXd& b, const Eigen::VectorXd& boundary) +{ + int n = int(boundary.size()); + int id1 = i+j*n; + + if(i==-1 || i==n) b(id) -= w * boundary(j); // constrained coefficient + else if(j==-1 || j==n) b(id) -= w * boundary(i); // constrained coefficient + else coeffs.push_back(T(id,id1,w)); // unknown coefficient +} + +void buildProblem(std::vector<T>& coefficients, Eigen::VectorXd& b, int n) +{ + b.setZero(); + Eigen::ArrayXd boundary = Eigen::ArrayXd::LinSpaced(n, 0,M_PI).sin().pow(2); + for(int j=0; j<n; ++j) + { + for(int i=0; i<n; ++i) + { + int id = i+j*n; + insertCoefficient(id, i-1,j, -1, coefficients, b, boundary); + insertCoefficient(id, i+1,j, -1, coefficients, b, boundary); + insertCoefficient(id, i,j-1, -1, coefficients, b, boundary); + insertCoefficient(id, i,j+1, -1, coefficients, b, boundary); + insertCoefficient(id, i,j, 4, coefficients, b, boundary); + } + } +} + +// void saveAsBitmap(const Eigen::VectorXd& x, int n, const char* filename) +// { +// Eigen::Array<unsigned char,Eigen::Dynamic,Eigen::Dynamic> bits = (x*255).cast<unsigned char>(); +// QImage img(bits.data(), n,n,QImage::Format_Indexed8); +// img.setColorCount(256); +// for(int i=0;i<256;i++) img.setColor(i,qRgb(i,i,i)); +// img.save(filename); +// } diff --git a/lib/README.md b/lib/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0cf64c7f9b1e252d8cdc426b5f4385e8b7dbfcbd --- /dev/null +++ b/lib/README.md @@ -0,0 +1,3 @@ +# math0471/lib + +This folder contains scripts for downloading the 2 external libraries that are required for this project. \ No newline at end of file diff --git a/lib/get_eigen.cmd b/lib/get_eigen.cmd new file mode 100644 index 0000000000000000000000000000000000000000..7930122a0a8ff970b4abed92e447d4f41ce4ad55 --- /dev/null +++ b/lib/get_eigen.cmd @@ -0,0 +1,18 @@ +::@echo off +:: download eigen & extract to current folder + +set version=3.4.0 +set file=eigen-%version%.zip + +:: download file +del %file% >nul 2>&1 +bitsadmin /transfer get_eigen /dynamic /download /priority foreground "https://gitlab.com/libeigen/eigen/-/archive/%version%/%file%" "%CD%\%file%" + +:: unzip +rd /Q/S eigen-%version% >nul 2>&1 +powershell.exe -nologo -noprofile -command "& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('%CD%\%file%', '%CD%'); }" +del %file% >nul 2>&1 + +:: rename folder +rd /Q/S eigen >nul 2>&1 +ren eigen-%version% eigen diff --git a/lib/get_eigen.sh b/lib/get_eigen.sh new file mode 100644 index 0000000000000000000000000000000000000000..5f6e9e75f1fa310fd0e42eae2c757e5ba5f3c1ba --- /dev/null +++ b/lib/get_eigen.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set -e + +VERSION=3.4.0 + +wget -q https://gitlab.com/libeigen/eigen/-/archive/${VERSION}/eigen-${VERSION}.tar.gz +tar xzf eigen-${VERSION}.tar.gz +rm eigen-${VERSION}.tar.gz +rm -rf eigen +mv eigen-${VERSION} eigen diff --git a/lib/get_gmsh.cmd b/lib/get_gmsh.cmd new file mode 100644 index 0000000000000000000000000000000000000000..e960844e2b7b81e7b989979b1fcfb22c6e04a926 --- /dev/null +++ b/lib/get_gmsh.cmd @@ -0,0 +1,21 @@ +::@echo off +:: download gmsh-sdk & extract to current folder + +set gmsh=gmsh-4.9.3-Windows64-sdk +set file=%gmsh%.zip + +:: download file +del %file% >nul 2>&1 +bitsadmin /transfer get_gmsh /dynamic /download /priority foreground "https://gmsh.info/bin/Windows/%file%" "%CD%\%file%" + +:: unzip +rd /Q/S %gmsh% >nul 2>&1 +powershell.exe -nologo -noprofile -command "& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('%CD%\%file%', '%CD%'); }" +del %file% >nul 2>&1 + +:: rename folder +rd /Q/S gmsh-sdk >nul 2>&1 +ren %gmsh% gmsh-sdk + +:: copy dll to bin folder so that double-clic on gmsh.exe works! +copy /Y gmsh-sdk\lib\gmsh-*.dll gmsh-sdk\bin\ diff --git a/lib/get_gmsh.sh b/lib/get_gmsh.sh new file mode 100644 index 0000000000000000000000000000000000000000..0fb81224d41b54b17d0aed8b01f45ce743934347 --- /dev/null +++ b/lib/get_gmsh.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +set -e + +VERSION=4.9.3 + +if [[ "$OSTYPE" == "linux-gnu" ]]; then + wget -q https://gmsh.info/bin/Linux/gmsh-${VERSION}-Linux64-sdk.tgz + tar xzf gmsh-${VERSION}-Linux64-sdk.tgz + rm gmsh-${VERSION}-Linux64-sdk.tgz + rm -rf gmsh-sdk + mv gmsh-${VERSION}-Linux64-sdk gmsh-sdk +elif [[ "$OSTYPE" == "darwin"* ]]; then + wget -q https://gmsh.info/bin/MacOSX/gmsh-${VERSION}-MacOSX-sdk.tgz + tar xzf gmsh-${VERSION}-MacOSX-sdk.tgz + rm gmsh-${VERSION}-MacOSX-sdk.tgz + rm -rf gmsh-sdk + mv gmsh-${VERSION}-MacOSX-sdk gmsh-sdk +else + echo "unknown system" +fi diff --git a/srcs/CMakeLists.txt b/srcs/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..e71010cdbebb28ac31889be22a7b02481935e378 --- /dev/null +++ b/srcs/CMakeLists.txt @@ -0,0 +1,52 @@ +PROJECT(MATH0471 CXX) +CMAKE_MINIMUM_REQUIRED(VERSION 3.12) + +# ------------------------------------------------------------------------------ +# Find libraries and setup compiler +# ------------------------------------------------------------------------------ + +# build type is "" by default in Linux +IF(NOT CMAKE_BUILD_TYPE) + SET( CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE) +ENDIF() + +# enable C++11 +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +IF(APPLE) + # on macOS, do not give priority to frameworks/apps + SET(CMAKE_FIND_APPBUNDLE LAST) + SET(CMAKE_FIND_FRAMEWORK LAST) +ENDIF() + +# find gmsh-sdk +# gmsh.h +FIND_PATH(GMSH_INCLUDE_DIRS NAMES "gmsh.h") +MESSAGE(STATUS "GMSH_INCLUDE_DIRS=" ${GMSH_INCLUDE_DIRS}) +if(NOT GMSH_INCLUDE_DIRS) + MESSAGE(FATAL_ERROR "gmsh.h not found!") +ENDIF() +INCLUDE_DIRECTORIES(${GMSH_INCLUDE_DIRS}) + +# libgmsh.so +FIND_LIBRARY(GMSH_LIBRARIES gmsh) +MESSAGE(STATUS "GMSH_LIBRARIES=" ${GMSH_LIBRARIES}) +IF(NOT GMSH_LIBRARIES) + MESSAGE(FATAL_ERROR "gmsh library not found!") +ENDIF() + +# find Eigen +find_path(EIGEN_INCLUDE_DIRS "Eigen/Dense" + PATHS "${PROJECT_SOURCE_DIR}/lib/eigen" "/usr/include/eigen3") +MESSAGE(STATUS "EIGEN_INCLUDE_DIRS=" ${EIGEN_INCLUDE_DIRS}) +IF(NOT EIGEN_INCLUDE_DIRS) + MESSAGE(FATAL_ERROR "Eigen include dir not found!") +ENDIF() +INCLUDE_DIRECTORIES(${EIGEN_INCLUDE_DIRS}) + +# ------------------------------------------------------------------------------ + +FILE(GLOB SRCS *.cpp) +ADD_EXECUTABLE(solver ${SRCS}) +TARGET_LINK_LIBRARIES(solver ${GMSH_LIBRARIES}) diff --git a/srcs/README.md b/srcs/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c265be7582c451b283e289136a4088a4474670cd --- /dev/null +++ b/srcs/README.md @@ -0,0 +1,3 @@ +# srcs + +You can start your project here. diff --git a/srcs/main.cpp b/srcs/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..05f39ef006504a19e39564482cee715b115b8940 --- /dev/null +++ b/srcs/main.cpp @@ -0,0 +1,8 @@ +#include <iostream> + +int main(int argc, char **argv) +{ + std::cout << "Hello world!\n"; + + return 0; +} \ No newline at end of file