# Contents of /trunk/doc/user/escript.tex

Revision 2647 - (show annotations)
Fri Sep 4 05:25:25 2009 UTC (9 years, 7 months ago) by gross
File MIME type: application/x-tex
File size: 62396 byte(s)
fault system add. There is still an example for the usage missing.

 1 2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 % 4 % Copyright (c) 2003-2009 by University of Queensland 5 % Earth Systems Science Computational Center (ESSCC) 6 7 % 8 % Primary Business: Queensland, Australia 9 % Licensed under the Open Software License version 3.0 10 11 % 12 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 13 14 15 \chapter{The Module \escript} 16 \label{ESCRIPT CHAP} 17 18 19 \begin{figure} 20 \includegraphics[width=\textwidth]{figures/EscriptDiagram1} 21 \caption{\label{ESCRIPT DEP}Dependency of Function Spaces in Finley. An arrow indicates that a function in the 22 function space at the starting point can be interpolated to the function space of the arrow target. 23 All functionspaces on the left side can be interpolated to any of the functionspaces on the right.} 24 \end{figure} 25 26 \escript is a Python module that allows you to represent the values of 27 a function at points in a \Domain in such a way that the function will 28 be useful for the Finite Element Method (FEM) simulation. It also 29 provides what we call a function space that describes how the data is 30 used in the simulation. Stored along with the data is information 31 about the elements and nodes which will be used by \finley. 32 33 In order to understand what we mean by the term 'function space', 34 consider that the solution of a partial differential equation 35 \index{partial differential equation} (PDE) is a function on a domain 36 $\Omega$. When solving a PDE using FEM, the solution is 37 piecewise-differentiable but, in general, its gradient is 38 discontinuous. To reflect these different degrees of smoothness, 39 different function spaces are used. For instance, in FEM, the 40 displacement field is represented by its values at the nodes of the 41 mesh, and so is continuous. The strain, which is the symmetric 42 part of the gradient of the displacement field, is stored on the 43 element centers, and so is considered to be discontinuous. 44 45 A function space is described by a \FunctionSpace object. The 46 following statement generates the object \var{solution_space} which is 47 a \FunctionSpace object and provides access to the function space of 48 PDE solutions on the \Domain \var{mydomain}: 49 50 \begin{python} 51 solution_space=Solution(mydomain) 52 \end{python} 53 The following generators for function spaces on a \Domain \var{mydomain} are commonly used: 54 \begin{itemize} 55 \item \var{Solution(mydomain)}: solutions of a PDE. 56 \item \var{ReducedSolution(mydomain)}: solutions of a PDE with a reduced smoothness requirement. 57 \item \var{ContinuousFunction(mydomain)}: continuous functions, eg. a temperature distribution. 58 \item \var{Function(mydomain)}: general functions which are not necessarily continuous, eg. a stress field. 59 \item \var{FunctionOnBoundary(mydomain)}: functions on the boundary of the domain, eg. a surface pressure. 60 \item \var{FunctionOnContact0(mydomain)}: functions on side $0$ of the discontinuity. 61 \item \var{FunctionOnContact1(mydomain)}: functions on side $1$ of the discontinuity. 62 \end{itemize} 63 In some cases under-integration is used. For these cases the user may use a 64 \FunctionSpace from the floowing list: 65 \begin{itemize} 66 \item \var{ReducedFunction(mydomain)} 67 \item \var{ReducedFunctionOnBoundary(mydomain)} 68 \item \var{ReducedFunctionOnContact0(mydomain)} 69 \item \var{ReducedFunctionOnContact1(mydomain)} 70 \end{itemize} 71 In comparison to the full teh corresponding full version 72 they use a reduced number of integration nodes (typically one only) to represent values. 73 74 75 The reduced smoothness for PDE solution is often used to fulfill the Ladyzhenskaya–-Babuska–-Brezzi condition \cite{LBB} when 76 solving saddle point problems \index{saddle point problems}, eg. the Stokes equation. 77 A discontinuity \index{discontinuity} is a region within the domain across which functions may be discontinuous. 78 The location of discontinuity is defined in the \Domain object. 79 \fig{ESCRIPT DEP} shows the dependency between the types of function spaces in Finley (other libraries may have different relationships). 80 81 The solution of a PDE is a continuous function. Any continuous function can be seen as a general function 82 on the domain and can be restricted to the boundary as well as to one side of 83 discontinuity (the result will be different depending on 84 which side is chosen). Functions on any side of the 85 discontinuity can be seen as a function on the corresponding other side. 86 87 A function on the boundary or on one side of 88 the discontinuity cannot be seen as a general function on the domain as there are no values 89 defined for the interior. For most PDE solver libraries 90 the space of the solution and continuous functions is identical, however in some cases, eg. 91 when periodic boundary conditions are used in \finley, a solution 92 fulfills periodic boundary conditions while a continuous function does not have to be periodic. 93 94 The concept of function spaces describes the properties of 95 functions and allows abstraction from the actual representation 96 of the function in the context of a particular application. For instance, 97 in the FEM context a 98 function of the \Function type (written as \emph{Function()} in Figure~\ref{ESCRIPT DEP}) 99 is usually represented by its values at the element center, 100 but in a finite difference scheme the edge midpoint of cells is preferred. 101 By changing its function space you can use the same function in a Finite Difference 102 scheme instead of Finite Element scheme. 103 Changing the function space of a particular function 104 will typically lead to a change of its representation. 105 So, when seen as a general function, 106 a continuous function which is typically represented by its values 107 on the node of the FEM mesh or finite difference grid 108 must be interpolated to the element centers or the cell edges, 109 respectively. Interpolation happens automatically in \escript 110 whenever it is required. 111 112 In \escript the class that stores these functions is called \Data. 113 The function is represented through its values on \DataSamplePoints where 114 the \DataSamplePoints are chosen according to the function space 115 of the function. 116 \Data class objects are used to define the coefficients 117 of the PDEs to be solved by a PDE solver library 118 and also to store the solutions of the PDE. 119 120 The values of the function have a rank which gives the 121 number of indices, and a \Shape defining the range of each index. 122 The rank in \escript is limited to the range $0$ through $4$ and 123 it is assumed that the rank and \Shape is the same for all \DataSamplePoints. 124 The \Shape of a \Data object is a tuple (list) \var{s} of integers. The length 125 of \var{s} is the rank of the \Data object and the \var{i}-th index ranges between $0$ and $\var{s[i]}-1$. 126 For instance, a stress field has rank $2$ and 127 \Shape $(d,d)$ where $d$ is the spatial dimension. 128 The following statement creates the \Data object 129 \var{mydat} representing a 130 continuous function with values 131 of \Shape $(2,3)$ and rank $2$: 132 \begin{python} 133 mydat=Data(value=1,what=ContinuousFunction(myDomain),shape=(2,3)) 134 \end{python} 135 The initial value is the constant $1$ for all \DataSamplePoints and 136 all components. 137 138 \Data objects can also be created from any \numpy 139 array or any object, such as a list of floating point numbers, 140 that can be converted into a \numpyNDA \cite{NUMPY}. 141 The following two statements 142 create objects which are equivalent to \var{mydat}: 143 \begin{python} 144 mydat1=Data(value=numpy.ones((2,3)),what=ContinuousFunction(myDomain)) 145 mydat2=Data(value=[[1,1],[1,1],[1,1]],what=ContinuousFunction(myDomain)) 146 \end{python} 147 In the first case the initial value is \var{numpy.ones((2,3))} 148 which generates a $2 \times 3$ matrix as a \numpyNDA 149 filled with ones. The \Shape of the created \Data object 150 it taken from the \Shape of the array. In the second 151 case, the creator converts the initial value, which is a list of lists, 152 and converts it into a \numpyNDA before creating the actual 153 \Data object. 154 155 For convenience \escript provides creators for the most common types 156 of \Data objects in the following forms (\var{d} defines the 157 spatial dimension): 158 \begin{itemize} 159 \item \var{Scalar(0,Function(mydomain))} is the same as \var{Data(0,Function(myDomain),(,))} (each value is a scalar), 160 e.g a temperature field. 161 \item \var{Vector(0,Function(mydomain))} is the same as \var{Data(0,Function(myDomain),(d))} (each value is a vector), e.g 162 a velocity field. 163 \item \var{Tensor(0,Function(mydomain))} is the same as \var{Data(0,Function(myDomain),(d,d))}, 164 eg. a stress field. 165 \item \var{Tensor4(0,Function(mydomain))} is the same as \var{Data(0,Function(myDomain),(d,d,d,d))} 166 eg. a Hook tensor field. 167 \end{itemize} 168 Here the initial value is $0$ but any object that can be converted into a \numpyNDA and whose \Shape 169 is consistent with \Shape of the \Data object to be created can be used as the initial value. 170 171 \Data objects can be manipulated by applying unary operations (eg. cos, sin, log) point 172 and can be combined point-wise by applying arithmetic operations (eg. +, - ,* , /). 173 It is to be emphasized that \escript itself does not handle any spatial dependencies as 174 it does not know how values are interpreted by the processing PDE solver library. 175 However \escript invokes interpolation if this is needed during data manipulations. 176 Typically, this occurs in binary operation when both arguments belong to different 177 function spaces or when data are handed over to a PDE solver library 178 which requires functions to be represented in a particular way. 179 180 The following example shows the usage of {\tt Data} objects: Assume we have a 181 displacement field $u$ and we want to calculate the corresponding stress field 182 $\sigma$ using the linear--elastic isotropic material model 183 \begin{eqnarray}\label{eq: linear elastic stress} 184 \sigma\hackscore {ij}=\lambda u\hackscore {k,k} \delta\hackscore {ij} + \mu ( u\hackscore {i,j} + u\hackscore {j,i}) 185 \end{eqnarray} 186 where $\delta\hackscore {ij}$ is the Kronecker symbol and 187 $\lambda$ and $\mu$ are the Lame coefficients. The following function 188 takes the displacement {\tt u} and the Lame coefficients 189 \var{lam} and \var{mu} as arguments and returns the corresponding stress: 190 \begin{python} 191 from esys.escript import * 192 def getStress(u,lam,mu): 193 d=u.getDomain().getDim() 194 g=grad(u) 195 stress=lam*trace(g)*kronecker(d)+mu*(g+transpose(g)) 196 return stress 197 \end{python} 198 The variable 199 \var{d} gives the spatial dimension of the 200 domain on which the displacements are defined. 201 \var{kronecker} returns the Kronecker symbol with indexes 202 $i$ and $j$ running from $0$ to \var{d}-1. The call \var{grad(u)} requires 203 the displacement field \var{u} to be in the \var{Solution} or \ContinuousFunction 204 function space. The result \var{g} as well as the returned stress will be in the \Function function space. 205 If, for example, \var{u} is the solution of a PDE then \var{getStress} might be called 206 in the following way: 207 \begin{python} 208 s=getStress(u,1.,2.) 209 \end{python} 210 However \var{getStress} can also be called with \Data objects as values for 211 \var{lam} and \var{mu} which, 212 for instance in the case of a temperature dependency, are calculated by an expression. 213 The following call is equivalent to the previous example: 214 \begin{python} 215 lam=Scalar(1.,ContinuousFunction(mydomain)) 216 mu=Scalar(2.,Function(mydomain)) 217 s=getStress(u,lam,mu) 218 \end{python} 219 220 The function \var{lam} belongs to the \ContinuousFunction function space 221 but with \var{g} the function \var{trace(g)} is in the \Function function space. 222 In the evaluation of the product \var{lam*trace(g)} we have different function 223 spaces (on the nodes versus in the centers) and at first glance we have incompatible data. 224 \escript converts the arguments in an appropriate function space according to 225 Table~\ref{ESCRIPT DEP}. In this example that means 226 \escript sees \var{lam} as a function of the \Function function space. 227 In the context of FEM this means the nodal values of 228 \var{lam} are interpolated to the element centers. 229 The interpolation is automatic and requires no special handling. 230 231 \begin{figure} 232 \includegraphics[width=\textwidth]{figures/EscriptDiagram2} 233 \caption{\label{Figure: tag}Element Tagging. A rectangular mesh over a region with two rock types {\it white} and {\it gray}. 234 The number in each cell refers to the major rock type present in the cell ($1$ for {\it white} and $2$ for {\it gray}). 235 } 236 \end{figure} 237 238 Material parameters such as the Lame coefficients are typically dependent on rock types present in the 239 area of interest. A common technique to handle these kinds of material parameters is "tagging", which 240 uses storage efficiently. \fig{Figure: tag} 241 shows an example. In this case two rock types {\it white} and {\it gray} can be found in the domain. The domain 242 is subdivided into triangular shaped cells. Each 243 cell has a tag indicating the rock type predominately found in this cell. Here $1$ is used to indicate 244 rock type {\it white} and $2$ for rock type {\it gray}. The tags are assigned at the time when the cells are generated 245 and stored in the \Domain class object. To allow easier usage of tags, names can be used instead of numbers. These names are typically defined 246 at the time when the geometry is generated. 247 248 The following statements show how, for the 249 example of \fig{Figure: tag}, the stress calculation discussed above and tagged values are used for 250 \var{lam}: 251 \begin{python} 252 lam=Scalar(value=2.,what=Function(mydomain)) 253 insertTaggedValue(lam,white=30.,gray=5000.) 254 s=getStress(u,lam,2.) 255 \end{python} 256 In this example \var{lam} is set to $30$ for those cells with tag {\it white} (=$1$) and to $5000.$ for those cells 257 with tag {\it gray} (=$2$_. The initial value $2$ of \var{lam} is used as a default value for the case when a tag 258 is encountered which has not been linked with a value. The \var{getStress} method 259 does not need to be changed now that we are using tags. 260 \escript resolves the tags when \var{lam*trace(g)} is calculated. 261 262 This brings us to a very important point about \escript. 263 You can develop a simulation with constant Lame coefficients, and then later switch to tagged 264 Lame coefficients without otherwise changing your python script. 265 In short, you can use the same script to model with different domains and different types of input data. 266 267 There are three main ways in which \Data objects are represented internally: constant, tagged, and expanded. 268 In the constant case, the same value is used at each sample point and only a single value is stored to save memory. 269 In the expanded case, each sample point has an individual value (such as for the solution of a PDE). 270 This is where your largest data sets will be created because the values are stored as a complete array. 271 The tagged case has already been discussed above. 272 273 Expanded data is created when you create a \Data object with expanded=True. 274 Tagged data sets are created when you use the insertTaggedValue() method as shown above. 275 276 Values are accessed through a sample reference number. Operations on expanded \Data 277 objects have to be performed for each sample point individually. When tagged values are used, the values are 278 held in a dictionary. Operations on tagged data require processing the set of tagged values only, rather than 279 processing the value for each individual sample point. 280 \escript allows any mixture of constant, tagged and expanded data in a single expression. 281 282 \Data objects can be written to disk files and read with \var{dump} and \var{load}, both of which use \netCDF\cite{NETCDF}. 283 Use these to save data for visualization, checkpoint/restart or simply to save and reuse data that was expensive to compute. 284 285 For instance to save the coordinates of the data points of the 286 \ContinuousFunction to the file {\tt x.nc} use 287 \begin{python} 288 x=ContinuousFunction(mydomain).getX() 289 x.dump("x.nc") 290 mydomain.dump(dom.nc) 291 \end{python} 292 To recover the object \var{x} and \var{mydomain} was a \finley mesh use 293 \begin{python} 294 from esys.finley import LoadMesh 295 mydomain=LoadMesh('dom.nc') 296 x=load("x.nc", mydomain) 297 \end{python} 298 It possible to rerun the mechanism that was originally used to generates 299 \var{mydomain} to recreate \var{mydomain}. However in most cases using \var{dump} and 300 load is faster in particular if optimization has been applied. In case that 301 \escript is running on more than one \MPI processor the \var{dump} will create an individual file for each processor containing the local data. In order to avoid conflicts the file name is extended by the \MPI processor rank. 302 303 The function space of the \Data is stored in {\tt x.nc}, though. 304 If the \Data object 305 is expanded, the number of data points in the file and of the \Domain for the particular \FunctionSpace must match. 306 Moreover, the ordering of the values is checked using the reference identifiers provided by 307 \FunctionSpace on the \Domain. In some cases, data points will be re-ordered. Take care to be sure you get what you want! 308 309 310 \section{\escript Classes} 311 \declaremodule{extension}{esys.escript} 312 \modulesynopsis{Data manipulation} 313 314 \subsection{\Domain class} 315 \begin{classdesc}{Domain}{} 316 A \Domain object is used to describe a geometric region together with 317 a way of representing functions over this region. 318 The \Domain class provides an abstract interface to the domain of \FunctionSpace and \Data objects. 319 \Domain needs to be subclassed in order to provide a complete implementation. 320 \end{classdesc} 321 The following methods are available: 322 \begin{methoddesc}[Domain]{getDim}{} 323 returns the spatial dimension of the \Domain. 324 \end{methoddesc} 325 \begin{methoddesc}[Domain]{dump}{filename} 326 dumps the \Domain into the file \var{filename}. 327 \end{methoddesc} 328 \begin{methoddesc}[Domain]{getX}{} 329 returns the locations in the \Domain. The \FunctionSpace of the returned 330 \Data object is chosen by the \Domain implementation. Typically it will be 331 in the \Function. 332 \end{methoddesc} 333 334 \begin{methoddesc}[Domain]{setX}{newX} 335 assigns a new location to the \Domain. \var{newX} has to have \Shape $(d,)$ 336 where $d$ is the spatial dimension of the domain. Typically \var{newX} must be 337 in the \ContinuousFunction but the space actually to be used depends on the \Domain implementation. 338 \end{methoddesc} 339 340 \begin{methoddesc}[Domain]{getNormal}{} 341 returns the surface normals on the boundary of the \Domain as \Data object. 342 \end{methoddesc} 343 344 \begin{methoddesc}[Domain]{getSize}{} 345 returns the local sample size, e.g. the element diameter, as \Data object. 346 \end{methoddesc} 347 348 \begin{methoddesc}[Domain]{setTagMap}{tag_name, tag} 349 defines a mapping of the tag name \var{tag_name} to the \var{tag}. 350 \end{methoddesc} 351 \begin{methoddesc}[Domain]{getTag}{tag_name} 352 returns the tag associated with the tag name \var{tag_name}. 353 \end{methoddesc} 354 \begin{methoddesc}[Domain]{isValidTagName}{tag_name} 355 return \True if \var{tag_name} is a valid tag name. 356 \end{methoddesc} 357 358 \begin{methoddesc}[Domain]{__eq__}{arg} 359 (python == operator) returns \True if the \Domain \var{arg} describes the same domain. Otherwise 360 \False is returned. 361 \end{methoddesc} 362 363 \begin{methoddesc}[Domain]{__ne__}{arg} 364 (python != operator) returns \True if the \Domain \var{arg} does not describe the same domain. 365 Otherwise \False is returned. 366 \end{methoddesc} 367 368 \begin{methoddesc}[Domain]{__str__}{arg} 369 (python str() function) returns string representation of the \Domain. 370 \end{methoddesc} 371 372 \begin{methoddesc}[Domain]{onMasterProcessor)}{} 373 returns \True if the processor is the master processor within 374 the \MPI processor group used by the \Domain. This is the processor with rank 0. 375 If \MPI support is not enabled the return value is always \True. 376 \end{methoddesc} 377 378 \begin{methoddesc}[Domain]{getMPISize}{} 379 returns the number of \MPI processors used for this \Domain. If \MPI support is not enabled 380 1 is returned. 381 \end{methoddesc} 382 383 \begin{methoddesc}[Domain]{getMPIRank}{} 384 returns the rank of the processor executing the statement 385 within the \MPI processor group used by the \Domain. 386 If \MPI support is not enabled 0 is returned. 387 \end{methoddesc} 388 389 \begin{methoddesc}[Domain]{MPIBarrier}{} 390 executes barrier synchronization within 391 the \MPI processor group used by the \Domain. 392 If \MPI support is not enabled, this command does nothing. 393 \end{methoddesc} 394 395 \subsection{\FunctionSpace class} 396 \begin{classdesc}{FunctionSpace}{} 397 \FunctionSpace objects are used to define properties of \Data objects, such as continuity. \FunctionSpace objects 398 are instantiated by generator functions. A \Data object in a particular \FunctionSpace is 399 represented by its values at \DataSamplePoints which are defined by the type and the \Domain of the 400 \FunctionSpace. 401 \end{classdesc} 402 The following methods are available: 403 \begin{methoddesc}[FunctionSpace]{getDim}{} 404 returns the spatial dimension of the \Domain of the \FunctionSpace. 405 \end{methoddesc} 406 407 408 409 \begin{methoddesc}[FunctionSpace]{getX}{} 410 returns the location of the \DataSamplePoints. 411 \end{methoddesc} 412 413 \begin{methoddesc}[FunctionSpace]{getNormal}{} 414 If the domain of functions in the \FunctionSpace 415 is a hyper-manifold (e.g. the boundary of a domain) 416 the method returns the outer normal at each of the 417 \DataSamplePoints. Otherwise an exception is raised. 418 \end{methoddesc} 419 420 \begin{methoddesc}[FunctionSpace]{getSize}{} 421 returns a \Data objects measuring the spacing of the \DataSamplePoints. 422 The size may be zero. 423 \end{methoddesc} 424 425 \begin{methoddesc}[FunctionSpace]{getDomain}{} 426 returns the \Domain of the \FunctionSpace. 427 \end{methoddesc} 428 429 \begin{methoddesc}[FunctionSpace]{setTags}{new_tag, mask} 430 assigns a new tag \var{new_tag} to all data sample 431 where \var{mask} is positive for a least one data point. 432 \var{mask} must be defined on the this \FunctionSpace. 433 Use the \var{setTagMap} to assign a tag name to \var{new_tag}. 434 \end{methoddesc} 435 436 \begin{methoddesc}[FunctionSpace]{__eq__}{arg} 437 (python == operator) returns \True if the \Domain \var{arg} describes the same domain. Otherwise 438 \False is returned. 439 \end{methoddesc} 440 441 \begin{methoddesc}[FunctionSpace]{__ne__}{arg} 442 (python != operator) returns \True if the \Domain \var{arg} do not describe the same domain. 443 Otherwise \False is returned. 444 \end{methoddesc} 445 446 \begin{methoddesc}[Domain]{__str__}{g} 447 (python str() function) returns string representation of the \Domain. 448 \end{methoddesc} 449 450 The following function provide generators for \FunctionSpace objects: 451 \begin{funcdesc}{Function}{domain} 452 returns the \Function on the \Domain \var{domain}. \Data objects in this type of \Function 453 are defined over the whole geometric region defined by \var{domain}. 454 \end{funcdesc} 455 456 \begin{funcdesc}{ContinuousFunction}{domain} 457 returns the \ContinuousFunction on the \Domain domain. \Data objects in this type of \Function 458 are defined over the whole geometric region defined by \var{domain} and assumed to represent 459 a continuous function. 460 \end{funcdesc} 461 462 \begin{funcdesc}{FunctionOnBoundary}{domain} 463 returns the \ContinuousFunction on the \Domain domain. \Data objects in this type of \Function 464 are defined on the boundary of the geometric region defined by \var{domain}. 465 \end{funcdesc} 466 467 \begin{funcdesc}{FunctionOnContactZero}{domain} 468 returns the \FunctionOnContactZero the \Domain domain. \Data objects in this type of \Function 469 are defined on side 0 of a discontinuity within the geometric region defined by \var{domain}. 470 The discontinuity is defined when \var{domain} is instantiated. 471 \end{funcdesc} 472 473 \begin{funcdesc}{FunctionOnContactOne}{domain} 474 returns the \FunctionOnContactOne on the \Domain domain. 475 \Data objects in this type of \Function 476 are defined on side 1 of a discontinuity within the geometric region defined by \var{domain}. 477 The discontinuity is defined when \var{domain} is instantiated. 478 \end{funcdesc} 479 480 \begin{funcdesc}{Solution}{domain} 481 returns the \SolutionFS on the \Domain domain. \Data objects in this type of \Function 482 are defined on geometric region defined by \var{domain} and are solutions of 483 partial differential equations \index{partial differential equation}. 484 \end{funcdesc} 485 486 \begin{funcdesc}{ReducedSolution}{domain} 487 returns the \ReducedSolutionFS on the \Domain domain. \Data objects in this type of \Function 488 are defined on geometric region defined by \var{domain} and are solutions of 489 partial differential equations \index{partial differential equation} with a reduced smoothness 490 for the solution approximation. 491 \end{funcdesc} 492 493 \subsection{\Data Class} 494 \label{SEC ESCRIPT DATA} 495 496 The following table shows arithmetic operations that can be performed point-wise on 497 \Data objects. 498 \begin{tableii}{l|l}{textrm}{expression}{Description} 499 \lineii{+\var{arg0}} {identical to \var{arg} \index{+}} 500 \lineii{-\var{arg0}} {negation\index{-}} 501 \lineii{\var{arg0}+\var{arg1}} {adds \var{arg0} and \var{arg1} \index{+}} 502 \lineii{\var{arg0}*\var{arg1}} {multiplies \var{arg0} and \var{arg1} \index{*}} 503 \lineii{\var{arg0}-\var{arg1}} {difference \var{arg1} from\var{arg1} \index{-}} 504 \lineii{\var{arg0}/\var{arg1}} {divide \var{arg0} by \var{arg1} \index{/}} 505 \lineii{\var{arg0}**\var{arg1}} {raises \var{arg0} to the power of \var{arg1} \index{**}} 506 \end{tableii} 507 At least one of the arguments \var{arg0} or \var{arg1} must be a 508 \Data object. 509 Either of the arguments may be a \Data object, a python number or a \numpy object. 510 511 If \var{arg0} or \var{arg1} are 512 not defined on the same \FunctionSpace, then an attempt is made to convert \var{arg0} 513 to the \FunctionSpace of \var{arg1} or to convert \var{arg1} to 514 the \FunctionSpace of \var{arg0}. Both arguments must have the same 515 \Shape or one of the arguments may be of rank 0 (a constant). 516 517 The returned \Data object has the same \Shape and is defined on 518 the \DataSamplePoints as \var{arg0} or \var{arg1}. 519 520 The following table shows the update operations that can be applied to 521 \Data objects: 522 \begin{tableii}{l|l}{textrm}{expression}{Description} 523 \lineii{\var{arg0}+=\var{arg2}} {adds \var{arg0} to \var{arg2} \index{+}} 524 \lineii{\var{arg0}*=\var{arg2}} {multiplies \var{arg0} with \var{arg2} \index{*}} 525 \lineii{\var{arg0}-=\var{arg2}} {subtracts \var{arg2} from\var{arg2} \index{-}} 526 \lineii{\var{arg0}/=\var{arg2}} {divides \var{arg0} by \var{arg2} \index{/}} 527 \lineii{\var{arg0}**=\var{arg2}} {raises \var{arg0} by \var{arg2} \index{**}} 528 \end{tableii} 529 \var{arg0} must be a \Data object. \var{arg1} must be a 530 \Data object or an object that can be converted into a 531 \Data object. \var{arg1} must have the same \Shape as 532 \var{arg0} or have rank 0. In the latter case it is 533 assumed that the values of \var{arg1} are constant for all 534 components. \var{arg1} must be defined in the same \FunctionSpace as 535 \var{arg0} or it must be possible to interpolate \var{arg1} onto the 536 \FunctionSpace of \var{arg0}. 537 538 The \Data class supports taking slices from a \Data object as well as assigning new values to a slice of an existing 539 \Data object. \index{slicing} 540 The following expressions for taking and setting slices are valid: 541 \begin{tableiii}{l|ll}{textrm}{rank of \var{arg}}{slicing expression}{\Shape of returned and assigned object} 542 \lineiii{0}{ no slicing } {-} 543 \lineiii{1}{\var{arg[l0:u0]}} {(\var{u0}-\var{l0},)} 544 \lineiii{2}{\var{arg[l0:u0,l1:u1]}} {(\var{u0}-\var{l0},\var{u1}-\var{l1})} 545 \lineiii{3}{\var{arg[l0:u0,l1:u1,l2:u2]} } {(\var{u0}-\var{l0},\var{u1}-\var{l1},\var{u2}-\var{l2})} 546 \lineiii{4}{\var{arg[l0:u0,l1:u1,l2:u2,l3:u3]}} {(\var{u0}-\var{l0},\var{u1}-\var{l1},\var{u2}-\var{l2},\var{u3}-\var{l3})} 547 \end{tableiii} 548 where \var{s} is the \Shape of \var{arg} and 549 $0 \le \var{l0} \le \var{u0} \le \var{s[0]},$ 550 $0 \le \var{l1} \le \var{u1} \le \var{s[1]},$ 551 $0 \le \var{l2} \le \var{u2} \le \var{s[2]},$ 552 $0 \le \var{l3} \le \var{u3} \le \var{s[3]}.$ 553 Any of the lower indexes \var{l0}, \var{l1}, \var{l2} and \var{l3} may not be present in which case 554 $0$ is assumed. 555 Any of the upper indexes \var{u0}, \var{u1}, \var{u2} and \var{u3} may be omitted, in which case, the upper limit for that dimension is assumed. 556 The lower and upper index may be identical, in which case the column and the lower or upper 557 index may be dropped. In the returned or in the object assigned to a slice, the corresponding component is dropped, 558 i.e. the rank is reduced by one in comparison to \var{arg}. 559 The following examples show slicing in action: 560 \begin{python} 561 t=Data(1.,(4,4,6,6),Function(mydomain)) 562 t[1,1,1,0]=9. 563 s=t[:2,:,2:6,5] # s has rank 3 564 s[:,:,1]=1. 565 t[:2,:2,5,5]=s[2:4,1,:2] 566 \end{python} 567 568 \subsection{Generation of \Data objects} 569 \begin{classdesc}{Data}{value=0,shape=(,),what=FunctionSpace(),expand=\False} 570 creates a \Data object with \Shape \var{shape} in the \FunctionSpace \var{what}. 571 The values at all \DataSamplePoints are set to the double value \var{value}. If \var{expanded} is \True 572 the \Data object is represented in expanded from. 573 \end{classdesc} 574 575 \begin{classdesc}{Data}{value,what=FunctionSpace(),expand=\False} 576 creates a \Data object in the \FunctionSpace \var{what}. 577 The value for each \DataSamplePoints is set to \var{value}, which could be a \numpy, \Data object \var{value} or a dictionary of 578 \numpy or floating point numbers. In the latter case the keys must be integers and are used 579 as tags. 580 The \Shape of the returned object is equal to the \Shape of \var{value}. If \var{expanded} is \True 581 the \Data object is represented in expanded form. 582 \end{classdesc} 583 584 \begin{classdesc}{Data}{} 585 creates an \EmptyData object. The \EmptyData object is used to indicate that an argument is not present 586 where a \Data object is required. 587 \end{classdesc} 588 589 \begin{funcdesc}{Scalar}{value=0.,what=FunctionSpace(),expand=\False} 590 returns a \Data object of rank 0 (a constant) in the \FunctionSpace \var{what}. 591 Values are initialized with \var{value}, a double precision quantity. If \var{expanded} is \True 592 the \Data object is represented in expanded from. 593 \end{funcdesc} 594 595 \begin{funcdesc}{Vector}{value=0.,what=FunctionSpace(),expand=\False} 596 returns a \Data object of \Shape \var{(d,)} in the \FunctionSpace \var{what}, 597 where \var{d} is the spatial dimension of the \Domain of \var{what}. 598 Values are initialed with \var{value}, a double precision quantity. If \var{expanded} is \True 599 the \Data object is represented in expanded from. 600 \end{funcdesc} 601 602 \begin{funcdesc}{Tensor}{value=0.,what=FunctionSpace(),expand=\False} 603 returns a \Data object of \Shape \var{(d,d)} in the \FunctionSpace \var{what}, 604 where \var{d} is the spatial dimension of the \Domain of \var{what}. 605 Values are initialed with \var{value}, a double precision quantity. If \var{expanded} is \True 606 the \Data object is represented in expanded from. 607 \end{funcdesc} 608 609 \begin{funcdesc}{Tensor3}{value=0.,what=FunctionSpace(),expand=\False} 610 returns a \Data object of \Shape \var{(d,d,d)} in the \FunctionSpace \var{what}, 611 where \var{d} is the spatial dimension of the \Domain of \var{what}. 612 Values are initialed with \var{value}, a double precision quantity. If \var{expanded} is \True 613 the \Data object is re\var{arg}presented in expanded from. 614 \end{funcdesc} 615 616 \begin{funcdesc}{Tensor4}{value=0.,what=FunctionSpace(),expand=\False} 617 returns a \Data object of \Shape \var{(d,d,d,d)} in the \FunctionSpace \var{what}, 618 where \var{d} is the spatial dimension of the \Domain of \var{what}. 619 Values are initialized with \var{value}, a double precision quantity. If \var{expanded} is \True 620 the \Data object is represented in expanded from. 621 \end{funcdesc} 622 623 \begin{funcdesc}{load}{filename,domain} 624 recovers a \Data object on \Domain \var{domain} from the file \var{filename}, which was created by \function{dump}. 625 \end{funcdesc} 626 627 \subsection{\Data methods} 628 These are the most frequently-used methods of the 629 \Data class. A complete list of methods can be found on \ReferenceGuide. 630 \begin{methoddesc}[Data]{getFunctionSpace}{} 631 returns the \FunctionSpace of the object. 632 \end{methoddesc} 633 634 \begin{methoddesc}[Data]{getDomain}{} 635 returns the \Domain of the object. 636 \end{methoddesc} 637 638 \begin{methoddesc}[Data]{getShape}{} 639 returns the \Shape of the object as a \class{tuple} of 640 integers. 641 \end{methoddesc} 642 643 \begin{methoddesc}[Data]{getRank}{} 644 returns the rank of the data on each data point. \index{rank} 645 \end{methoddesc} 646 647 \begin{methoddesc}[Data]{isEmpty}{} 648 returns \True id the \Data object is the \EmptyData object. 649 Otherwise \False is returned. 650 Note that this is not the same as asking if the object contains no \DataSamplePoints. 651 \end{methoddesc} 652 653 \begin{methoddesc}[Data]{setTaggedValue}{tag_name,value} 654 assigns the \var{value} to all \DataSamplePoints which have the tag 655 assigned to \var{tag_name}. \var{value} must be an object of class 656 \class{numpy.ndarray} or must be convertible into a 657 \class{numpy.ndarray} object. \var{value} (or the corresponding 658 \class{numpy.ndarray} object) must be of rank $0$ or must have the 659 same rank like the object. 660 If a value has already be defined for tag \var{tag_name} within the object 661 it is overwritten by the new \var{value}. If the object is expanded, 662 the value assigned to \DataSamplePoints with tag \var{tag_name} is replaced by 663 \var{value}. If no tag is assigned tag name \var{tag_name}, no value is set. 664 \end{methoddesc} 665 666 \begin{methoddesc}[Data]{dump}{filename} 667 dumps the \Data object to the file \var{filename}. The file stores the 668 function space but not the \Domain. It is in the responsibility of the user to 669 save the \Domain. 670 \end{methoddesc} 671 672 \begin{methoddesc}[Data]{__str__}{} 673 returns a string representation of the object. 674 \end{methoddesc} 675 676 \subsection{Functions of \Data objects} 677 This section lists the most important functions for \Data class objects \var{a}. 678 A complete list and a more detailed description of the functionality can be found on \ReferenceGuide. 679 \begin{funcdesc}{saveVTK}{filename,**kwdata} 680 writes \Data defined by keywords in the file with \var{filename} using the 681 vtk file format \VTK file format. The key word is used as an identifier. The statement 682 \begin{python} 683 saveVTK("out.xml",temperature=T,velocity=v) 684 \end{python} 685 will write the scalar \var{T} as \var{temperature} and the vector \var{v} as \var{velocity} into the 686 file \file{out.xml}. Restrictions on the allowed combinations of \FunctionSpace apply. 687 \end{funcdesc} 688 \begin{funcdesc}{saveDX}{filename,**kwdata} 689 writes \Data defined by keywords in the file with \var{filename} using the 690 vtk file format \OpenDX file format. The key word is used as an identifier. The statement 691 \begin{python} 692 saveDX("out.dx",temperature=T,velocity=v) 693 \end{python} 694 will write the scalar \var{T} as \var{temperature} and the vector \var{v} as \var{velocity} into the 695 file \file{out.dx}. Restrictions on the allowed combinations of \FunctionSpace apply. 696 \end{funcdesc} 697 \begin{funcdesc}{kronecker}{d} 698 returns a \RankTwo \Data object in \FunctionSpace \var{d} such that 699 \begin{equation} 700 \code{kronecker(d)}\left[ i,j\right] = \left\{ 701 \begin{array}{cc} 702 1 & \mbox{ if } i=j \\ 703 0 & \mbox{ otherwise } 704 \end{array} 705 \right. 706 \end{equation} 707 If \var{d} is an integer a $(d,d)$ \numpy array is returned. 708 \end{funcdesc} 709 \begin{funcdesc}{identityTensor}{d} 710 is a synonym for \code{kronecker} (see above). 711 % returns a \RankTwo \Data object in \FunctionSpace \var{d} such that 712 % 713 % \code{identityTensor(d)}\left[ i,j\right] = \left\{ 714 % \begin{array}{cc} 715 % 1 & \mbox{ if } i=j \\ 716 % 0 & \mbox{ otherwise } 717 % \end{array} 718 % \right. 719 % 720 % If \var{d} is an integer a $(d,d)$ \numpy array is returned. 721 \end{funcdesc} 722 \begin{funcdesc}{identityTensor4}{d} 723 returns a \RankFour \Data object in \FunctionSpace \var{d} such that 724 \begin{equation} 725 \code{identityTensor(d)}\left[ i,j,k,l\right] = \left\{ 726 \begin{array}{cc} 727 1 & \mbox{ if } i=k \mbox{ and } j=l\\ 728 0 & \mbox{ otherwise } 729 \end{array} 730 \right. 731 \end{equation} 732 If \var{d} is an integer a $(d,d,d,d)$ \numpy array is returned. 733 \end{funcdesc} 734 \begin{funcdesc}{unitVector}{i,d} 735 returns a \RankOne \Data object in \FunctionSpace \var{d} such that 736 \begin{equation} 737 \code{identityTensor(d)}\left[ j \right] = \left\{ 738 \begin{array}{cc} 739 1 & \mbox{ if } j=i\\ 740 0 & \mbox{ otherwise } 741 \end{array} 742 \right. 743 \end{equation} 744 If \var{d} is an integer a $(d,)$ \numpy array is returned. 745 746 \end{funcdesc} 747 748 \begin{funcdesc}{Lsup}{a} 749 returns the $L^{sup}$ norm of \var{arg}. This is the maximum of the absolute values 750 over all components and all \DataSamplePoints of \var{a}. 751 \end{funcdesc} 752 753 \begin{funcdesc}{sup}{a} 754 returns the maximum value over all components and all \DataSamplePoints of \var{a}. 755 \end{funcdesc} 756 757 \begin{funcdesc}{inf}{a} 758 returns the minimum value over all components and all \DataSamplePoints of \var{a} 759 \end{funcdesc} 760 761 762 763 \begin{funcdesc}{minval}{a} 764 returns at each \DataSamplePoints the minimum value over all components. 765 \end{funcdesc} 766 767 \begin{funcdesc}{maxval}{a} 768 returns at each \DataSamplePoints the maximum value over all components. 769 \end{funcdesc} 770 771 \begin{funcdesc}{length}{a} 772 returns at Euclidean norm at each \DataSamplePoints. For a \RankFour \var{a} this is 773 \begin{equation} 774 \code{length(a)}=\sqrt{\sum\hackscore{ijkl} \var{a} \left[i,j,k,l\right]^2} 775 \end{equation} 776 \end{funcdesc} 777 \begin{funcdesc}{trace}{a\optional{,axis_offset=0}} 778 returns the trace of \var{a}. This is the sum over components \var{axis_offset} and \var{axis_offset+1} with the same index. For instance in the 779 case of a \RankTwo function and this is 780 \begin{equation} 781 \code{trace(a)}=\sum\hackscore{i} \var{a} \left[i,i\right] 782 \end{equation} 783 and for a \RankFour function and \code{axis_offset=1} this is 784 \begin{equation} 785 \code{trace(a,1)}\left[i,j\right]=\sum\hackscore{k} \var{a} \left[i,k,k,j\right] 786 \end{equation} 787 \end{funcdesc} 788 789 \begin{funcdesc}{transpose}{a\optional{, axis_offset=None}} 790 returns the transpose of \var{a}. This swaps the first \var{axis_offset} components of \var{a} with the rest. If \var{axis_offset} is not 791 present \code{int(r/2)} is used where \var{r} is the rank of \var{a}. 792 the sum over components \var{axis_offset} and \var{axis_offset+1} with the same index. For instance in the 793 case of a \RankTwo function and this is 794 \begin{equation} 795 \code{transpose(a)}\left[i,j\right]=\var{a} \left[j,i\right] 796 \end{equation} 797 and for a \RankFour function and \code{axis_offset=1} this is 798 \begin{equation} 799 \code{transpose(a,1)}\left[i,j,k,l\right]=\var{a} \left[j,k,l,i\right] 800 \end{equation} 801 \end{funcdesc} 802 803 \begin{funcdesc}{swap_axes}{a\optional{, axis0=0 \optional{, axis1=1 }}} 804 returns \var{a} but with swapped components \var{axis0} and \var{axis1}. The argument \var{a} must be 805 at least of \RankTwo. For instance in the 806 for a \RankFour argument, \code{axis0=1} and \code{axis1=2} this is 807 \begin{equation} 808 \code{swap_axes(a,1,2)}\left[i,j,k,l\right]=\var{a} \left[i,k,j,l\right] 809 \end{equation} 810 \end{funcdesc} 811 812 \begin{funcdesc}{symmetric}{a} 813 returns the symmetric part of \var{a}. This is \code{(a+transpose(a))/2}. 814 \end{funcdesc} 815 \begin{funcdesc}{nonsymmetric}{a} 816 returns the non--symmetric part of \var{a}. This is \code{(a-transpose(a))/2}. 817 \end{funcdesc} 818 \begin{funcdesc}{inverse}{a} 819 return the inverse of \var{a}. This is 820 \begin{equation} 821 \code{matrix_mult(inverse(a),a)=kronecker(d)} 822 \end{equation} 823 if \var{a} has shape \code{(d,d)}. The current implementation is restricted to arguments of shape 824 \code{(2,2)} and \code{(3,3)}. 825 \end{funcdesc} 826 \begin{funcdesc}{eigenvalues}{a} 827 return the eigenvalues of \var{a}. This is 828 \begin{equation} 829 \code{matrix_mult(a,V)=e[i]*V} 830 \end{equation} 831 where \code{e=eigenvalues(a)} and \var{V} is suitable non--zero vector \var{V}. 832 The eigenvalues are ordered in increasing size. 833 The argument \var{a} has to be the symmetric, ie. \code{a=symmetric(a)}. 834 The current implementation is restricted to arguments of shape 835 \code{(2,2)} and \code{(3,3)}. 836 \end{funcdesc} 837 \begin{funcdesc}{eigenvalues_and_eigenvectors}{a} 838 return the eigenvalues and eigenvectors of \var{a}. This is 839 \begin{equation} 840 \code{matrix_mult(a,V[:,i])=e[i]*V[:,i]} 841 \end{equation} 842 where \code{e,V=eigenvalues_and_eigenvectors(a)}. The eigenvectors \var{V} are orthogonal and normalized, ie. 843 \begin{equation} 844 \code{matrix_mult(transpose(V),V)=kronecker(d)} 845 \end{equation} 846 if \var{a} has shape \code{(d,d)}. The eigenvalues are ordered in increasing size. 847 The argument \var{a} has to be the symmetric, ie. \code{a=symmetric(a)}. 848 The current implementation is restricted to arguments of shape 849 \code{(2,2)} and \code{(3,3)}. 850 \end{funcdesc} 851 \begin{funcdesc}{maximum}{*a} 852 returns the maximum value over all arguments at all \DataSamplePoints and for each component. 853 For instance 854 \begin{equation} 855 \code{maximum(a0,a1)}\left[i,j\right]=max(\var{a0} \left[i,j\right],\var{a1} \left[i,j\right]) 856 \end{equation} 857 at all \DataSamplePoints. 858 \end{funcdesc} 859 \begin{funcdesc}{minimum}{*a} 860 returns the minimum value over all arguments at all \DataSamplePoints and for each component. 861 For instance 862 \begin{equation} 863 \code{minimum(a0,a1)}\left[i,j\right]=min(\var{a0} \left[i,j\right],\var{a1} \left[i,j\right]) 864 \end{equation} 865 at all \DataSamplePoints. 866 \end{funcdesc} 867 868 \begin{funcdesc}{clip}{a\optional{, minval=0.}\optional{, maxval=1.}} 869 cuts back \var{a} into the range between \var{minval} and \var{maxval}. A value in the returned object equals 870 \var{minval} if the corresponding value of \var{a} is less than \var{minval}, equals \var{maxval} if the 871 corresponding value of \var{a} is greater than \var{maxval} 872 or corresponding value of \var{a} otherwise. 873 \end{funcdesc} 874 \begin{funcdesc}{inner}{a0,a1} 875 returns the inner product of \var{a0} and \var{a1}. For instance in the 876 case of \RankTwo arguments and this is 877 \begin{equation} 878 \code{inner(a)}=\sum\hackscore{ij}\var{a0} \left[j,i\right] \cdot \var{a1} \left[j,i\right] 879 \end{equation} 880 and for a \RankFour arguments this is 881 \begin{equation} 882 \code{inner(a)}=\sum\hackscore{ijkl}\var{a0} \left[i,j,k,l\right] \cdot \var{a1} \left[j,i,k,l\right] 883 \end{equation} 884 \end{funcdesc} 885 886 \begin{funcdesc}{matrix_mult}{a0,a1} 887 returns the matrix product of \var{a0} and \var{a1}. If \var{a1} is \RankOne this is 888 \begin{equation} 889 \code{matrix_mult(a)}\left[i\right]=\sum\hackscore{k}\var{a0} \cdot \left[i,k\right]\var{a1} \left[k\right] 890 \end{equation} 891 and if \var{a1} is \RankTwo this is 892 \begin{equation} 893 \code{matrix_mult(a)}\left[i,j\right]=\sum\hackscore{k}\var{a0} \cdot \left[i,k\right]\var{a1} \left[k,j\right] 894 \end{equation} 895 \end{funcdesc} 896 897 \begin{funcdesc}{transposed_matrix_mult}{a0,a1} 898 returns the matrix product of the transposed of \var{a0} and \var{a1}. The function is equivalent to 899 \code{matrix_mult(transpose(a0),a1)}. 900 If \var{a1} is \RankOne this is 901 \begin{equation} 902 \code{transposed_matrix_mult(a)}\left[i\right]=\sum\hackscore{k}\var{a0} \cdot \left[k,i\right]\var{a1} \left[k\right] 903 \end{equation} 904 and if \var{a1} is \RankTwo this is 905 \begin{equation} 906 \code{transposed_matrix_mult(a)}\left[i,j\right]=\sum\hackscore{k}\var{a0} \cdot \left[k,i\right]\var{a1} \left[k,j\right] 907 \end{equation} 908 \end{funcdesc} 909 910 \begin{funcdesc}{matrix_transposed_mult}{a0,a1} 911 returns the matrix product of \var{a0} and the transposed of \var{a1}. 912 The function is equivalent to 913 \code{matrix_mult(a0,transpose(a1))}. 914 If \var{a1} is \RankTwo this is 915 \begin{equation} 916 \code{matrix_transposed_mult(a)}\left[i,j\right]=\sum\hackscore{k}\var{a0} \cdot \left[i,k\right]\var{a1} \left[j,k\right] 917 \end{equation} 918 \end{funcdesc} 919 920 \begin{funcdesc}{outer}{a0,a1} 921 returns the outer product of \var{a0} and \var{a1}. For instance if \var{a0} and \var{a1} both are \RankOne then 922 \begin{equation} 923 \code{outer(a)}\left[i,j\right]=\var{a0} \left[i\right] \cdot \var{a1}\left[j\right] 924 \end{equation} 925 and if \var{a0} is \RankOne and \var{a1} is \RankThree 926 \begin{equation} 927 \code{outer(a)}\left[i,j,k\right]=\var{a0} \left[i\right] \cdot \var{a1}\left[j,k\right] 928 \end{equation} 929 \end{funcdesc} 930 931 \begin{funcdesc}{tensor_mult}{a0,a1} 932 returns the tensor product of \var{a0} and \var{a1}. If \var{a1} is \RankTwo this is 933 \begin{equation} 934 \code{tensor_mult(a)}\left[i,j\right]=\sum\hackscore{kl}\var{a0}\left[i,j,k,l\right] \cdot \var{a1} \left[k,l\right] 935 \end{equation} 936 and if \var{a1} is \RankFour this is 937 \begin{equation} 938 \code{tensor_mult(a)}\left[i,j,k,l\right]=\sum\hackscore{mn}\var{a0} \left[i,j,m,n\right] \cdot \var{a1} \left[m,n,k,l\right] 939 \end{equation} 940 \end{funcdesc} 941 942 \begin{funcdesc}{transposed_tensor_mult}{a0,a1} 943 returns the tensor product of the transposed of \var{a0} and \var{a1}. The function is equivalent to 944 \code{tensor_mult(transpose(a0),a1)}. 945 If \var{a1} is \RankTwo this is 946 \begin{equation} 947 \code{transposed_tensor_mult(a)}\left[i,j\right]=\sum\hackscore{kl}\var{a0}\left[k,l,i,j\right] \cdot \var{a1} \left[k,l\right] 948 \end{equation} 949 and if \var{a1} is \RankFour this is 950 \begin{equation} 951 \code{transposed_tensor_mult(a)}\left[i,j,k,l\right]=\sum\hackscore{mn}\var{a0} \left[m,n,i,j\right] \cdot \var{a1} \left[m,n,k,l\right] 952 \end{equation} 953 \end{funcdesc} 954 955 \begin{funcdesc}{tensor_transposed_mult}{a0,a1} 956 returns the tensor product of \var{a0} and the transposed of \var{a1}. 957 The function is equivalent to 958 \code{tensor_mult(a0,transpose(a1))}. 959 If \var{a1} is \RankTwo this is 960 \begin{equation} 961 \code{tensor_transposed_mult(a)}\left[i,j\right]=\sum\hackscore{kl}\var{a0}\left[i,j,k,l\right] \cdot \var{a1} \left[l,k\right] 962 \end{equation} 963 and if \var{a1} is \RankFour this is 964 \begin{equation} 965 \code{tensor_transposed_mult(a)}\left[i,j,k,l\right]=\sum\hackscore{mn}\var{a0} \left[i,j,m,n\right] \cdot \var{a1} \left[k,l,m,n\right] 966 \end{equation} 967 \end{funcdesc} 968 969 \begin{funcdesc}{grad}{a\optional{, where=None}} 970 returns the gradient of \var{a}. If \var{where} is present the gradient will be calculated in \FunctionSpace \var{where} otherwise a 971 default \FunctionSpace is used. In case that \var{a} has \RankTwo one has 972 \begin{equation} 973 \code{grad(a)}\left[i,j,k\right]=\frac{\partial \var{a} \left[i,j\right]}{\partial x\hackscore{k}} 974 \end{equation} 975 \end{funcdesc} 976 \begin{funcdesc}{integrate}{a\optional{ ,where=None}} 977 returns the integral of \var{a} where the domain of integration is defined by the \FunctionSpace of \var{a}. If \var{where} is 978 present the argument is interpolated into \FunctionSpace \var{where} before integration. For instance in the case of 979 a \RankTwo argument in \ContinuousFunction it is 980 \begin{equation} 981 \code{integrate(a)}\left[i,j\right]=\int\hackscore{\Omega}\var{a} \left[i,j\right] \; d\Omega 982 \end{equation} 983 where $\Omega$ is the spatial domain and $d\Omega$ volume integration. To integrate over the boundary of the domain one uses 984 \begin{equation} 985 \code{integrate(a,where=FunctionOnBoundary(a.getDomain))}\left[i,j\right]=\int\hackscore{\partial \Omega} a\left[i,j\right] \; ds 986 \end{equation} 987 where $\partial \Omega$ is the surface of the spatial domain and $ds$ area or line integration. 988 \end{funcdesc} 989 \begin{funcdesc}{interpolate}{a,where} 990 interpolates argument \var{a} into the \FunctionSpace \var{where}. 991 \end{funcdesc} 992 \begin{funcdesc}{div}{a\optional{ ,where=None}} 993 returns the divergence of \var{a}. This 994 \begin{equation} 995 \code{div(a)}=trace(grad(a),where) 996 \end{equation} 997 \end{funcdesc} 998 \begin{funcdesc}{jump}{a\optional{ ,domain=None}} 999 returns the jump of \var{a} over the discontinuity in its domain or if \Domain \var{domain} is present 1000 in \var{domain}. 1001 \begin{equation} 1002 \begin{array}{rcl} 1003 \code{jump(a)}& = &\code{interpolate(a,FunctionOnContactOne(domain))} \\ 1004 & & \hfill - \code{interpolate(a,FunctionOnContactZero(domain))} 1005 \end{array} 1006 \end{equation} 1007 \end{funcdesc} 1008 \begin{funcdesc}{L2}{a} 1009 returns the $L^2$-norm of \var{a} in its function space. This is 1010 \begin{equation} 1011 \code{L2(a)=integrate(length(a)}^2\code{)} \; . 1012 \end{equation} 1013 \end{funcdesc} 1014 1015 The following functions operate point-wise''. That is, the operation is applied to each component of each point 1016 individually. 1017 1018 \begin{funcdesc}{sin}{a} 1019 applies sine function to \var{a}. 1020 \end{funcdesc} 1021 1022 \begin{funcdesc}{cos}{a} 1023 applies cosine function to \var{a}. 1024 \end{funcdesc} 1025 1026 \begin{funcdesc}{tan}{a} 1027 applies tangent function to \var{a}. 1028 \end{funcdesc} 1029 1030 \begin{funcdesc}{asin}{a} 1031 applies arc (inverse) sine function to \var{a}. 1032 \end{funcdesc} 1033 1034 \begin{funcdesc}{acos}{a} 1035 applies arc (inverse) cosine function to \var{a}. 1036 \end{funcdesc} 1037 1038 \begin{funcdesc}{atan}{a} 1039 applies arc (inverse) tangent function to \var{a}. 1040 \end{funcdesc} 1041 1042 \begin{funcdesc}{sinh}{a} 1043 applies hyperbolic sine function to \var{a}. 1044 \end{funcdesc} 1045 1046 \begin{funcdesc}{cosh}{a} 1047 applies hyperbolic cosine function to \var{a}. 1048 \end{funcdesc} 1049 1050 \begin{funcdesc}{tanh}{a} 1051 applies hyperbolic tangent function to \var{a}. 1052 \end{funcdesc} 1053 1054 \begin{funcdesc}{asinh}{a} 1055 applies arc (inverse) hyperbolic sine function to \var{a}. 1056 \end{funcdesc} 1057 1058 \begin{funcdesc}{acosh}{a} 1059 applies arc (inverse) hyperbolic cosine function to \var{a}. 1060 \end{funcdesc} 1061 1062 \begin{funcdesc}{atanh}{a} 1063 applies arc (inverse) hyperbolic tangent function to \var{a}. 1064 \end{funcdesc} 1065 1066 \begin{funcdesc}{exp}{a} 1067 applies exponential function to \var{a}. 1068 \end{funcdesc} 1069 1070 \begin{funcdesc}{sqrt}{a} 1071 applies square root function to \var{a}. 1072 \end{funcdesc} 1073 1074 \begin{funcdesc}{log}{a} 1075 applies the natural logarithm to \var{a}. 1076 \end{funcdesc} 1077 1078 \begin{funcdesc}{log10}{a} 1079 applies the base-$10$ logarithm to \var{a}. 1080 \end{funcdesc} 1081 1082 \begin{funcdesc}{sign}{a} 1083 applies the sign function to \var{a}, that is $1$ where \var{a} is positive, 1084 $-1$ where \var{a} is negative and $0$ otherwise. 1085 \end{funcdesc} 1086 1087 \begin{funcdesc}{wherePositive}{a} 1088 returns a function which is $1$ where \var{a} is positive and $0$ otherwise. 1089 \end{funcdesc} 1090 1091 \begin{funcdesc}{whereNegative}{a} 1092 returns a function which is $1$ where \var{a} is negative and $0$ otherwise. 1093 \end{funcdesc} 1094 1095 \begin{funcdesc}{whereNonNegative}{a} 1096 returns a function which is $1$ where \var{a} is non--negative and $0$ otherwise. 1097 \end{funcdesc} 1098 1099 \begin{funcdesc}{whereNonPositive}{a} 1100 returns a function which is $1$ where \var{a} is non--positive and $0$ otherwise. 1101 \end{funcdesc} 1102 1103 \begin{funcdesc}{whereZero}{a\optional{, tol=None, \optional{, rtol=1.e-8}}} 1104 returns a function which is $1$ where \var{a} equals zero with tolerance \var{tol} and $0$ otherwise. If \var{tol} is not present, the absolute maximum value of C{a} times C{rtol} is used. 1105 \end{funcdesc} 1106 1107 \begin{funcdesc}{whereNonZero}{a, \optional{, tol=None, \optional{, rtol=1.e-8}}} 1108 returns a function which is $1$ where \var{a} different from zero with tolerance \var{tol} and $0$ otherwise. If \var{tol} is not present, the absolute maximum value of C{a} times C{rtol} is used. 1109 \end{funcdesc} 1110 1111 \subsection{Interpolating Data} 1112 \index{interpolateTable} 1113 In some cases, it may be useful to produce Data objects which fit some user defined function. 1114 Manually modifying each value in the Data object is not a good idea since it depends on 1115 knowing the location and order of each datapoint in the domain. 1116 Instead \escript can use an interpolation table to produce a Data object. 1117 1118 The following example is available as int_save.py in the examples directory. 1119 We will produce a \Data object which aproximates a sine curve. 1120 1121 \begin{python} 1122 from esys.escript import saveDataCSV, sup 1123 import numpy 1124 from esys.finley import Rectangle 1125 1126 n=4 1127 r=Rectangle(n,n) 1128 x=r.getX() 1129 x0=x[0] 1130 x1=x[1] #we'll use this later 1131 toobig=100 1132 \end{python} 1133 1134 First we produce an interpolation table. 1135 \begin{python} 1136 sine_table=[0, 0.70710678118654746, 1, 0.70710678118654746, 0, 1137 -0.70710678118654746, -1, -0.70710678118654746, 0, 1138 0.70710678118654746] 1139 \end{python} 1140 1141 We wish to identify $0$ and $1$ with the ends of the curve. 1142 That is, with the first and eighth values in the table. 1143 But \var{x0} contains values between $0$ and $1$ inclusive so we need to continue the table to the ninth entry. 1144 1145 \begin{python} 1146 numslices=len(sine_table)-1 1147 1148 minval=0 1149 maxval=1.125 # The extra 0.25 is to account for the extra entry in the table 1150 1151 step=sup(maxval-minval)/numslices 1152 \end{python} 1153 1154 So the values $v$ from the input lie in the interval minval$\leq v <$maxval. 1155 \var{step} represents the gap (in the input range) between entries in the table. 1156 Now we produce our new \Data object. 1157 1158 \begin{python} 1159 result=x0.interpolateTable(sine_table, minval, step, toobig) 1160 \end{python} 1161 Any values which interpolate to larger than \var{toobig} will raise an exception. 1162 1163 Now for a 2D example. 1164 We will interpolate a surface such that the bottom edge is the sine curve described above. 1165 The amplitude of the curve decreases as we move towards the top edge. 1166 1167 Our interpolation table will have three rows. 1168 \begin{python} 1169 st=numpy.array(sine_table) 1170 1171 table=[st, 0.5*st, 0*st ] 1172 \end{python} 1173 1174 The use of numpy and multiplication here is just to save typing. 1175 1176 \begin{python} 1177 result2=x1.interpolateTable(table, 0, 0.55, x0, minval, step, toobig) 1178 \end{python} 1179 1180 In the 2D case, the params for the x1 direction (min=0, step=0.55) come first followed by the x0 data object and 1181 its params. 1182 In this case, the upper bound in the x1 direction is $1.10$ not $1$ so we don't need to add an extra row to the table. 1183 1184 \subsection{Saving Data as CSV} 1185 \index{saveDataCSV} 1186 \index{CSV} 1187 For simple post-processing, \Data objects can be saved in comma separated value format. 1188 1189 If \var{mydata1} and \var{mydata2} are scalar data, the following command: 1190 \begin{python} 1191 saveDataCSV('output.csv',U=mydata1, V=mydata2) 1192 \end{python} 1193 will record the values of mydata in \texttt{output.csv} in the following format: 1194 \begin{verbatim} 1195 U, V 1196 1.0000000e+0, 2.0000000e-1 1197 5.0000000e-0, 1.0000000e+1 1198 ... 1199 \end{verbatim} 1200 1201 The names of the keyword parameters form the names of columns in the ouput. 1202 If the data objects are over different function spaces, then saveDataCSV will attempt to 1203 interpolate to a common function space. 1204 If this is not possible, then an exception will be raised. 1205 1206 Output can be restricted using a scalar mask. 1207 \begin{python} 1208 saveDataCSV('outfile.csv', U=mydata1, V=mydata2, mask=myscalar) 1209 \end{python} 1210 Will only output those rows which correspond to to positive values of \var{myscalar}. 1211 Some aspects of the output can be tuned using additional params. 1212 \begin{python} 1213 saveDataCSV('data.csv', append=True, sep=' ', csep='/', mask=mymask, e=mat1) 1214 \end{python} 1215 1216 \begin{itemize} 1217 \item \var{append} - specifies that the output should be written to the end of an existing file. 1218 \item \var{sep} - defines the separator between fields. 1219 \item \var{csep} - defines the separator between components in the header line. For example between the components of a matrix. 1220 \end{itemize} 1221 1222 The above command would produce output like this: 1223 \begin{verbatim} 1224 e/0/0 e/1/0 e/0/1 e/1/1 1225 1.0000000000e+00 2.0000000000e+00 3.0000000000e+00 4.0000000000e+00 1226 ... 1227 \end{verbatim} 1228 1229 1230 1231 \subsection{\Operator Class} 1232 The \Operator class provides an abstract access to operators build 1233 within the \LinearPDE class. \Operator objects are created 1234 when a PDE is handed over to a PDE solver library and handled 1235 by the \LinearPDE object defining the PDE. The user can gain access 1236 to the \Operator of a \LinearPDE object through the \var{getOperator} 1237 method. 1238 1239 \begin{classdesc}{Operator}{} 1240 creates an empty \Operator object. 1241 \end{classdesc} 1242 1243 \begin{methoddesc}[Operator]{isEmpty}{fileName} 1244 returns \True is the object is empty. Otherwise \True is returned. 1245 \end{methoddesc} 1246 1247 \begin{methoddesc}[Operator]{setValue}{value} 1248 resets all entries in the object representation to \var{value} 1249 \end{methoddesc} 1250 1251 \begin{methoddesc}[Operator]{solves}{rhs} 1252 solves the operator equation with right hand side \var{rhs} 1253 \end{methoddesc} 1254 1255 \begin{methoddesc}[Operator]{of}{u} 1256 applies the operator to the \Data object \var{u} 1257 \end{methoddesc} 1258 1259 \begin{methoddesc}[Operator]{saveMM}{fileName} 1260 saves the object to a matrix market format file of name 1261 \var{fileName}, see 1262 \url{http://maths.nist.gov/MatrixMarket} 1263 % \ulink{maths.nist.gov/MatrixMarket}{\url{http://maths.nist.gov/MatrixMarket}}. 1264 \index{Matrix Market} 1265 \end{methoddesc} 1266 1267 \section{Physical Units} 1268 \escript provides support for physical units in the SI system \index{SI units} including unit conversion. So the 1269 user can define variables in the form 1270 \begin{python} 1271 from esys.escript.unitsSI import * 1272 l=20*m 1273 w=30*kg 1274 w2=40*lb 1275 T=100*Celsius 1276 \end{python} 1277 In the two latter cases an conversion from pounds\index{pounds} and degree Celsius\index{Celsius} is performed into the appropriate SI units kg and Kelvin is performed. In addition 1278 composed units can be used, for instance 1279 \begin{python} 1280 from esys.escript.unitsSI import * 1281 rho=40*lb/cm**3 1282 \end{python} 1283 to define the density in the units of pounds per cubic centimeter. The value $40$ will be converted 1284 into SI units, in this case kg per cubic meter. 1285 Moreover unit prefixes are supported: 1286 \begin{python} 1287 from esys.escript.unitsSI import * 1288 p=40*Mega*Pa 1289 \end{python} 1290 to the the pressure to 40 Mega Pascal. Units can also be converted back from the SI system into 1291 a desired unit, e.g 1292 \begin{python} 1293 from esys.escript.unitsSI import * 1294 print p/atm 1295 \end{python} 1296 can be used print the pressure in units of atmosphere\index{atmosphere}. 1297 1298 This is an incomplete list of supported physical units: 1299 1300 \begin{datadesc}{km} 1301 unit of kilo meter 1302 \end{datadesc} 1303 1304 \begin{datadesc}{m} 1305 unit of meter 1306 \end{datadesc} 1307 1308 \begin{datadesc}{cm} 1309 unit of centi meter 1310 \end{datadesc} 1311 1312 \begin{datadesc}{mm} 1313 unit of milli meter 1314 \end{datadesc} 1315 1316 \begin{datadesc}{sec} 1317 unit of second 1318 \end{datadesc} 1319 1320 \begin{datadesc}{minute} 1321 unit of minute 1322 \end{datadesc} 1323 1324 \begin{datadesc}{h} 1325 unit of hour 1326 \end{datadesc} 1327 \begin{datadesc}{day} 1328 unit of day 1329 \end{datadesc} 1330 \begin{datadesc}{yr} 1331 unit of year 1332 \end{datadesc} 1333 1334 \begin{datadesc}{gram} 1335 unit of gram 1336 \end{datadesc} 1337 \begin{datadesc}{kg} 1338 unit of kilo gram 1339 \end{datadesc} 1340 \begin{datadesc}{lb} 1341 unit of pound 1342 \end{datadesc} 1343 \begin{datadesc}{ton} 1344 metric ton 1345 \end{datadesc} 1346 1347 \begin{datadesc}{A} 1348 unit of Ampere 1349 \end{datadesc} 1350 1351 \begin{datadesc}{Hz} 1352 unit of Hertz 1353 \end{datadesc} 1354 1355 \begin{datadesc}{N} 1356 unit of Newton 1357 \end{datadesc} 1358 \begin{datadesc}{Pa} 1359 unit of Pascal 1360 \end{datadesc} 1361 \begin{datadesc}{atm} 1362 unit of atmosphere 1363 \end{datadesc} 1364 \begin{datadesc}{J} 1365 unit of Joule 1366 \end{datadesc} 1367 1368 \begin{datadesc}{W} 1369 unit of Watt 1370 \end{datadesc} 1371 1372 \begin{datadesc}{C} 1373 unit of Coulomb 1374 \end{datadesc} 1375 \begin{datadesc}{V} 1376 unit of Volt 1377 \end{datadesc} 1378 \begin{datadesc}{F} 1379 unit of Farad 1380 \end{datadesc} 1381 1382 \begin{datadesc}{Ohm} 1383 unit of Ohm 1384 \end{datadesc} 1385 \begin{datadesc}{K} 1386 unit of Kelvin 1387 \end{datadesc} 1388 \begin{datadesc}{Celsius} 1389 unit of Celsius 1390 \end{datadesc} 1391 1392 \begin{datadesc}{Fahrenheit} 1393 unit of Fahrenheit 1394 \end{datadesc} 1395 1396 Moreover unit prefixes are supported: 1397 1398 \begin{datadesc}{Yotta} 1399 prefix yotta = $10^{24}$. 1400 1401 \end{datadesc} 1402 1403 \begin{datadesc}{Zetta} 1404 prefix zetta= $10^{21}$. 1405 \end{datadesc} 1406 1407 \begin{datadesc}{Exa} 1408 prefix exa= $10^{18}$. 1409 \end{datadesc} 1410 1411 \begin{datadesc}{Peta} 1412 prefix peta= $10^{15}$. 1413 \end{datadesc} 1414 1415 \begin{datadesc}{Tera} 1416 prefix tera= $10^{12}$. 1417 \end{datadesc} 1418 1419 \begin{datadesc}{Giga} 1420 prefix giga= $10^9$. 1421 \end{datadesc} 1422 1423 \begin{datadesc}{Mega} 1424 prefix mega= $10^6$. 1425 \end{datadesc} 1426 1427 \begin{datadesc}{Kilo} 1428 prefix kilo= $10^3$. 1429 \end{datadesc} 1430 1431 \begin{datadesc}{Hecto} 1432 prefix hecto= $10^2$. 1433 \end{datadesc} 1434 1435 \begin{datadesc}{Deca} 1436 prefix deca= $10^1$. 1437 \end{datadesc} 1438 1439 \begin{datadesc}{Deci} 1440 prefix deci= $10^{-1}$. 1441 \end{datadesc} 1442 1443 \begin{datadesc}{Centi} 1444 prefix centi= $10^{-2}$. 1445 \end{datadesc} 1446 1447 \begin{datadesc}{Milli} 1448 prefix milli= $10^{-3}$. 1449 \end{datadesc} 1450 1451 \begin{datadesc}{Micro} 1452 prefix micro= $10^{-6}$. 1453 \end{datadesc} 1454 1455 \begin{datadesc}{Nano} 1456 prefix nano= $10^{-9}$. 1457 \end{datadesc} 1458 1459 \begin{datadesc}{Pico} 1460 prefix pico= $10^{-12}$. 1461 \end{datadesc} 1462 1463 \begin{datadesc}{Femto} 1464 prefix femto= $10^{-15}$. 1465 \end{datadesc} 1466 1467 \begin{datadesc}{Atto} 1468 prefix atto= $10^{-18}$. 1469 \end{datadesc} 1470 1471 \begin{datadesc}{Zepto} 1472 prefix zepto= $10^{-21}$. 1473 \end{datadesc} 1474 1475 \begin{datadesc}{Yocto} 1476 prefix yocto= $10^{-24}$. 1477 \end{datadesc} 1478 1479 1480 \section{Utilities} 1481 1482 The \class{FileWriter} provides a mechanism to write data to a file. 1483 In essence, this class wraps the standard \class{file} class to write data 1484 that are global in MPI to a file. In fact, data are written on the processor 1485 with \MPI rank 0 only. It is recommended to use \class{FileWriter} 1486 rather than \class{open} in order to write code that is running 1487 with and without \MPI. It is save to use \class{open} under MPI to read data which are global under \MPI. 1488 1489 \begin{classdesc}{FileWriter}{fn\optional{,append=\False, \optional{createLocalFiles=\False}})} 1490 Opens a file of name \var{fn} for writing. If \var{append} is set to \True 1491 written data are append at the end of the file. 1492 If running under \MPI only the first processor with rank==0 1493 will open the file and write to it. 1494 If \var{createLocalFiles} is set each individual processor will create a file 1495 where for any processor with rank>0 the file name is extended by its rank. This option is normally used for debug purposes only. 1496 \end{classdesc} 1497 1498 The following methods are available: 1499 \begin{methoddesc}[FileWriter]{close}{} 1500 closes the file. 1501 \end{methoddesc} 1502 \begin{methoddesc}[FileWriter]{flush}{} 1503 flushes the internal buffer to disk. 1504 \end{methoddesc} 1505 \begin{methoddesc}[FileWriter]{write}{txt} 1506 Write string \var{txt} to file. 1507 Note that newline is not added. 1508 \end{methoddesc} 1509 \begin{methoddesc}[FileWriter]{writelines}{txts} 1510 Write the list \var{txts} of strings to the file.. 1511 Note that newlines are not added. 1512 This method is equivalent to call write() for each string. 1513 \end{methoddesc} 1514 \begin{memberdesc}[FileWriter]{closed} 1515 \True if file is closed. 1516 \end{memberdesc} 1517 \begin{memberdesc}[FileWriter]{mode} 1518 access mode. 1519 \end{memberdesc} 1520 \begin{memberdesc}[FileWriter]{name} 1521 file name. 1522 \end{memberdesc} 1523 \begin{memberdesc}[FileWriter]{newlines} 1524 line separator 1525 \end{memberdesc} 1526 1527 1528 \begin{funcdesc}{setEscriptParamInt}{name,value} 1529 assigns the integer value \var{value} to the parameter \var{name}. 1530 If \var{name}="TOO_MANY_LINES" conversion of any \Data object to a string switches to a 1531 condensed format if more than \var{value} lines would be created. 1532 \end{funcdesc} 1533 1534 \begin{funcdesc}{getEscriptParamInt}{name} 1535 returns the current value of integer parameter \var{name}. 1536 \end{funcdesc} 1537 1538 \begin{funcdesc}{listEscriptParams}{a} 1539 returns a list of valid parameters and their description. 1540 \end{funcdesc} 1541 1542 \begin{funcdesc}{getMPISizeWorld}{} 1543 returns the number of \MPI processors in use in the \env{MPI_COMM_WORLD} processor group. 1544 If \MPI is not used 1 is returned. 1545 \end{funcdesc} 1546 \begin{funcdesc}{getMPIRankWorld}{} 1547 returns the rank of the process within the \env{MPI_COMM_WORLD} processor group. 1548 If \MPI is not used 0 is returned. 1549 \end{funcdesc} 1550 \begin{funcdesc}{MPIBarrierWorld}{} 1551 performs a barrier synchronization across all processors within \env{MPI_COMM_WORLD} 1552 processor group. 1553 \end{funcdesc} 1554 \begin{funcdesc}{getMPIWorldMax}{a} 1555 returns the maximum value of the integer \var{a} across all 1556 processors within \env{MPI_COMM_WORLD}. 1557 \end{funcdesc} 1558

## Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision