1 |
jgs |
102 |
% $Id$ |
2 |
gross |
625 |
% |
3 |
|
|
% Copyright © 2006 by ACcESS MNRF |
4 |
|
|
% \url{http://www.access.edu.au |
5 |
|
|
% Primary Business: Queensland, Australia. |
6 |
|
|
% Licensed under the Open Software License version 3.0 |
7 |
|
|
% http://www.opensource.org/licenses/osl-3.0.php |
8 |
|
|
% |
9 |
jgs |
102 |
|
10 |
gross |
625 |
|
11 |
jgs |
121 |
\section{The First Steps} |
12 |
jgs |
102 |
\label{FirstSteps} |
13 |
|
|
|
14 |
|
|
\begin{figure} |
15 |
gross |
599 |
\centerline{\includegraphics[width=\figwidth]{figures/FirstStepDomain}} |
16 |
jgs |
102 |
\caption{Domain $\Omega=[0,1]^2$ with outer normal field $n$.} |
17 |
|
|
\label{fig:FirstSteps.1} |
18 |
|
|
\end{figure} |
19 |
|
|
|
20 |
jgs |
107 |
In this chapter we will give an introduction how to use \escript to solve |
21 |
|
|
a partial differential equation \index{partial differential equation} (PDE \index{partial differential equation!PDE}). The reader should be familiar with Python. The knowledge presented at the Python tutorial at \url{http://docs.python.org/tut/tut.html} |
22 |
|
|
is sufficient. It is helpful if the reader has some basic knowledge of PDEs \index{partial differential equation}. |
23 |
jgs |
102 |
|
24 |
jgs |
107 |
The PDE \index{partial differential equation} we wish to solve is the Poisson equation \index{Poisson equation} |
25 |
jgs |
102 |
\begin{equation} |
26 |
|
|
-\Delta u =f |
27 |
|
|
\label{eq:FirstSteps.1} |
28 |
|
|
\end{equation} |
29 |
jgs |
107 |
for the solution $u$. The function $f$ is the given right hand side. The domain of interest, denoted by $\Omega$ |
30 |
jgs |
102 |
is the unit square |
31 |
|
|
\begin{equation} |
32 |
|
|
\Omega=[0,1]^2=\{ (x\hackscore 0;x\hackscore 1) | 0\le x\hackscore{0} \le 1 \mbox{ and } 0\le x\hackscore{1} \le 1 \} |
33 |
|
|
\label{eq:FirstSteps.1b} |
34 |
|
|
\end{equation} |
35 |
jgs |
107 |
The domain is shown in \fig{fig:FirstSteps.1}. |
36 |
jgs |
102 |
|
37 |
|
|
$\Delta$ denotes the Laplace operator\index{Laplace operator} which is defined by |
38 |
|
|
\begin{equation} |
39 |
|
|
\Delta u = (u\hackscore {,0})\hackscore{,0}+(u\hackscore{,1})\hackscore{,1} |
40 |
|
|
\label{eq:FirstSteps.1.1} |
41 |
|
|
\end{equation} |
42 |
jgs |
107 |
where, for any function $w$ and any direction $i$, $u\hackscore{,i}$ |
43 |
|
|
denotes the partial derivative \index{partial derivative} of $u$ with respect to $i$. |
44 |
jgs |
102 |
\footnote{Some readers |
45 |
|
|
may be more familiar with the Laplace operator\index{Laplace operator} being written |
46 |
|
|
as $\nabla^2$, and written in the form |
47 |
|
|
\begin{equation*} |
48 |
jgs |
110 |
\nabla^2 u = \nabla^t \cdot \nabla u = \frac{\partial^2 u}{\partial x\hackscore 0^2} |
49 |
jgs |
102 |
+ \frac{\partial^2 u}{\partial x\hackscore 1^2} |
50 |
|
|
\end{equation*} |
51 |
|
|
and \eqn{eq:FirstSteps.1} as |
52 |
|
|
\begin{equation*} |
53 |
|
|
-\nabla^2 u = f |
54 |
|
|
\end{equation*} |
55 |
|
|
} |
56 |
jgs |
107 |
Basically, in the subindex of a function, any index to the left of the comma denotes a spatial derivative with respect |
57 |
jgs |
102 |
to the index. To get a more compact form we will write $w\hackscore{,ij}=(w\hackscore {,i})\hackscore{,j}$ |
58 |
|
|
which leads to |
59 |
|
|
\begin{equation} |
60 |
|
|
\Delta u = u\hackscore{,00}+u\hackscore{,11}=\sum\hackscore{i=0}^2 u\hackscore{,ii} |
61 |
|
|
\label{eq:FirstSteps.1.1b} |
62 |
|
|
\end{equation} |
63 |
|
|
In some cases, and we will see examples for this in the next chapter, |
64 |
|
|
the usage of the nested $\sum$ symbols blows up the formulas and therefore |
65 |
jgs |
107 |
it is convenient to use the Einstein summation convention \index{summation convention}. This |
66 |
|
|
drops the $\sum$ sign and assumes that a summation over a repeated index is performed |
67 |
jgs |
102 |
("repeated index means summation"). For instance we write |
68 |
|
|
\begin{eqnarray} |
69 |
|
|
x\hackscore{i}y\hackscore{i}=\sum\hackscore{i=0}^2 x\hackscore{i}y\hackscore{i} \\ |
70 |
|
|
x\hackscore{i}u\hackscore{,i}=\sum\hackscore{i=0}^2 x\hackscore{i}u\hackscore{,i} \\ |
71 |
|
|
u\hackscore{,ii}=\sum\hackscore{i=0}^2 u\hackscore{,ii} \\ |
72 |
jgs |
107 |
x\hackscore{ij}u\hackscore{i,j}=\sum\hackscore{j=0}^2\sum\hackscore{i=0}^2 x\hackscore{ij}u\hackscore{i,j} \\ |
73 |
jgs |
102 |
\label{eq:FirstSteps.1.1c} |
74 |
|
|
\end{eqnarray} |
75 |
|
|
With the summation convention we can write the Poisson equation \index{Poisson equation} as |
76 |
|
|
\begin{equation} |
77 |
|
|
- u\hackscore{,ii} =1 |
78 |
|
|
\label{eq:FirstSteps.1.sum} |
79 |
|
|
\end{equation} |
80 |
lkettle |
575 |
where $f=1$ in this example. |
81 |
|
|
|
82 |
jgs |
102 |
On the boundary of the domain $\Omega$ the normal derivative $n\hackscore{i} u\hackscore{,i}$ |
83 |
|
|
of the solution $u$ shall be zero, ie. $u$ shall fulfill |
84 |
|
|
the homogeneous Neumann boundary condition\index{Neumann |
85 |
|
|
boundary condition!homogeneous} |
86 |
|
|
\begin{equation} |
87 |
|
|
n\hackscore{i} u\hackscore{,i}= 0 \;. |
88 |
|
|
\label{eq:FirstSteps.2} |
89 |
|
|
\end{equation} |
90 |
|
|
$n=(n\hackscore{i})$ denotes the outer normal field |
91 |
|
|
of the domain, see \fig{fig:FirstSteps.1}. Remember that we |
92 |
|
|
are applying the Einstein summation convention \index{summation convention}, i.e |
93 |
jgs |
107 |
$n\hackscore{i} u\hackscore{,i}= n\hackscore{0} u\hackscore{,0} + |
94 |
|
|
n\hackscore{1} u\hackscore{,1}$. |
95 |
jgs |
102 |
\footnote{Some readers may familiar with the notation |
96 |
|
|
\begin{equation*} |
97 |
|
|
\frac{\partial u}{\partial n} = n\hackscore{i} u\hackscore{,i} |
98 |
|
|
\end{equation*} |
99 |
|
|
for the normal derivative.} |
100 |
|
|
The Neumann boundary condition of \eqn{eq:FirstSteps.2} should be fulfilled on the |
101 |
|
|
set $\Gamma^N$ which is the top and right edge of the domain: |
102 |
|
|
\begin{equation} |
103 |
|
|
\Gamma^N=\{(x\hackscore 0;x\hackscore 1) \in \Omega | x\hackscore{0}=1 \mbox{ or } x\hackscore{1}=1 \} |
104 |
|
|
\label{eq:FirstSteps.2b} |
105 |
|
|
\end{equation} |
106 |
jgs |
107 |
On the bottom and the left edge of the domain which is defined |
107 |
jgs |
102 |
as |
108 |
|
|
\begin{equation} |
109 |
|
|
\Gamma^D=\{(x\hackscore 0;x\hackscore 1) \in \Omega | x\hackscore{0}=0 \mbox{ or } x\hackscore{1}=0 \} |
110 |
|
|
\label{eq:FirstSteps.2c} |
111 |
|
|
\end{equation} |
112 |
|
|
the solution shall be identically zero: |
113 |
|
|
\begin{equation} |
114 |
|
|
u=0 \; . |
115 |
|
|
\label{eq:FirstSteps.2d} |
116 |
|
|
\end{equation} |
117 |
jgs |
107 |
This kind of boundary condition is called a homogeneous Dirichlet boundary condition |
118 |
jgs |
102 |
\index{Dirichlet boundary condition!homogeneous}. The partial differential equation in \eqn{eq:FirstSteps.1.sum} together |
119 |
|
|
with the Neumann boundary condition \eqn{eq:FirstSteps.2} and |
120 |
|
|
Dirichlet boundary condition in \eqn{eq:FirstSteps.2d} form a so |
121 |
|
|
called boundary value |
122 |
jgs |
107 |
problem\index{boundary value problem} (BVP\index{boundary value problem!BVP}) for |
123 |
|
|
the unknown |
124 |
jgs |
102 |
function $u$. |
125 |
|
|
|
126 |
|
|
|
127 |
|
|
\begin{figure} |
128 |
gross |
599 |
\centerline{\includegraphics[width=\figwidth]{figures/FirstStepMesh.eps}} |
129 |
jgs |
102 |
\caption{Mesh of $4 \time 4$ elements on a rectangular domain. Here |
130 |
|
|
each element is a quadrilateral and described by four nodes, namely |
131 |
|
|
the corner points. The solution is interpolated by a bi-linear |
132 |
|
|
polynomial.} |
133 |
|
|
\label{fig:FirstSteps.2} |
134 |
|
|
\end{figure} |
135 |
|
|
|
136 |
|
|
In general the BVP\index{boundary value problem!BVP} cannot be solved analytically and numerical |
137 |
|
|
methods have to be used construct an approximation of the solution |
138 |
|
|
$u$. Here we will use the finite element method\index{finite element |
139 |
|
|
method} (FEM\index{finite element |
140 |
|
|
method!FEM}). The basic idea is to fill the domain with a |
141 |
jgs |
107 |
set of points called nodes. The solution is approximated by its |
142 |
jgs |
102 |
values on the nodes\index{finite element |
143 |
lkettle |
573 |
method!nodes}. Moreover, the domain is subdivided into smaller |
144 |
|
|
sub-domains called elements \index{finite element |
145 |
jgs |
102 |
method!element}. On each element the solution is |
146 |
|
|
represented by a polynomial of a certain degree through its values at |
147 |
|
|
the nodes located in the element. The nodes and its connection through |
148 |
|
|
elements is called a mesh\index{finite element |
149 |
jgs |
107 |
method!mesh}. \fig{fig:FirstSteps.2} shows an |
150 |
jgs |
102 |
example of a FEM mesh with four elements in the $x_0$ and four elements |
151 |
|
|
in the $x_1$ direction over the unit square. |
152 |
|
|
For more details we refer the reader to the literature, for instance |
153 |
|
|
\Ref{Zienc,NumHand}. |
154 |
|
|
|
155 |
|
|
\escript provides the class \Poisson to define a Poisson equation \index{Poisson equation}. |
156 |
|
|
(We will discuss a more general form of a PDE \index{partial differential equation!PDE} |
157 |
|
|
that can be defined through the \LinearPDE class later). The instantiation of |
158 |
|
|
a \Poisson class object requires the specification of the domain $\Omega$. In \escript |
159 |
|
|
the \Domain class objects are used to describe the geometry of a domain but it also |
160 |
|
|
contains information about the discretization methods and the actual solver which is used |
161 |
|
|
to solve the PDE. Here we are using the FEM library \finley \index{finite element |
162 |
|
|
method}. The following statements create the \Domain object \var{mydomain} from the |
163 |
|
|
\finley method \method{Rectangle} |
164 |
|
|
\begin{python} |
165 |
jgs |
107 |
from esys.finley import Rectangle |
166 |
|
|
mydomain = Rectangle(l0=1.,l1=1.,n0=40, n1=20) |
167 |
jgs |
102 |
\end{python} |
168 |
|
|
In this case the domain is a rectangle with the lower, left corner at point $(0,0)$ and |
169 |
|
|
the right, upper corner at $(\var{l0},\var{l1})=(1,1)$. |
170 |
jgs |
107 |
The arguments \var{n0} and \var{n1} define the number of elements in $x\hackscore{0}$ and |
171 |
jgs |
102 |
$x\hackscore{1}$-direction respectively. For more details on \method{Rectangle} and |
172 |
|
|
other \Domain generators within the \finley module, |
173 |
|
|
see \Chap{CHAPTER ON FINLEY}. |
174 |
|
|
|
175 |
jgs |
107 |
The following statements define the \Poisson class object \var{mypde} with domain \var{mydomain} and |
176 |
jgs |
102 |
the right hand side $f$ of the PDE to constant $1$: |
177 |
|
|
\begin{python} |
178 |
gross |
565 |
from esys.escript.linearPDEs import Poisson |
179 |
jgs |
107 |
mypde = Poisson(mydomain) |
180 |
|
|
mypde.setValue(f=1) |
181 |
jgs |
102 |
\end{python} |
182 |
|
|
We have not specified any boundary condition but the |
183 |
|
|
\Poisson class implicitly assumes homogeneous Neuman boundary conditions \index{Neumann |
184 |
|
|
boundary condition!homogeneous} defined by \eqn{eq:FirstSteps.2}. With this boundary |
185 |
|
|
condition the BVP\index{boundary value problem!BVP} we have defined has no unique solution. In fact, with any solution $u$ |
186 |
|
|
and any constant $C$ the function $u+C$ becomes a solution as well. We have to add |
187 |
|
|
a Dirichlet boundary condition \index{Dirichlet boundary condition}. This is done |
188 |
jgs |
107 |
by defining a characteristic function \index{characteristic function} |
189 |
|
|
which has positive values at locations $x=(x\hackscore{0},x\hackscore{1})$ where Dirichlet boundary condition is set |
190 |
jgs |
102 |
and $0$ elsewhere. In our case of $\Gamma^D$ defined by \eqn{eq:FirstSteps.2c}, |
191 |
gross |
565 |
we need to construct a function \var{gammaD} which is positive for the cases $x\hackscore{0}=0$ or $x\hackscore{1}=0$. To get |
192 |
|
|
an object \var{x} which represents locations in the domain one uses |
193 |
jgs |
102 |
\begin{python} |
194 |
lkettle |
573 |
x=mydomain.getX() |
195 |
jgs |
102 |
\end{python} |
196 |
gross |
660 |
The method \method{getX} of the \Domain \var{mydomain} |
197 |
jgs |
107 |
gives access to locations |
198 |
gross |
565 |
in the domain defined by \var{mydomain}. The object \var{x} is actually a \Data object which is |
199 |
gross |
660 |
discussed in \Chap{ESCRIPT CHAP} in more details. What we need to know here is that |
200 |
|
|
|
201 |
gross |
565 |
\var{x} has \Rank (=number of dimensions) and a \Shape (=tuple of dimensions) which can be checked by |
202 |
|
|
calling the \method{getRank} and \method{getShape} methods: |
203 |
|
|
\begin{python} |
204 |
|
|
print "rank ",x.getRank(),", shape ",x.getShape() |
205 |
|
|
\end{python} |
206 |
|
|
will print something like |
207 |
|
|
\begin{python} |
208 |
|
|
rank 1, shape (2,) |
209 |
|
|
\end{python} |
210 |
|
|
The \Data object also maintains type information which is represented by the |
211 |
|
|
\FunctionSpace of the object. For instance |
212 |
|
|
\begin{python} |
213 |
|
|
print x.getFunctionSpace() |
214 |
|
|
\end{python} |
215 |
|
|
will print |
216 |
|
|
\begin{python} |
217 |
|
|
Function space type: Finley_Nodes on FinleyMesh |
218 |
|
|
\end{python} |
219 |
|
|
which tells us that the coordinates are stored on the nodes of a \finley mesh. |
220 |
|
|
To get the $x\hackscore{0}$ coordinates of the locations we use the |
221 |
|
|
statement |
222 |
|
|
\begin{python} |
223 |
|
|
x0=x[0] |
224 |
|
|
\end{python} |
225 |
|
|
Object \var{x0} |
226 |
|
|
is again a \Data object now with \Rank $0$ and |
227 |
|
|
\Shape $()$. It inherits the \FunctionSpace from \var{x}: |
228 |
|
|
\begin{python} |
229 |
|
|
print x0.getRank(),x0.getShape(),x0.getFunctionSpace() |
230 |
|
|
\end{python} |
231 |
|
|
will print |
232 |
|
|
\begin{python} |
233 |
|
|
0 () Function space type: Finley_Nodes on FinleyMesh |
234 |
|
|
\end{python} |
235 |
|
|
We can now construct the function \var{gammaD} by |
236 |
|
|
\begin{python} |
237 |
lkettle |
573 |
from esys.escript import whereZero |
238 |
gross |
565 |
gammaD=whereZero(x[0])+whereZero(x[1]) |
239 |
|
|
\end{python} |
240 |
|
|
where |
241 |
|
|
\code{whereZero(x[0])} creates function which equals $1$ where \code{x[0]} is (allmost) equal to zero |
242 |
jgs |
107 |
and $0$ elsewhere. |
243 |
gross |
565 |
Similarly, \code{whereZero(x[1])} creates function which equals $1$ where \code{x[1]} is |
244 |
jgs |
107 |
equal to zero and $0$ elsewhere. |
245 |
gross |
565 |
The sum of the results of \code{whereZero(x[0])} and \code{whereZero(x[1])} |
246 |
|
|
gives a function on the domain \var{mydomain} which is exactly positive where $x\hackscore{0}$ or $x\hackscore{1}$ is equal to zero. |
247 |
|
|
Note that \var{gammaD} has the same \Rank, \Shape and \FunctionSpace like \var{x0} used to define it. So from |
248 |
|
|
\begin{python} |
249 |
|
|
print gammaD.getRank(),gammaD.getShape(),gammaD.getFunctionSpace() |
250 |
|
|
\end{python} |
251 |
|
|
one gets |
252 |
|
|
\begin{python} |
253 |
|
|
0 () Function space type: Finley_Nodes on FinleyMesh |
254 |
|
|
\end{python} |
255 |
jgs |
107 |
The additional parameter \var{q} of the \code{setValue} method of the \Poisson class defines the |
256 |
jgs |
102 |
characteristic function \index{characteristic function} of the locations |
257 |
|
|
of the domain where homogeneous Dirichlet boundary condition \index{Dirichlet boundary condition!homogeneous} |
258 |
|
|
are set. The complete definition of our example is now: |
259 |
|
|
\begin{python} |
260 |
jgs |
107 |
from esys.linearPDEs import Poisson |
261 |
jgs |
102 |
x = mydomain.getX() |
262 |
gross |
565 |
gammaD = whereZero(x[0])+whereZero(x[1]) |
263 |
jgs |
107 |
mypde = Poisson(domain=mydomain) |
264 |
lkettle |
573 |
mypde.setValue(f=1,q=gammaD) |
265 |
jgs |
102 |
\end{python} |
266 |
lkettle |
573 |
The first statement imports the \Poisson class definition from the \linearPDEs module \escript package. |
267 |
jgs |
107 |
To get the solution of the Poisson equation defined by \var{mypde} we just have to call its |
268 |
jgs |
102 |
\method{getSolution}. |
269 |
|
|
|
270 |
|
|
Now we can write the script to solve our test problem (Remember that |
271 |
gross |
569 |
lines starting with '\#' are comment lines in Python) (available as \file{poisson.py} |
272 |
jgs |
102 |
in the \ExampleDirectory): |
273 |
|
|
\begin{python} |
274 |
gross |
565 |
from esys.escript import * |
275 |
gross |
569 |
from esys.escript.linearPDEs import Poisson |
276 |
jgs |
107 |
from esys.finley import Rectangle |
277 |
jgs |
102 |
# generate domain: |
278 |
jgs |
107 |
mydomain = Rectangle(l0=1.,l1=1.,n0=40, n1=20) |
279 |
jgs |
102 |
# define characteristic function of Gamma^D |
280 |
|
|
x = mydomain.getX() |
281 |
gross |
565 |
gammaD = whereZero(x[0])+whereZero(x[1]) |
282 |
jgs |
102 |
# define PDE and get its solution u |
283 |
gross |
565 |
mypde = Poisson(domain=mydomain) |
284 |
|
|
mypde.setValue(f=1,q=gammaD) |
285 |
jgs |
102 |
u = mypde.getSolution() |
286 |
|
|
# write u to an external file |
287 |
gross |
565 |
saveVTK("u.xml",sol=u) |
288 |
jgs |
102 |
\end{python} |
289 |
gross |
565 |
The last statement writes the solution tagged with the name "sol" to the external file \file{u.xml} in |
290 |
|
|
\VTK file format. \VTK is a software library |
291 |
jgs |
102 |
for the visualization of scientific, engineering and analytical data and is freely available |
292 |
lkettle |
573 |
from \url{http://www.vtk.org}. There are a variety of graphical user interfaces |
293 |
gross |
565 |
for \VTK available, for instance \mayavi which can be downloaded from \url{http://mayavi.sourceforge.net/} but is also available on most |
294 |
|
|
\LINUX distributions. |
295 |
jgs |
102 |
|
296 |
|
|
\begin{figure} |
297 |
gross |
599 |
\centerline{\includegraphics[width=\figwidth]{figures/FirstStepResult.eps}} |
298 |
lkettle |
573 |
\caption{Visualization of the Poisson Equation Solution for $f=1$} |
299 |
jgs |
102 |
\label{fig:FirstSteps.3} |
300 |
|
|
\end{figure} |
301 |
|
|
|
302 |
jgs |
107 |
You can edit the script file using your favourite text editor (or the Integrated DeveLopment Environment IDLE |
303 |
gross |
569 |
for Python, see \url{http://idlefork.sourceforge.net}). If the script file has the name \file{poisson.py} \index{scripts!\file{poisson.py}} you can run the |
304 |
jgs |
102 |
script from any shell using the command: |
305 |
gross |
565 |
\begin{python} |
306 |
gross |
569 |
python poisson.py |
307 |
gross |
565 |
\end{python} |
308 |
gross |
569 |
After the script has (hopefully successfully) been completed you will find the file \file{u.xml} in the current |
309 |
jgs |
102 |
directory. An easy way to visualize the results is the command |
310 |
gross |
565 |
\begin{python} |
311 |
|
|
mayavi -d u.xml -m SurfaceMap & |
312 |
|
|
\end{python} |
313 |
|
|
to show the results, see \fig{fig:FirstSteps.3}. |