1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (c) 2003-2018 by The University of Queensland
% http://www.uq.edu.au
%
% Primary Business: Queensland, Australia
% Licensed under the Apache License, version 2.0
% http://www.apache.org/licenses/LICENSE-2.0
%
% Development until 2012 by Earth Systems Science Computational Center (ESSCC)
% Development 2012-2013 by School of Earth Sciences
% Development from 2014 by Centre for Geoscience Computing (GeoComp)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%!TEX root = user.tex
\chapter{The escript symbolic toolbox}
\label{CHAP:Symbolic}
\section{Introduction}
\escript builds on the existing Sympy~\cite{Sympy} symbolic maths library to
provide a \SYMBOL class with support for \escript Data objects. \SYMBOL objects
act as placeholders for a single mathematical symbol, such as \texttt{x}, or
for arbitrarily complex mathematical expressions such as
\texttt{c*x**4 + alpha*exp(x) - 2*sin(beta * x)}, where \texttt{alpha},
\texttt{beta}, \texttt{c}, and \texttt{x} are also symbols (the symbolic
``atoms" of the expression).
With the help of the \EVALUATOR class, these symbols and expressions can
be evaluated by substituting numeric values and/or \escript \Data objects
for the atoms. Escript's \SYMBOL class has a shape (and thus a rank) as well
as a dimensionality.
Symbols are useful to perform mathematical simplifications, compute
derivatives, take gradients and in the case of \escript describe PDEs.
As an example of how the symbolic toolbox can be used, consider the following
code extract.
\begin{python}
import esys.escript as es
u = es.Symbol('u')
p = 2*u**2 + 3*u + 1
p2 = es.sin(u)
p3 = p.diff(u) # p3 = derivative of p with respect to u
evalu = es.Evaluator()
evalu.addExpression(p)
evalu.addExpression(p2)
evalu.addExpression(p3)
evalu.subs(u=2*es.symconstants.pi)
evaluated=evalu.evaluate()
print("p3 =", p3) # The symbols can be printed, this line will print p3.
print(evaluated)
\end{python}
Running this code outputs:\\\texttt{p3 = 4*u + 3\\(1 + 6*pi + 8*pi**2, 0, 3 + 8*pi)}.\\
To get the numeric value of the expression we replace\\
\texttt{evalu.evaluate()}
\\with\\
\texttt{evalu.evaluate(evalf=True)}.\\
This results in
\texttt{(98.806, 0, 28.132)}.
The use of these Symbols becomes more interesting in the context of escript
when they are integrated with escript \Data objects.
\pagebreak
\section{NonlinearPDE}
The \NLPDE class in escript makes use of the escript \SYMBOL class and allows
for the solution of PDEs of the form:
\begin{equation}
-X_{ij,j} + Y_i = 0
\label{symbolic eq1}
\end{equation}
where $X$ and $Y$ are both functions of $u_{k,j}$ and $u_k$, and $u$ is the
unknown function implemented as a \SYMBOL. $\nabla\cdotp x$ denotes divergence
of $x$.
The \NLPDE class uses the \SYMBOL class to solve the nonlinear PDE given in
\eqn{symbolic eq1}.
The class incorporates Newton's method to find the zeroes of the left hand side
of \eqn{symbolic eq1} and as a consequence finding the $X$ and $Y$ which
satisfy \eqn{symbolic eq1}.
Consecutive updates are calculated until the equation is satisfied to the
desired level of accuracy. The solution to each update step involves solving a
linear PDE. The \NLPDE class uses $X$ and $Y$ to produce the coefficients of
the linear PDE for the update step. The linear PDE class given in
\Sec{SEC LinearPDE} is used to solve the linear PDEs from the update step.
The coefficients of the linear PDE to be solved are calculated as follows:
\begin{equation*}
A_{ijkl} = \frac{\partial \text{X}_{ij}}{\partial u_{k,l}}, B_{ijkl} = \frac{\partial \text{X}_{ij}}{\partial u_{k}}, C_{ijkl} = \frac{\partial \text{Y}_{ij}}{\partial u_{k,l}}, D_{ijkl} = \frac{\partial \text{Y}}{\partial u_{k}}
\end{equation*}
\section{2D Plane Strain Problem}
The \NLPDE class can be used to solve a 2D plane strain problem. In continuous
media, stress is given by Lam\'e's \eqn{symbolic eq2}.
\begin{equation}
-\sigma_{ij,j}=f
\label{symbolic eq2}
\end{equation}
Hook's Law provides a relation between $\sigma$ and $\epsilon$ in the following form
\begin{equation}
\left[ \begin{array}{c}
\sigma_{00} \\
\sigma_{11} \\
\sigma_{01} \\
\end{array} \right] =
\left[ \begin{array}{ccc}
c_{00} & c_{01} & c_{05}\\
c_{01} & c_{11} & c_{15}\\
c_{05} & c_{15} & c_{55}\\
\end{array}\right]
\left[ \begin{array}{c}
\epsilon_{00} \\
\epsilon_{11} \\
2\epsilon_{10} \\
\end{array} \right]
\label{symbolic eq3}
\end{equation}
Where $\epsilon = symmetric(grad(u)) \text{ or } %
\epsilon_{ij}=\frac{1}{2}\left(\frac{\partial u_i}{\partial x_j} + %
{\frac{\partial u_j}{\partial x_i}}\right)$, $u$ is the unknown function and
$c_{ij}$ is the stiffness matrix. To fit this to the nonlinear PDE class'
standard form, X is set to $C \times symmetric(grad(u))$.
The following \PYTHON extract shows how an example 2D plane strain problem can
be set up.
\begin{python}
from esys.escript import *
from esys.finley import Rectangle
#set up domain and symbols
mydomain = Rectangle(l0=1.,l1=1.,n0=10, n1=10)
u = Symbol('u',(2,), dim=2)
q = Symbol('q', (2,2))
sigma = Symbol('sigma',(2,2))
theta = Symbol('theta')
# q is a rotation matrix represented by a Symbol. Values can be substituted for
# theta.
q[0,0]=cos(theta)
q[0,1]=-sin(theta)
q[1,0]=sin(theta)
q[1,1]=cos(theta)
# Theta gets substituted by pi/4 and masked to lie between .3 and .7 in the
# vertical direction. Using this masking means that when q is used it will apply
# only to the specified area of the domain.
x = Function(mydomain).getX()
q=q.subs(theta,(symconstants.pi/4)*whereNonNegative(x[1]-.30)*
whereNegative(x[1]-.70))
# epsilon is defined in terms of u and has the rotation applied.
epsilon0 = symmetric(grad(u))
epsilon = matrixmult(matrixmult(q,epsilon0),q.transpose(1))
# For the purposes of demonstration, an arbitrary c with isotropic constraints
# is chosen here. In order to act as an isotropic material c is chosen such that
# c00 = c11 = c01+c1+2*c55
c00 = 10
c01 = 8; c11 = 10
c05 = 0; c15 = 0; c55 = 1
# sigma is defined in terms of epsilon
sigma[0,0] = c00*epsilon[0,0]+c01*epsilon[1,1]+c05*2*epsilon[1,0]
sigma[1,1] = c01*epsilon[0,0]+c11*epsilon[1,1]+c15*2*epsilon[1,0]
sigma[0,1] = c05*epsilon[0,0]+c15*epsilon[1,1]+c55*2*epsilon[1,0]
sigma[1,0] = sigma[0,1]
sigma0=matrixmult(matrixmult(q.transpose(1),sigma),q)
# set up boundary conditions
x=mydomain.getX()
gammaD=whereZero(x[1])*[1,1]
yconstraint = FunctionOnBoundary(mydomain).getX()[1]
# The nonlinear PDE is set up, the values are substituted in and the solution is
# calculated, y represents an external shearing force acting on the domain.
# In this case a force of magnitude 50 is acting in the x[0] direction.
p = NonlinearPDE(mydomain, u, debug=NonlinearPDE.DEBUG0)
p.setValue(X=sigma0,q=gammaD,y=[-50,0]*whereZero(yconstraint-1),r=[1,1])
v = p.getSolution(u=[0,0])
\end{python}
%\pagebreak
The way in which the rotation matrix q is set up demonstrates the seamless integration of escript symbols and \Data objects. A \SYMBOL is used to set up the matrix, the values for theta are then later substituted in. The example also demonstrates how the symbolic toolbox can be used as an aid to easily move from a mathematical equation to an escript data object which can be used to do numerical calculations.
Running the script calculates the unknown function u and assigns it to v. We can use v to calculate the stress and strain.
\begin{table}[!h]
\centering
\begin{tabular}{|c|c|c|}
\hline
& Anisotropic & Isotropic\\%\multicolumn{2}{|c|}{Isotopic} \\
\hline
No rotation & \includegraphics[scale=0.2]{0RotAniso} & \includegraphics[scale=0.2]{0RotIso}\\
\hline
60\textdegree\ rotation & \includegraphics[scale=0.2]{MidRotAniso} & \includegraphics[scale=0.2]{MidRotIso}\\
\hline
difference & \includegraphics[scale=0.2]{diffaniso} & \raisebox{2cm}{no difference}\\
\hline
\end{tabular}
\caption{Displacement vectors calculated using \NLPDE}
\label{isovsaniso}
\end{table}
Table \ref{isovsaniso}, shows the result of running the above script under varying values of c and theta. Both isotropic and anisotropic cases are considered. For the anisotropic case, $c_{ij}$ is chosen such that
$c00 = c11 = c01+c1+2*c55$ does not hold.
Two values of theta are also considered; one with a masked 60\textdegree
rotation in the middle and one with no rotation.
The last row of the table shows the difference between rotation in the middle
and no rotation. In the isotropic case it can be seen that there is no
difference in the output when the rotation is applied.
There is however, an obvious difference when the anisotropic case is considered.
\section{Classes}
A number of classes are associated with escript symbols. A detailed listing of the definitions and usage is provided below.
\subsection{Symbol class}
\begin{classdesc}{Symbol}{symbol \optional{, shape} \optional{, Dim}}
Defines a \SYMBOL object. The first argument \member{symbol} is a string given
to represent the \SYMBOL. The string typically matches the name of the object,
for instance \texttt{u=Symbol('u')}. Next optional \member{shape} argument
defines whether the \SYMBOL is a scalar, vector, matrix, or tensor and the
length or size of it. \member{dim} is used to define the dimensionality of the
object contained in the \SYMBOL.
For a \SYMBOL definition \texttt{u = Symbol('u',(10,), dim=2)}, the value of u
will be a vector with 10 components and the domain on which u will be used is
2-dimensional (this is relevant with operations such as \texttt{grad} where the
number of spatial dimensions is important).
\end{classdesc}
\subsubsection{Symbol class methods}
\begin{methoddesc}[Symbol]{atoms}{\optional{types}}
Returns the atoms that form the current \SYMBOL.
By default, only objects that are truly atomic and cannot be divided into
smaller pieces are returned: symbols, numbers, and number symbols like $e$ and
$pi$. It is possible to request atoms of any type via the \member{types}
argument, however.
\end{methoddesc}
\begin{methoddesc}[Symbol]{coeff}{x \optional{, expand=true}}
Returns the coefficient of the term \texttt{x} or $0$ if there is no \texttt{x}.
If \texttt{x} is a scalar \SYMBOL then \texttt{x} is searched in all components
of this \SYMBOL. Otherwise the shapes must match and the coefficients are
checked component by component. For example:
\begin{python}
x=Symbol('x', (2,2))
y=3*x
print y.coeff(x)
print y.coeff(x[1,1])
\end{python}
will print:
\begin{python}
[[3 3]
[3 3]]
[[0 0]
[0 3]]
\end{python}
\end{methoddesc}
\begin{methoddesc}[Symbol]{diff}{symbols}
Takes the derivative of the \SYMBOL object of which the method is called with
respect to the symbols specified in the argument \member{symbols}.
\end{methoddesc}
\begin{methoddesc}[Symbol]{evalf}{}
Applies the \texttt{sympy.evalf} operation on all elements of the \SYMBOL which
are of type or inherit from \texttt{sympy.Basic}.
\end{methoddesc}
\begin{methoddesc}[Symbol]{expand}{}
Applies the \texttt{sympy.expand} operation on all elements in this \SYMBOL.
\end{methoddesc}
%\begin{methoddesc}[Symbol]{getDataSubstitutions}{}
%end{methoddesc}
\begin{methoddesc}[Symbol]{getDim}{}
Returns the \SYMBOL's spatial dimensionality, or -1 if undefined.
\end{methoddesc}
\begin{methoddesc}[Symbol]{getRank}{}
Returns the \SYMBOL's rank which is equal to the length of the shape.
\end{methoddesc}
\begin{methoddesc}[Symbol]{getShape}{}
Returns the shape of this \SYMBOL.
\end{methoddesc}
\begin{methoddesc}[Symbol]{grad}{\optional{where=none}}
Returns the gradient of this \SYMBOL. The \SYMBOL must have a dimensionality
defined in order for \texttt{grad} to work. As with the normal escript
\texttt{grad} function a \FunctionSpace can be specified using the
\member{where} argument. The \FunctionSpace should be wrapped in a \SYMBOL.
To do this, set up a \SYMBOL and then use the \texttt{subs} function to
substitute in the \FunctionSpace.
\end{methoddesc}
\begin{methoddesc}[Symbol]{inverse}{}
Find the inverse of the \SYMBOL to which the function is applied. Inverse is only valid for square rank 2 symbols.
\end{methoddesc}
%\begin{methoddesc}[Symbol]{item}{}
%\end{methoddesc}
%\begin{methoddesc}[Symbol]{lambdarepr}{}
%test
%\end{methoddesc}
\begin{methoddesc}[Symbol]{simplify}{}
Applies the \texttt{sympy.simplify} operation on all elements in this \SYMBOL.
\end{methoddesc}
\begin{methoddesc}[Symbol]{subs}{old, new}
Substitutes or replaces a \SYMBOL specified in \member{old} with whatever is in
\member{new} for this \SYMBOL. Consider:
\begin{python}
import esys.escript as es
u=es.Symbol("u")
expr=2*u
expr.subs(u,2)
\end{python}
This prints 4.
\end{methoddesc}
\begin{methoddesc}[Symbol]{trace}{axis_offset}
Returns the trace of the \SYMBOL object.
\end{methoddesc}
\subsection{Evaluator class}
The \EVALUATOR class is intended to have a group of expressions added to it,
substitutions can be made across all expressions and the expressions can then
all be evaluated.
\subsubsection{Evaluator class methods}
\begin{classdesc}{Evaluator}{\optional{expressions}}
An \EVALUATOR object is initiated via \texttt{Evaluator()} with an optional
argument of expressions to store.
\end{classdesc}
\begin{methoddesc}[Evaluator]{addExpression}{expression}
Adds an expression to this \EVALUATOR.
\end{methoddesc}
\begin{methoddesc}[Evaluator]{evaluate}{\optional{evalf=False}\optional{, args}}
Evaluates all expressions in this \EVALUATOR and returns the result as a tuple.
\member{evalf} can be set to \texttt{True} to call \texttt{evalf} on any sympy
symbols which may be part of the expression.
\member{args} can be provided to make any substitutions before the expression
is evaluated.
\end{methoddesc}
\begin{methoddesc}[Evaluator]{subs}{old,new}
Substitutes or replaces a \SYMBOL specified in \member{old} with whatever is
in \member{new} for all expressions in the \EVALUATOR.
\end{methoddesc}
\subsection{NonlinearPDE class}
\begin{classdesc}{NonlinearPDE}{domain, u}
Defines a general nonlinear, steady, second order PDE for an unknown function
\var{u} on a given domain defined through a \Domain object \var{domain}.
\var{u} is a \SYMBOL object.
The general form is -div(X) + Y = 0
\end{classdesc}
\iffalse
\begin{methoddesc}[NonlinearPDE]{concatenateRow}{}
test
\end{methoddesc}
\begin{methoddesc}[NonlinearPDE]{createCoefficient}{}
test
\end{methoddesc}
\begin{methoddesc}[NonlinearPDE]{getUnknownSymbol}{}
test
\end{methoddesc}
\begin{methoddesc}[NonlinearPDE]{getLinearSolverOptions}{}
test
\end{methoddesc}
\begin{methoddesc}[NonlinearPDE]{getLinearPDE}{}
test
\end{methoddesc}
\begin{methoddesc}[NonlinearPDE]{getNumSolutions}{}
test
\end{methoddesc}
\begin{methoddesc}[NonlinearPDE]{getShapeOfCoefficient}{}
test
\end{methoddesc}
\begin{methoddesc}[NonlinearPDE]{getCoefficient}{}
test
\end{methoddesc}
\begin{methoddesc}[NonlinearPDE]{getSensitivity}{}
test
\end{methoddesc}
\fi
\begin{methoddesc}[NonlinearPDE]{getSolution}{subs}
Returns the solution of the PDE. \member{subs} contatins the substitutions for
all symbols used in the coefficients including the initial value for the
unknown \texttt{u}.
\end{methoddesc}
\begin{methoddesc}[NonlinearPDE]{setOptions}{opts}
\begin{verbatim}
Allows setting options for the nonlinear PDE.
The supported options are:
tolerance
error tolerance for the Newton method
iteration_steps_max
maximum number of Newton iterations
omega_min
minimum relaxation factor
atol
solution norms less than atol are assumed to be atol.
This can be useful if one of your solutions is expected to
be zero.
quadratic_convergence_limit
if the norm of the Newton-Raphson correction is reduced by
less than quadratic_convergence_limit between two iteration
steps, quadratic convergence is assumed.
simplified_newton_limit
if the norm of the defect is reduced by less than
simplified_newton_limit between two iteration steps and
quadratic convergence is detected, the iteration switches to the
simplified Newton-Raphson scheme.
\end{verbatim}
\end{methoddesc}
\begin{methoddesc}[NonlinearPDE]{setValue}{
\optional{X}\optional{, Y}
\optional{, y}
\optional{, q}\optional{, r}}
Assigns new values to coefficients. By default all values are assumed to be
zero\footnote{In fact, it is assumed they are not present by assigning the
value \code{escript.Data()}. This can be used by the solver library to reduce
computational costs.}.
If the new coefficient value is not a \Data object, it is converted into a
\Data object in the appropriate \FunctionSpace.
\end{methoddesc}
\subsection{Symconsts class}
Symconsts provides symbolic constants for use in symbolic expressions. These constants are preferred to floating point implementation as they can cancel perfectly when mathematical expressions are evaluated, avoiding numerical imprecision.
\begin{verbatim}
usage:
symconsts.pi this provides a Symbol object
Available constants currently include:
pi and e
\end{verbatim}
|