Commit 6fe0e0c8 by Praetorius, Simon

workshop and new tutorial added

parent accfb8e5
 \documentclass[dresden]{lotdiss} \include{declarations} \include{papers} % \makeindex % ====================================================== \title{AMDiS\$0.5\baselineskip]\Large Adaptive Multi-Dimensional Simulations} \subtitle{An overview about the Finite-Element Toolbox} \englishabstract{English summary} \germanabstract{Deutsche Kurzzusammenfassung} \author{Simon Praetorius} %\geboren{20. September 1985 \\in Nordhausen, Deutschland} \include{publishing} \promotors{ Gutachter: & Prof. Dr. A. Voigt \\ & Prof. S.~M. Wise \\ %[4pt] Co-promoters: & Prof. Dr. H. Kopka\\ % & Dr. P. W. Daly \\ } % \dedicationfile{dedication} % ====================================================== \begin{document} \FrontMatter %\include{acknowledgements} % \include{abbreviations} \include{preface} % ====================================================== \MainMatter % The regular (numbered) chapters. \include{introduction} \include{ch1} \include{ch2} \include{ch3} \include{ch4} \include{ch5} \include{ch6} \include{conclusion} %\appendix %\include{appendix-a} % ====================================================== \BackMatter \bibliographystyle{siam} %\bibliography{references,references_solver} %\printindex \end{document} \ No newline at end of file doc/book/ch1.tex 0 → 100644  \chapter{\label{ch1}AMDiS tutorial} \begin{abstract} Abstract \end{abstract} \section{Scalar linear second order PDEs} \[ \left[\partial_t u\right] + cu + \underline{b}\cdot\nabla u - \nabla\cdot(\mathbb{A}\nabla u) = f,\quad\text{ in }\Omega\times(0,T]$ with $u=u(x)$ the unknown function and coefficients $c, \underline{b}, \mathbb{A}$ that can depend on space, time, and other quantities $v$ living in $\Omega$: $\begin{split} c &= c(t,x,v,\nabla v),\\ \underline{b} &= \underline{b}(t,x,v,\nabla v)\in\mathbb{R}^d,\\ \mathbb{A} &= \mathbb{A}(t,x,v,\nabla v)\in\mathbb{R}^{d\times d},\\ f &= f(t,x,v,\nabla v), \end{split}$ equipped with (initial- and) boundary conditions on $\partial\Omega$. \begin{minted}{c++} #include "AMDiS.h" using namespace AMDiS; int main(int argc, char** argv) { AMDiS::init(argc, argv); // create and init the scalar problem ProblemStat prob("poisson"); prob.initialize(INIT_ALL); // define operators Operator opLaplace(prob.getFeSpace(), prob.getFeSpace()); addSOT(opLaplace, 1.0); // Operator opF(prob.getFeSpace()); addZOT(opF, 1.0); // // add operators to problem prob.addMatrixOperator(opLaplace, 0, 0); prob.addVectorOperator(opF, 0); // add boundary conditions BoundaryType nr = 1; prob.addDirichletBC(nr, 0, 0, new Constant(0.0)); AdaptInfo adaptInfo("adapt"); // assemble and solve linear system prob.assemble(&adaptInfo); prob.solve(&adaptInfo); prob.writeFiles(&adaptInfo, true); AMDiS::finalize(); } \end{minted} \section{Handling data on unstructured grids} \section{Adaptivity and systems of equations} \section{Time-dependent and nonlinear problems} \ No newline at end of file
doc/book/ch2.tex 0 → 100644
 \chapter{\label{ch2}Parallelization} \begin{abstract} Abstract \end{abstract} \ No newline at end of file
doc/book/ch3.tex 0 → 100644
 \chapter{\label{ch3}Multi-Grid and Multi-Mesh} \begin{abstract} Abstract \end{abstract} \ No newline at end of file
doc/book/ch4.tex 0 → 100644
 \chapter{\label{ch4}Working with Meshes} \begin{abstract} Abstract \end{abstract} \ No newline at end of file
doc/book/ch5.tex 0 → 100644
 \chapter{\label{ch5}Expression templates} \begin{abstract} Abstract \end{abstract} \section{Motivation} Beim Umsetzen einer Differentialgleichung ist der Standardweg in AMDiS diese zu zerlegen in einzelne Terme, zu klassifizieren als 0.-Orderung, 1.-Ordnung oder 2.-Ordnungs Term und dann alle in Operatoren zu packen, die wiederum dem zu lösenden Problem zugewiesen werden. Beispielsweise findet man folgenden Ausdruck im Navier-Stokes Beispiel: \begin{minted}{c++} DOFVector* U = prob->getSolution(j); Operator opUGradU2(getFeSpace(i), getFeSpace(i)); opUGradU2.addTerm(new Vec2AndPartialDerivative_FOT(densityPhase, U, j), GRD_PHI); prob->addMatrixOperator(opUGradU2, i, i); \end{minted} Was genau dieser Operator nun implementiert lässt sich am Namen der Terme nur noch erahnen. Als Ausweg daraus und aus der Notwendigkeit für immer neue Termkonstallationen habe ich folgende Variante implementiert, die die Sytax zumindest einer Zeile etwas aufräumt: \begin{minted}{c++} DOFVector* U = prob->getSolution(j); Operator opUGradU2(getFeSpace(i), getFeSpace(i)); addFOT(opUGradU2, valueOf(densityPhase) * valueOf(U), j, GRD_PHI); prob->addMatrixOperator(opUGradU2, i, i); \end{minted} Dies ist zwar nur unwesentlich kürzer, gibt dafür direkte Auskunft darüber, wie sich der Koeffizient des Terms zusammensetzt, nämlich als Produkt von 2 DOFVectoren (densityPhase und \cpp{solution[j]}). Die Syntax lässt sich aber nicht nur anwenden beim Füllen von Operatoren, sondern auch für die Integration über Ausdrücke von DOFVectoren und bei der Transformation von DOFVectoren. Dazu aber später mehr. Nachfolgend ist die neue Syntax aufgelistet. \section{Syntaxbeschreibung} \subsection{Header} Um die neue Sytax nutzen zu können muss folgende Header-datei eingebunden werden: \cppline{#include "Expressions.h"} \subsection{Elementare Ausdrücke} In der Folgenden Tabelle ist DV ein Pointer/Reference auf \cpp{DOFVector} mit beliebigem \cpp{T}: % \begin{table}[H] \rowcolors{1}{}{lightblue} \begin{tabular}{p{.3\linewidth}|p{.65\linewidth}} \hline \textbf{Expression} & \textbf{Semantics} \\ \hline\hline \cpp{valueOf(DV)} & Auswertung des DOFVectors an Quadraturpunkten \\ \cpp{gradientOf(DV)} & Auswertung des Gradienten eines DOFVectors an Quadraturpunkten \\ \cpp{derivativeOf(DV, i)} & Auswertung der partiellen Ableitung des DOFVectors nach der i-ten Koordinate an Quadraturpunkten \\ \cpp{hessianOf(DV)} & Auswertung der Hesse-Matrix des DOFVectors: $\nabla^2 DV = H_{ij} = \partial_i(\partial_j(DV))$ (kann nur in \cpp{transformDOF}, bzw. \cpp{<<} verwendet werden) \\ \cpp{laplacianOf(DV)} & Auswertung des Laplace des DOFVectors: $\Delta DV = \sum_i\partial_i(\partial_i(DV)))$ (kann nur in \cpp{transformDOF}, bzw. \cpp{<<} verwendet werden) \\ \cpp{componentOf(DV, i)} & Auswertung der i-ten Komponente eines DOFVectors an Quadraturpunkten. Dies macht nur Sinn für \cpp{DOFVector >} Objekten. \\ \cpp{X(), X()} & Der Koordinatenvektor, bzw. die i-te Komponente des Koordinatenvektors an den QP. \\ \cpp{N(b), N(b,i)} & Der äußere Normalenvektor zurgörig zur Fläche mit dem Index b, bzw. deren i-te Komponente. \\ \cpp{M(), M(i)} & Der Elementnormalenvektor (bei Oberflächengittern), bzw. deren i-te Komponente \\ \cpp{constant(c), c} & Konstanten (numeric values) sind z.B. double, float, int. Entweder eingeschlossen in \cpp{constant()}, oder direkt \\ \cpp{constant()} & (Compiletime) Konstanten sind Integer, die beim Differenzierten von Ausdrücken verwendet werden können (s.u.). \\ \cpp{var(c)} & Eine Referenz oder ein Pointer auf einen Wert, der sich dynamisch verändern kann.\\ \hline \end{tabular} \end{table} Der Ausdruck \texttt{valueOf(DV)} kann außerdem auf Vektoren und Matrizen von DOFVectoren angewendet werden: % \begin{table}[H] \rowcolors{1}{}{lightblue} \begin{tabular}{p{.3\linewidth}|p{.65\linewidth}} \hline \textbf{Expression} & \textbf{Semantics} \\ \hline\hline \cpp{valueOf(DV)} & Auswertung des DOFVectors an Quadraturpunkten \\ \cpp{valueOf(vector>)} & Ergibt an den Quadraturpunkten einen \cpp{vector} mit den Werten der einzelnen DOFVektoren and dieser Stelle. \\ \cpp{valueOf(matrix>)} & Ergibt an den Quadraturpunkten eine \cpp{matrix} mit den Werten der einzelnen DOFVektoren and dieser Stelle. \\ \cpp{valueOf(X)} & Eine Expression, der ein Name zugeordnet ist. \cpp{Name} ist dabei ein C++-Typ. Ist kein Name explizit angegeben, wird \cpp{_unknown} verwendet. (siehe auch Differenzieren von Ausdrücken)\\ \hline \end{tabular} \end{table} \subsection{Operationen} In der folgenden Tabelle sind Operationen aufgelistet, über die die elementaren Term (s.o.) kombiniert werden können. Dabei steht \cpp{T}, bzw. \cpp{Tn} für einen Term und \cpp{F} für ein Funktor-Objekt (siehe unten). % \begin{table}[H] \rowcolors{1}{}{lightblue} \begin{tabular}{p{.3\linewidth}|p{.65\linewidth}} \hline \textbf{Expression} & \textbf{Semantics} \\ \hline\hline \cpp{+, -, *, /} & Elementare arithmetische Ausdrücke \\ \cpp{pow

(T)} & Die p-te Potenz eines Term, wenn Term skalar ist. \\ \cpp{sqrt(T)} & Die Wurzel des Ausdrucks \cpp{T} \\ \cpp{exp(T)} & Die Exponentialfunktion angewendet auf das Ergebnis der Auswertung des Terms \cpp{T}. \\ \cpp{log(T)} & Der natürlich Logarithmus der Auswertung des Terms \cpp{T}. \\ \cpp{cos(T), sin(T), tan(T)} & Der Cosinus, Sinus, bzw. Tangens der Auswertung des Terms \cpp{T}. \\ \cpp{acos(T), asin(T), atan(T)} & Der Arkus-Cosinus, Arkus-Sinus, bzw. Arkus Tangens der Auswertung des Terms \cpp{T}. \\ \cpp{atan2(T1, T2)} & Der Arkus-Tangens 2 = \cpp{atan(T1 / T2)}. \\ \cpp{cosh(T), sinh(T), tanh(T)} & Der Cosinus-Hyperbolicus, Sinus-Hyperbolicus, bzw. Tangens-Hyperbolicus der Auswertung des Terms \cpp{T}. \\ \cpp{acosh(T), asinh(T), atanh(T)} & Der Arkus Cosinus-Hyperbolicus, Arkus Sinus-Hyperbolicus, Arkus Tangens-Hyperbolicus der Auswertung des Terms \cpp{T}. \\ \cpp{max(T1,T2), min(T1,T2)} & Das Maximum/Minimum der Auswertungen von \cpp{T1}, bzw. \cpp{T2}. \\ \cpp{absolute(T), signum(T)} & Der Absolutbetrag von T, bzw. das Vorzeichen (\cpp{if(T < 0) signum(T) = -1 else if(T > 0) signum(T) = 1 else signum(T) = 0}) \\ \cpp{ceil(T), floor(T)} & die kleines ganze Zahl größer, bzw. die größte ganze Zahl kleiner als Wert von \cpp{T} \\ \cpp{func(F, T1, T2,...)} & Die Anwendung eines Funktors \cpp{F} auf die Auswertung der Terme \cpp{T1,T2,...} \\ \hline \end{tabular} \end{table} Ein Funktor ist dabei eine Klasse mit folgender Struktur: \begin{minted}{c++} struct Functor : public FunctorBase { typedef (...) value_type; int getDegree(int d1, int d2, ...) const { return (...); } value_type operator()(const T1::value_type&, const T2::value_type&, ...) const { return (...); } }; \end{minted} Die Argumente, die an die Funktion \texttt{getDegree} übergeben werden sind die Polynomgrade der Terme: \cppline{getDegree(T1.getDegree(), T2.getDegree(), ...)} \subsection{Vektor-/Matrix-Ausdrücke} Für vektor- bzw. matrixwertige Term, wie z.B. \cpp{gradientOf(DV)} gibt es eine Reihe von Ausdrücken / Operationen, die im Folgenden aufgelistet sind. Dabei bezeichnet \cpp{V} eine vektorwerte Expression und \cpp{M} eine matrixwertige Expressions. % \begin{table}[H] \rowcolors{1}{}{lightblue} \begin{tabular}{p{.3\linewidth}|p{.65\linewidth}} \hline \textbf{Expression} & \textbf{Semantics} \\ \hline\hline \cpp{+, -} & Elementare arithmetische Ausdrücke (Elementweise) \\ \cpp{unary_dot(V)} & Skalarprodukt eines vektorwertigen Terms mit sich selbst: \cpp{result = V^H * V}. \\ \cpp{dot(V1, V2)} & Skalarprodukt zweier vektorwertigen Terme: \cpp{result = V1^H * V2}. \\ \cpp{one_norm(V)} & Die 1-Norm eines Vektors \cpp{result = sum_i(abs(V_i))}. \\ \cpp{one_norm(M)} & Die 1-Norm einer Matrix \cpp{result = max_j(sum_i(abs(M_ij)))}. \\ \cpp{two_norm(V)} & Die 2-Norm eines Vektors \cpp{result = sqrt(V^H * V)}. \\ \cpp{p_norm

(V)} & Die p-Norm eines Vektors \cpp{result = [sum_i(abs(v_i)^p)]^(1/p)}. \\ \cpp{cross(V1, V2)} & Kreuzprodukt zweier vektorwertigen Terme: \cpp{result = V1 x V2}. \\ \cpp{diagonal(V)} & Diagonalmatrix aus einträgen eines vektorwertigen Terms: \cpp{matrix = diagonal(V)}. \\ \cpp{outer(V1, V2)} & Äußeres Produkt (dyadisches Produkt / Tensorprodukt) zweier vektorwertigen Terme: \cpp{matrix = V1 * V2^T}. \\ \cpp{trans(M)} & Das Transponierte eines matrixwertigen Terms: \cpp{result = M^T}. \\ \cpp{at(V, i)} & Zugriff auf eine Komponente eines Vektors: \cpp{result = V_i}. \\ \cpp{at(M, i, j)} & Zugriff auf eine Komponente einer Matrix: \cpp{result = M_ij}. \\ \hline \end{tabular} \end{table} \subsection{Differenzieren von Ausdrücken} Terme / Expressions sind Ausdrücke mit Variablen und Operationssymbolen, die Formal nach einzelnen Variablen differenziert werden können. Variablen sind hierbei Ausdrücke \cpp{valueOf(DV)} denen ein Variablenname zugeordnet ist. Jedem \cpp{valueOf} Ausdruck ist standardmäßig der Name \cpp{_unknown} zugeordnet. Für skalare Terme (also Terme ohne Vektorausdrücke und vektorwertige Variablen) lässt sich die Differentiation automatisch durchführen. Um dies nutzen zu können, muss die Datei \cpp{diff_expr.hpp} eingebunden werden: \cppline{#include "expressions/diff_expr.hpp"} \begin{table}[H] \rowcolors{1}{}{lightblue} \begin{tabular}{p{.3\linewidth}|p{.65\linewidth}} \hline \textbf{Expression} & \textbf{Semantics} \\ \hline\hline \cpp{diff(T)} & Differentiation nach Variable "Name": \cpp{d_Name (Term)} \\ \cpp{diff(T, U)} & Richtungsableitung nach Variable "Name", in Richtung \cpp{U}: \cpp{d_Name (Term)[U]} \\ \cpp{simplify(T)} & Vereinfachen eines Terms. Insbesondere werden Multiplikationen mit 0 und mit 1 entfernt und verschachtelte Operationen zwischen Konstanten zusammengefasst.\\ \hline \end{tabular} \end{table} \subsection{Anwendung} Diese verallgemeinerten Terme können nun beim Assemblieren, integrieren und transformieren von DOFVectoren verwendet werden: In der Folgenden Tabelle wird \cpp{Operator} für einen Pointer/Reference auf ein Operator Objekt verwendet und Term steht für einen Ausdruck, der sich aus den obigen Zutaten zusammensetzt. \cpp{Flag} ist entweder \cpp{GRD_PHI}, oder \cpp{GRD_PSI}, je nachdem, ob die Ableitung bei einem 1.-Ordnungs Term auf der Ansatzfunktion \cpp{u}, oder Testfunktion \cpp{v} steht. % \begin{table}[H] \rowcolors{1}{}{lightblue} \begin{tabular}{p{.3\linewidth}|p{.65\linewidth}} \hline \textbf{Expression} & \textbf{Semantics} \\ \hline\hline \cpp{addZOT(Operator, Term)} & Ein 0.-Ordnungs Term $(\text{Term} \cdot u,\; v)$ \\ \cpp{addFOT(Operator, Term, Flag)} & Ein 1.-Ordnungs Term $(\text{Term} \cdot \nabla{u},\; v)$, bzw. $(\text{Term} \cdot u,\; \nabla{v})$, wenn der \cpp{value_type} von \cpp{Term} einem \cpp{WorldVector} entspricht, bzw. $(\mathbf{1}\text{Term} \cdot \nabla{u},\; v)$, bzw. $(\mathbf{1}\text{Term} \cdot u,\; \nabla{v})$, mit $\mathbf{1}=(1,1,1)^\top$, wenn der \cpp{value_type} von \cpp{Term} einem \cpp{double} entspricht. \\ \cpp{addFOT(Operator, Term, i, Flag)} & Ein 1.-Ordnungs Term $(\text{Term} \cdot \partial_i{u},\; v)$, bzw. $(\text{Term} \cdot u,\; \partial_i{v})$ \\ \cpp{addSOT(Operator, Term)} & Ein 2.-Ordnungs Term $(\text{Term} \cdot \nabla{u},\; \nabla{v})$, wobei \cpp{value_type} von \cpp{Term} einem \cpp{WorldMatrix}, bzw. \cpp{double} entsprechen muss. \\ \cpp{addSOT(Operator, Term, i, j)} & Ein 2.-Ordnungs Term $(\text{Term} \cdot \partial_j{u},\; \partial_i{v})$, wobei \cpp{value_type} von \cpp{Term} einem \cpp{double} entsprechen muss.\\ \hline \end{tabular} \end{table} Für die Berechnung eines Integrals über einen Ausdruck von DOFVectoren kann folgender Befehl verwendet werden: \cpp{integrate(Term);} und für die Transformation eines DOFVectors folgendes: \cpp{DV << Term;} bzw. \cpp{transformDOF(Term, DV);} Wobei jeweils DV kein DOFVector-Pointer sein kann. \section{Beispiele} \subsection{Bilinearform} Folgende Bilinearform soll assembliert werden: $a(c,\vartheta) := \big\langle\frac{1}{\epsilon}(\phi^2 - 1) c,\; \vartheta\big\rangle + \big\langle\max(10^{-5},\;(\phi+1))\nabla c,\; \nabla\vartheta\big\rangle$ Mit Hilfe der oben definierten Ausdrücke lässt sich dies nun wie folgt schreiben: \begin{minted}{c++} Operator op(feSpace, feSpace); addZOT(op, (1.0/epsilon) * (pow<2>(valueOf(phi)) - 1.0) ); addSOT(op, max(1.e-5, valueOf(phi) + 1.0) ) prob->addMatrixOperator(op, 0, 0); \end{minted} \subsection{Integration und Interpolation} Danach sollen folgende Integrale und ein Double-well berechnet werden: $mean = \int \frac{1}{2}(c + 1)\;dx,\quad length = \int \|\nabla c\|\; dx,\quad DW=\frac{1}{2}(c^2\,(1-c)^2 + \frac{1}{\epsilon}\|\nabla c\|^2)$ Dazu können wieder die Expressions verwendet werden: \begin{minted}{c++} double mean = integrate( 0.5 * (valueOf(c) + 1.0) ); double length = integrate( two_norm(gradientOf(c)) ); DOFVector DW(c.getFeSpace(), "Double-Well"); DW << 0.5*( pow<2>(valueOf(c)) * pow<2>(1.0 - valueOf(c)) + unary_dot(gradientOf(c)) ); \end{minted} \subsection{Funktoren} Komplexe Ausdrücken können auch zu einem Funktor zusammen gefasst werden. Dazu wird erst das Funktor-Objekt erstellt und dieses dann über den Term \texttt{func} in einem Expression eingebunden: $V = \sum_{i=0}^{d-1} \alpha_i U_i$ Mit $\alpha_i$ sind Koeffizienten und $U=\{U_i\}$ ein DOFVector >. Dieser Term kann umgesetzt werden, mittels \begin{minted}{c++} struct F : FunctorBase { typedef double value_type; F(WorldVector& alpha_) : alpha(alpha_) {} int getDegree(int d0) const { return d0; } value_type operator()(const WorldVector& U) const { double result = 0.0; for (int i = 0; i < U.getSize(); i++) result += alpha[i]*U[i]; return result; } private: WorldVector alpha; }; int main() { // ... // DOFVector > U(feSpace, "U"); DOFVector V(feSpace, "V"); WorldVector alpha = (...); U << (...) V << func(F(alpha), valueOf(U)); } \end{minted} Bisher wird in AMDiS anstelle von Objekten zur Basis \texttt{FunctorBase} die AbstractFunction verwendet. Um diese nutzen zu können, gibt es Wrapper, die diese implizit zu einem Funktor Konvertieren. Für den Spezialfall der Auswertung an Koordinaten gibt es zusätzlich einen Shortcut: \begin{minted}{c++} struct F : AbstractFunction > { F() : AbstractFunction >(1) {} double operator()(const WorldVector& x) const { return cos(x[0]); } }; int main() { // ... // DOFVector V(feSpace, "V"); V << function_(wrap(new F), X()); // bzw. V << eval(new F) // und auch verschobene Auswertungen sind möglich: WorldVector shift = (...) V << function_(wrap(new F), X() + shift); } \end{minted} \subsection{Differentiation} Insbesondere in Newtonverfahren für nichtlineare PDEs und Rosenbrock-Verfahren für die Zeitintegration (nichtlinearer Gleichungen), wird die Jacobimatrix eines Differentialoperators benötigt. Den kann man häufig auf die Ableitung der Koeffizientefunktionen zurückführen. Siehe aus Motivation auch das Nonlinear Demo [[AMDiS:Demo:Nonlinear example]]. Grundlage der Implementierung könnte sein, dass die Terme automatisch differenziert werden Als Beispiel soll hier ein einfaches Polynom als Term dienen: $expr(u) = u^4$ Um nach $u$ zu differenzieren, müssen wir dem zugrundeliegenden DOFVector einen Namen geben: \begin{minted}{c++} struct u_ {}; struct v_ {}; ... DOFVector U(...); DOFVector V(...); auto expr = pow<4>(valueOf(U)); auto diff_expr = diff(expr); // = 4 * u^3 auto diff_expr2 = diff(expr, valueOf(V)); // 4 * u^3*v \end{minted} \ No newline at end of file

doc/book/ch6.tex 0 → 100644
 \chapter{\label{ch6}Applications} \begin{abstract} Abstract \end{abstract}
 \chapter{\label{ch:conclusion}Conclusion and outlook}
 \usepackage{amsmath} \usepackage[bbgreekl]{mathbbol} \usepackage{amsfonts} \usepackage{amssymb} \usepackage{amsthm} \usepackage{tabularx} \usepackage{multirow} \usepackage{graphicx} \usepackage{mathtools} \usepackage[table]{xcolor} \usepackage{wrapfig} \usepackage{relsize} \usepackage{hyperref} \usepackage{enumerate} \usepackage{algorithm} \usepackage{listings} \usepackage{minted} \usepackage[noend]{algpseudocode} \DeclareSymbolFontAlphabet{\mathbb}{AMSb} \DeclareSymbolFontAlphabet{\mathbbl}{bbold} \newcommand{\Du}{\mathbf{D}(\mathbf{u})} \newcommand{\eqdef}{\ensuremath{\overset{\text{def}}{=}}} \newcommand{\rrho}{\Bar{\Bar{\varrho}}} \newcommand{\kbt}{{k_\textup{B}{T}}} %\newcommand{\Eins}{\mathbf{1}}% %\newcommand{\Null}{\mathbf{0}}% \newcommand{\Diag}{\operatorname{diag}}% \newcommand{\esp}{\,\hat{=}\,}% \newcommand{\ii}{\mathrm{i}}% \newcommand{\Tr}{\operatorname{Tr}}% \newcommand{\Div}{\operatorname{div}}% \newcommand{\abs}[1]{\lvert#1\rvert}% \newcommand{\norm}[1]{\lVert#1\rVert}% \newcommand{\ie}{i.\,e.}% \newcommand{\eg}{e.\,g.}% \newcommand{\etal}{et\,al.}% \newcommand{\ZT}[1]{\textquotedblleft#1\textquotedblright}% \newcommand{\modulo}{\texttt{\%}}% \newcommand{\sign}{\operatorname{sign}} \newcommand{\R}{\mathbb{R}}% \newcommand{\C}{\mathbb{C}}% % dimensionless numbers \newcommand{\RE}{\textup{Re}}% \newcommand{\PE}{\textup{Pe}}% \newcommand{\CA}{\textup{Ca}}% \newcommand{\SC}{\textup{Sc}}% \newcommand{\xb}{\mathbf{x}}% \newcommand{\rb}{\mathbf{r}}% \newcommand{\ub}{\mathbf{u}}% \newcommand{\vb}{\mathbf{v}}% \newcommand{\pb}{\mathbf{p}}% \newcommand{\qb}{\mathbf{q}}% \newcommand{\nb}{\mathbf{n}}% \newcommand{\director}{\hat{\nb}} \newcommand{\Laplace}{\Delta}% \newcommand{\iDelta}{\mathlarger{\blacktriangle}}% \newcommand{\vecDelta}{\vec{\mathlarger{\Delta}}}% \newcommand{\vecLaplace}{\boldsymbol{\Delta}}% \newcommand{\Nabla}{\vec{\nabla}}% \newcommand{\inabla}{\mathlarger{\blacktriangledown}}% \newcommand{\iNabla}{\vec{\mathlarger{\blacktriangledown}}}% \newcommand{\Boundary}{\Sigma} \newcommand{\reflect}[1]{\text{\reflectbox{$#1$}}} \newcommand{\dif}{\textup{d}}% d \newcommand{\tdif}[2]{\frac{\dif#1}{\dif#2}}% du / dx \newcommand{\pdif}[2]{\frac{\partial#1}{\partial#2}}% du / dx \newcommand{\ppdif}[2]{\frac{\partial^{2}{#1}}{\partial{#2}^{2}}}% d^2 u / dx^2 \newcommand{\ds}{\,\dif{s}}% dr \newcommand{\dr}{\,\dif\rb}% dr \newcommand{\dx}{\,\dif\xb}% dx \newcommand{\dxhat}{\,\dif\hat{\xb}}% dx \newcommand{\dt}{\partial_t}% d_t \newcommand{\Poly}[1]{\mathbb{P}_{#1}}% P_p \newcommand{\Triang}{\mathcal{T}_{h}}% T_h \newcommand{\fdif}{\operatorname{\delta}\!}% d \newcommand{\Fdif}[2]{\frac{\fdif{#1}}{\fdif{#2}}}% dF / du \newcommand{\FFdif}[3]{\frac{\fdif^2{#1}}{\fdif{#2}\fdif{#3}}}% d^2F / dudv \newcommand{\drho}{\fdif\varrho} \newcommand{\phase}{{\phi}} \newcommand{\dotprod}[2]{\ensuremath{\langle{#1},\,{#2}\rangle}} \newcommand{\twoNorm}[1]{\ensuremath{\|{#1}\|}} % notation of energies \newcommand{\FF}{{F}} \newcommand{\F}[1]{\FF_\textup{#1}} \newcommand{\dotF}[1]{\dot{\FF}_\textup{#1}} \newcommand{\hatF}[1]{\hat{\FF}_\textup{#1}} \newcommand{\barF}[1]{\bar{\FF}_\textup{#1}} \newcommand{\Fourier}{\mathcal{F}} \newcommand{\ff}{\mathbbl{f}} %\theoremstyle{plain} \newtheorem{lem}{Lemma} \newtheorem{thm}{Theorem} \newtheorem{prop}[thm]{Proposition}