Fipy-3 1 3
Fipy-3 1 3
Fipy-3 1 3
𝜋
FiPy Manual
Release 3.1.3
Jonathan E. Guyer
Daniel Wheeler
James A. Warren
Certain commercial firms and trade names are identified in this document in order to specify the installation and usage
procedures adequately. Such identification is not intended to imply recommendation or endorsement by the National
Institute of Standards and Technology, nor is it intended to imply that related products are necessarily the best available
for the purpose.
Contents
I Introduction 1
1 Overview 3
1.1 Even if you don’t read manuals... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 What’s new in version 3.1.3? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3 Download and Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.4 Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5 Conventions and Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2 Installation 7
2.1 Recommended Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 Installing Python Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.3 Obtaining FiPy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.4 Installing FiPy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.5 Required Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.6 Optional Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.7 Level Set Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.8 Platform-Specific Instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.9 Development Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3 Solvers 13
3.1 PySparse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2 SciPy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.3 PyAMG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.4 Trilinos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4 Viewers 17
4.1 Matplotlib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4.2 Mayavi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
5 Using FiPy 19
5.1 Testing FiPy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
5.2 Command-line Flags and Environment Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.3 Solving in Parallel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
5.4 Meshing with Gmsh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.5 Coupled and Vector Equations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.6 Boundary Conditions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
5.7 Running under Python 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
5.8 Manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
i
6.1 General Conservation Equation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
6.2 Finite Volume Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
6.3 Discretization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
6.4 Linear Equations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
6.5 Numerical Schemes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
9 Glossary 49
II Examples 51
10 Diffusion Examples 55
10.1 examples.diffusion.mesh1D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
10.2 examples.diffusion.coupled . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
10.3 examples.diffusion.mesh20x20 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
10.4 examples.diffusion.circle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
10.5 examples.diffusion.electrostatics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
10.6 examples.diffusion.nthOrder.input4thOrder1D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
10.7 examples.diffusion.anisotropy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
11 Convection Examples 89
11.1 examples.convection.exponential1D.mesh1D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
11.2 examples.convection.exponential1DSource.mesh1D . . . . . . . . . . . . . . . . . . . . . . . . . . 90
11.3 examples.convection.robin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
11.4 examples.convection.source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
ii
13.2 examples.levelSet.distanceFunction.circle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
13.3 examples.levelSet.advection.mesh1D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
13.4 examples.levelSet.advection.circle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
13.5 Superconformal Electrodeposition Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
13.6 examples.levelSet.electroChem.simpleTrenchSystem . . . . . . . . . . . . . . . . . . . . . . . . . . 145
13.7 examples.levelSet.electroChem.gold . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
13.8 examples.levelSet.electroChem.leveler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
13.9 examples.levelSet.electroChem.howToWriteAScript . . . . . . . . . . . . . . . . . . . . . . . . . . 153
iii
21.5 fipy.meshes.cylindricalGrid2D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
21.6 fipy.meshes.cylindricalNonUniformGrid1D module . . . . . . . . . . . . . . . . . . . . . . . . . . 207
21.7 fipy.meshes.cylindricalNonUniformGrid2D module . . . . . . . . . . . . . . . . . . . . . . . . . . 208
21.8 fipy.meshes.cylindricalUniformGrid1D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
21.9 fipy.meshes.cylindricalUniformGrid2D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
21.10 fipy.meshes.factoryMeshes module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
21.11 fipy.meshes.gmshMesh module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
21.12 fipy.meshes.grid1D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
21.13 fipy.meshes.grid2D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
21.14 fipy.meshes.grid3D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
21.15 fipy.meshes.mesh module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
21.16 fipy.meshes.mesh1D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
21.17 fipy.meshes.mesh2D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
21.18 fipy.meshes.nonUniformGrid1D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
21.19 fipy.meshes.nonUniformGrid2D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
21.20 fipy.meshes.nonUniformGrid3D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
21.21 fipy.meshes.periodicGrid1D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
21.22 fipy.meshes.periodicGrid2D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
21.23 fipy.meshes.skewedGrid2D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
21.24 fipy.meshes.test module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
21.25 fipy.meshes.tri2D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
21.26 fipy.meshes.uniformGrid module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
21.27 fipy.meshes.uniformGrid1D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
21.28 fipy.meshes.uniformGrid2D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
21.29 fipy.meshes.uniformGrid3D module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
21.30 Module contents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
iv
24.12 fipy.terms.diffusionTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
24.13 fipy.terms.diffusionTermCorrection module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
24.14 fipy.terms.diffusionTermNoCorrection module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
24.15 fipy.terms.explicitDiffusionTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
24.16 fipy.terms.explicitSourceTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
24.17 fipy.terms.explicitUpwindConvectionTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
24.18 fipy.terms.exponentialConvectionTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
24.19 fipy.terms.faceTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
24.20 fipy.terms.firstOrderAdvectionTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
24.21 fipy.terms.hybridConvectionTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
24.22 fipy.terms.implicitDiffusionTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
24.23 fipy.terms.implicitSourceTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
24.24 fipy.terms.nonDiffusionTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
24.25 fipy.terms.powerLawConvectionTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
24.26 fipy.terms.residualTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
24.27 fipy.terms.sourceTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
24.28 fipy.terms.term module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
24.29 fipy.terms.test module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
24.30 fipy.terms.transientTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
24.31 fipy.terms.unaryTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
24.32 fipy.terms.upwindConvectionTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
24.33 fipy.terms.vanLeerConvectionTerm module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
24.34 Module contents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
v
27.6 fipy.variables.cellToFaceVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
27.7 fipy.variables.cellVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
27.8 fipy.variables.cellVolumeAverageVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
27.9 fipy.variables.constant module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
27.10 fipy.variables.constraintMask module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
27.11 fipy.variables.coupledCellVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
27.12 fipy.variables.distanceVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
27.13 fipy.variables.exponentialNoiseVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
27.14 fipy.variables.faceGradContributionsVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . 340
27.15 fipy.variables.faceGradVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
27.16 fipy.variables.faceVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
27.17 fipy.variables.gammaNoiseVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
27.18 fipy.variables.gaussCellGradVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
27.19 fipy.variables.gaussianNoiseVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
27.20 fipy.variables.harmonicCellToFaceVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
27.21 fipy.variables.histogramVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
27.22 fipy.variables.interfaceAreaVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
27.23 fipy.variables.interfaceFlagVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
27.24 fipy.variables.leastSquaresCellGradVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . 345
27.25 fipy.variables.levelSetDiffusionVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
27.26 fipy.variables.meshVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
27.27 fipy.variables.minmodCellToFaceVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
27.28 fipy.variables.modCellGradVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
27.29 fipy.variables.modCellToFaceVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
27.30 fipy.variables.modFaceGradVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
27.31 fipy.variables.modPhysicalField module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
27.32 fipy.variables.modularVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
27.33 fipy.variables.noiseVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
27.34 fipy.variables.operatorVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
27.35 fipy.variables.scharfetterGummelFaceVariable module . . . . . . . . . . . . . . . . . . . . . . . . . 347
27.36 fipy.variables.surfactantConvectionVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
27.37 fipy.variables.surfactantVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
27.38 fipy.variables.test module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
27.39 fipy.variables.unaryOperatorVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
27.40 fipy.variables.uniformNoiseVariable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
27.41 fipy.variables.variable module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
27.42 Module contents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
Bibliography 423
Index 429
vi
Part I
Introduction
1
Chapter 1
Overview
FiPy is an object oriented, partial differential equation (PDE) solver, written in Python, based on a standard finite vol-
ume (FV) approach. The framework has been developed in the Materials Science and Engineering Division (MSED)
and Center for Theoretical and Computational Materials Science (CTCMS), in the Material Measurement Laboratory
(MML) at the National Institute of Standards and Technology (NIST).
The solution of coupled sets of PDEs is ubiquitous to the numerical simulation of science problems. Numerous PDE
solvers exist, using a variety of languages and numerical approaches. Many are proprietary, expensive and difficult
to customize. As a result, scientists spend considerable resources repeatedly developing limited tools for specific
problems. Our approach, combining the FV method and Python, provides a tool that is extensible, powerful and freely
available. A significant advantage to Python is the existing suite of tools for array calculations, sparse matrices and
data rendering.
The FiPy framework includes terms for transient diffusion, convection and standard sources, enabling the solution of
arbitrary combinations of coupled elliptic, hyperbolic and parabolic PDEs. Currently implemented models include
phase field [2] [3] [4] treatments of polycrystalline, dendritic, and electrochemical phase transformations, as well as
drug eluting stents [5], reactive wetting [6], photovoltaics [7] and a level set treatment of the electrodeposition process
[8].
The latest information about FiPy can be found at http://www.ctcms.nist.gov/fipy/.
...please read Installation, Using FiPy and Frequently Asked Questions, as well as
examples.diffusion.mesh1D.
3
FiPy Manual, Release 3.1.3
4 Chapter 1. Overview
FiPy Manual, Release 3.1.3
Warning: FiPy 3 brought unavoidable syntax changes from FiPy 2. Please see
examples.updating.update2_0to3_0 for guidance on the changes that you will need to make to
your FiPy 2.x scripts.
Please refer to Installation for details on download and installation. FiPy can be redistributed and/or modified freely,
provided that any derivative works bear some notice that they are derived from it, and any modified versions bear some
notice that they have been modified.
1.4 Support
You can communicate with the FiPy developers and with other users via our mailing list and we wel-
come you to use the issue tracker for bugs, support requests, feature requests and patch submissions
<https://github.com/usnistgov/fipy/issues>. We also monitor StackOverflow for questions tagged with “fipy”. We
welcome collaborative efforts on this project.
FiPy is a member of MatForge, a project of the Materials Digital Library Pathway. This National Science Foundation
funded service provides a “wiki” space for public contributions of code snippets, discussions, and tutorials and hosts
our testing framework.
In order to discuss FiPy with other users and with the developers, we encourage you to sign up for the mailing list by
sending a subscription email:
To: [email protected]
Subject: (optional)
Body: subscribe
Once you are subscribed, you can post messages to the list simply by addressing email to mailto:[email protected].
If you are new to mailing lists, you may want to read the following resource about asking effective questions:
http://www.catb.org/~esr/faqs/smart-questions.html
To get off the list follow the instructions above, but place unsubscribe in the text body.
Send help in the text body to learn other mailing list configurations you can change.
List Archive
https://www.mail-archive.com/[email protected]/
Copies of messages sent to [email protected] are stored at The Mail Archive.
(note: we have also historically sent copies to http://dir.gmane.org/gmane.comp.python.fipy, but the GMANE site now
appears to be defunct.)
FiPy is driven by Python script files than you can view or modify in any text editor. FiPy sessions are invoked from a
command-line shell, such as tcsh or bash.
Throughout, text to be typed at the keyboard will appear like this. Commands to be issued from an interactive
shell will appear:
$ like this
where you would enter the text (“like this”) following the shell prompt, denoted by “$”.
Text blocks of the form:
>>> a = 3 * 4
>>> a
12
>>> if a == 12:
... print "a is twelve"
...
a is twelve
are intended to indicate an interactive session in the Python interpreter. We will refer to these as “interactive sessions”
or as “doctest blocks”. The text “>>>” at the beginning of a line denotes the primary prompt, calling for input of a
Python command. The text “...” denotes the secondary prompt, which calls for input that continues from the line
above, when required by Python syntax. All remaining lines, which begin at the left margin, denote output from the
Python interpreter. In all cases, the prompt is supplied by the Python interpreter and should not be typed by you.
Warning: Python is sensitive to indentation and care should be taken to enter text exactly as it appears in the
examples.
When references are made to file system paths, it is assumed that the current working directory is the FiPy distribution
directory, refered to as the “base directory”, such that:
examples/diffusion/steadyState/mesh1D.py
Paths will always be rendered using POSIX conventions (path elements separated by “/”). Any references of the form:
examples.diffusion.steadyState.mesh1D
are in the Python module notation and correspond to the equivalent POSIX path given above.
We may at times use a
or a
6 Chapter 1. Overview
Chapter 2
Installation
The FiPy finite volume PDE solver relies on several third-party packages. It is best to obtain and install those first
before attempting to install FiPy. This document explains how to install FiPy, not how to use it. See Using FiPy for
details on how to use FiPy.
Note: It may be useful to set up a Development Environment before beginning the installation process.
Attention:
There are many ways (described further down) to obtain the software packages necessary to run FiPy,
but the most expedient way is with the conda package manager.
• install Miniconda on your computer
• run:
Note: This command creates a self-contained conda environment and then downloads and populates the
environment with the prerequisites for FiPy from the channels guyer and conda-forge at https://anaconda.org.
Note: On Linux and Mac OS X, you should have a pretty complete system to run and visualize FiPy
simulations. On Windows, there are fewer packages available via conda, particularly amongst the sparse
matrix Solvers, but the system still should be functional.
In general, it is best to use the following order of precedence when installing packages:
• Use the operating system package manager, if possible.
• Use the conda package management system, which handles both Python and non-Python packages and pro-
vides facilities for self-contained environments with different combinations of Python packages, libraries, and
7
FiPy Manual, Release 3.1.3
applications.
• Use the pip installs python (pip) tool to obtain software from the Python Package Index (PyPI) repository:
$ pip install package
Warning: pip takes care of dependencies that are themselves Python packages. It does not deal with
non-Python dependencies.
Many of the packages listed below have prebuilt installers for different platforms (particularly for Windows). These
installers can save considerable time and effort compared to configuring and building from source, although they
frequently comprise somewhat older versions of the respective code. Whether building from source or using a
prebuilt installer, please read and follow explicitly any instructions given in the respective packages’ README and
INSTALLATION files.
FiPy is freely available for download via Git or as a compressed archive from
<http://www.ctcms.nist.gov/fipy/download>. Please see documentation:GIT for instructions on obtaining FiPy
with Git.
Warning: Keep in mind that if you choose to download the compressed archive you will then need to preserve
your changes when upgrades to FiPy become available (upgrades via Git will handle this issue automatically).
Details of the Required Packages and links are given below and in platform-specific instructions, but for the courageous
and the impatient, FiPy can be up and running quickly by simply installing the following prerequisite packages on your
system:
• Python
• NumPy
• At least one of the Solvers
• At least one of the Viewers (FiPy‘s tests will run without a viewer, but you’ll want one for any practical work)
Other Optional Packages add greatly to FiPy‘s capabilities, but are not necessary for an initial installation or to simply
run the test suite.
It is not necessary to formally install FiPy, but if you wish to do so and you are confident that all of the requisite
packages have been installed properly, you can install it by typing:
$ pip install fipy
8 Chapter 2. Installation
FiPy Manual, Release 3.1.3
at the command line in the base FiPy directory. You can also install FiPy in “development mode” by typing:
$ python setup.py develop
which allows the source code to be altered in place and executed without issuing further installation commands.
Alternatively, you may choose not to formally install FiPy and to simply work within the base directory instead. In this
case or if you are making a non-standard install (without admin privileges), read about setting up your Development
Environment before beginning the installation process.
2.5.1 Python
http://www.python.org/
FiPy is written in the Python language and requires a Python installation to run. Python comes pre-installed on many
operating systems, which you can check by opening a terminal and typing python, e.g.:
$ python
Python 2.3 (#1, Sep 13 2003, 00:49:11)
...
Type "help", "copyright", "credits" or "license" for more information.
>>>
If necessary, you can download and install it for your platform <http://www.python.org/download>.
Note: FiPy requires at least version 2.4.x of Python. See the specialized instructions if you wish to RunUnderPython3.
Python along with many of FiPy‘s required and optional packages is available with one of the following distributions.
conda
http://conda.pydata.org
This package manager provides a wide array of both Python-based and general scientific packages. In addition to the
default packages, many other developers (including us) provide “channels” to distribute their own builds of a variety
of software.
In a given conda environment, you can install FiPy with:
$ conda install --channel guyer --channel conda-forge fipy
http://www.enthought.com/epd
This installer provides a very large number of useful scientific packages for Python, including NumPy, SciPy, Mat-
plotlib, Mayavi, and IPython, as well as a Python interpreter. Installers are available for Windows, Mac OS X and
RedHat Linux, Solaris, Ubuntu Linux, and OpenSuSE Linux.
Attention: PySparse and FiPy are not presently included in EPD, so you will need to separately install them
manually.
Python(x,y)
http://python-xy.github.io
Another comprehensive Python package installer for scientific applications, presently only available for Windows.
Attention: PySparse and FiPy are not presently included in python(x,y), so you will need to separately install
them manually.
2.5.2 NumPy
http://numpy.scipy.org
Obtain and install the NumPy package. FiPy requires at least version 1.0 of NumPy.
2.6.1 Gmsh
http://www.geuz.org/gmsh/
Gmsh is an application that allows the creation of irregular meshes.
2.6.2 SciPy
http://www.scipy.org/
SciPy provides a large collection of functions and tools that can be useful for running and analyzing FiPy simula-
tions. Significantly improved performance has been achieved with the judicious use of C language inlining (see the
Command-line Flags and Environment Variables section for more details), via the scipy.weave module.
To use the level set components of FiPy one of the following is required.
2.7.1 Scikit-fmm
http://packages.python.org/scikit-fmm/
Scikit-fmm is a python extension module which implements the fast marching method.
10 Chapter 2. Installation
FiPy Manual, Release 3.1.3
2.7.2 LSMLIB
http://ktchu.serendipityresearch.org/software/lsmlib/index.html
The Level Set Method Library (LSMLIB) provides support for the serial and parallel simulation of implicit surface
and curve dynamics in two- and three-dimensions.
Install LSMLIB as per the instructions on the website. Additionally PyLSMLIB is required. To install, follow the
instructions on the website, https://github.com/ktchu/LSMLIB/tree/master/pylsmlib#pylsmlib.
FiPy is tested regularly on Mac OS X, Debian Linux, Ubuntu Linux, and Windows XP. We welcome reports of
compatibility with other systems, particularly if any additional steps are necessary to install (see Miscellaneous Build
Recipes for user contributed installation tips).
The only elements of FiPy that are likely to be platform-dependent are the Viewers but at least one viewer should work
on each platform. All other aspects should function on any platform that has a recent Python installation.
There is no official package manager for Mac OS X, but there are several third-party package managers that provide
many, but not all of FiPy‘s Required Packages and Optional Packages. Options include
Fink is based on the Debian package management system. It installs all of its dependencies into /sw.
MacPorts is a package manager originally part of OpenDarwin. It installs all of its dependencies into
/opt.
Homebrew is a recent, lightweight package manager based on Ruby scripts. It installs all of its depen-
dencies into /usr/local (although it can be directed not to).
In addition, there is an Enthought Python Distribution installer for Mac OS X.
Attention: PySparse and FiPy are not presently included in any of these package managers or installers, so you
will need to separately install them manually.
We presently find that the combination of Homebrew and pip is a pretty straightforward way to get most of FiPy‘s
prerequesites. See the Miscellaneous Build Recipes for up-to-date directions.
There is no official package manager for Windows, but the Enthought Python Distribution and Python(x,y) installers
provide most of FiPy‘s prerequisites.
Attention: PySparse and FiPy are not presently included in EPD or python(x,y), so you will need to separately
install them manually.
FiPy now has a .deb for Ubuntu/Debian systems that can be downloaded from
<http://www.ctcms.nist.gov/fipy/download>. Simply run:
to install. The .deb includes dependencies for all of the Required Packages and Optional Packages.
We often post miscellaneous installation instructions on the FiPy blog and wiki pages. The most useful of these
include:
• Installing FiPy on Mac OS X using Homebrew
• Building a 64-bit scientific python environment for FiPy from source
• Installing FiPy with pip
Note: We encourange you to contribute your own build recipes on the wiki if they are significantly different.
It is often preferable to not formally install packages in the system directories. The reasons for this include:
• developing or altering the package source code,
• trying out a new package along with its dependencies without violating a working system,
• dealing with conflicting packages and dependencies,
• or not having admin privileges.
The simplest way to use a Python package without installing it is to work in the base directory of the unpacked package
and set the PYTHONPATH environment variable to “.”. In order to work in an directory other than the package’s base
directory, the PYTHONPATH environment variable must be set to “~/path/to/package”. This method of working
is adequate for one package, but quickly becomes unmanageable with multiple Python packages. In order to manage
multiple packages, it is better to choose a standard location other than the default installation path.
If you do not have administrative privileges on your computer, or if for any reason you don’t want to tamper with your
existing Python installation, most packages (including FiPy) will allow you to install to an alternative location. Instead
of installing these packages with python setup.py install, you would use python setup.py install
--home=dir, where dir is the desired installation directory (usually “~” to indicate your home directory). You will
then need to append dir/lib/python to your PYTHONPATH environment variable. See the Alternate Installation
section of the Python document “Installing Python Modules” [1] for more information, such as circumstances in which
you should use --prefix instead of --home.
An alternative to setting the PYTHONPATH is to employ one of the utilities that manage packages and their depen-
dencies independently of the system package manager and the system directories. These utilities include Stow, Vir-
tualenv_ and zc.buildout, amongst others. Here we’ll describe the use of Virtualenv_, which we highly recommend.
12 Chapter 2. Installation
Chapter 3
Solvers
FiPy requires either PySparse, SciPy or Trilinos to be installed in order to solve linear systems. From our experiences,
FiPy runs most efficiently in serial when PySparse is the linear solver. Trilinos is the most complete of the three solvers
due to its numerous preconditioning and solver capabilities and it also allows FiPy to run in parallel. Although less
efficient than PySparse and less capable than Trilinos, SciPy is a very popular package, widely available and easy to
install. For this reason, SciPy may be the best linear solver choice when first installing and testing FiPy (and it is the
only viable solver under Python 3.x).
FiPy chooses the solver suite based on system availability or based on the user supplied Command-line Flags and
Environment Variables. For example, passing --no-pysparse:
$ python -c "from fipy import *; print DefaultSolver" --no-pysparse
<class 'fipy.solvers.trilinos.linearGMRESSolver.LinearGMRESSolver'>
uses a SciPy solver. Suite-specific solver classes can also be imported and instantiated overriding any other directives.
For example:
$ python -c "from fipy.solvers.scipy import DefaultSolver; \
> print DefaultSolver" --no-pysparse
<class 'fipy.solvers.scipy.linearLUSolver.LinearLUSolver'>
uses a SciPy solver regardless of the command line argument. In the absence of Command-line Flags and Environment
Variables, FiPy‘s order of precedence when choosing the solver suite for generic solvers is PySparse followed by
Trilinos, PyAMG and SciPy.
3.1 PySparse
http://pysparse.sourceforge.net
PySparse is a fast serial sparse matrix library for Python. It provides several sparse matrix storage formats and
conversion methods. It also implements a number of iterative solvers, preconditioners, and interfaces to efficient
factorization packages. The only requirement to install and use Pysparse is NumPy.
13
FiPy Manual, Release 3.1.3
3.2 SciPy
http://www.scipy.org/
The scipy.sparse module provides a basic set of serial Krylov solvers, but no preconditoners.
3.3 PyAMG
http://code.google.com/p/pyamg/
The PyAMG package provides adaptive multigrid preconditioners that can be used in conjunction with the SciPy
solvers.
3.4 Trilinos
http://trilinos.sandia.gov
Trilinos provides a more complete set of solvers and preconditioners than either PySparse or SciPy. Trilinos precon-
ditioning allows for iterative solutions to some difficult problems that PySparse and SciPy cannot solve, and it enables
parallel execution of FiPy (see Solving in Parallel for more details).
Attention: FiPy runs more efficiently when PySparse is installed alongside Trilinos.
Attention: Trilinos is a large software suite with its own set of prerequisites, and can be difficult to set up. It is
not necessary for most problems, and is not recommended for a basic install of FiPy.
Trilinos requires cmake, NumPy, and swig. The following are the minimal steps to build and install Trilinos (with
PyTrilinos) for FiPy:
$ cd trilinos-X.Y/
$ SOURCE_DIR=`pwd`
$ mkdir BUILD_DIR
$ cd BUILD_DIR
$ cmake \
> -D CMAKE_BUILD_TYPE:STRING=RELEASE \
> -D Trilinos_ENABLE_PyTrilinos:BOOL=ON \
> -D BUILD_SHARED_LIBS:BOOL=ON \
> -D Trilinos_ENABLE_ALL_OPTIONAL_PACKAGES:BOOL=ON \
> -D TPL_ENABLE_MPI:BOOL=ON \
> -D Trilinos_ENABLE_TESTS:BOOL=ON \
> -D DART_TESTING_TIMEOUT:STRING=600 \
> ${SOURCE_DIR}
$ make
$ make install
Depending on your platform, other options may be helpful or necessary; see the Trilinos user guide available from
http://trilinos.sandia.gov/documentation.html, or http://trilinos.sandia.gov/packages/pytrilinos/faq.html for more in-
depth documentation.
14 Chapter 3. Solvers
FiPy Manual, Release 3.1.3
3.4.1 mpi4py
http://mpi4py.scipy.org/
For Solving in Parallel, FiPy requires mpi4py, in addition to Trilinos.
3.4. Trilinos 15
FiPy Manual, Release 3.1.3
16 Chapter 3. Solvers
Chapter 4
Viewers
A viewer is required to see the results of FiPy calculations. Matplotlib is by far the most widely used Python based
viewer and the best choice to get FiPy up and running quickly. Matplotlib is also capable of publication quality plots.
Matplotlib has only rudimentary 3D capability, which FiPy does not attempt to use. Mayavi is required for 3D viewing.
4.1 Matplotlib
http://matplotlib.sourceforge.net
Matplotlib is a Python package that displays publication quality results. It displays both 1D X-Y type plots and
2D contour plots for both structured and unstructured data, but does not display 3D data. It works on all common
platforms.
4.2 Mayavi
http://code.enthought.com/projects/mayavi/
The Mayavi Data Visualizer is a free, easy to use scientific data visualizer. It displays 1D, 2D and 3D data. It is the
only FiPy viewer available for 3D data. Matplotlib is probably a better choice for 1D or 2D viewing.
Mayavi requires VTK, which can be difficult to build from source. Mayavi and VTK can be most easily obtained
through
• the Ubuntu or Debian package managers
• the Enthought Python Edition
• python(x,y)
• the Homebrew package manager for Mac OS X (VTK only, not Mayavi)
17
FiPy Manual, Release 3.1.3
18 Chapter 4. Viewers
Chapter 5
Using FiPy
This document explains how to use FiPy in a practical sense. To see the problems that FiPy is capable of solving, you
can run any of the scripts in the examples.
Note: We strongly recommend you proceed through the examples, but at the very least work through
examples.diffusion.mesh1D to understand the notation and basic concepts of FiPy.
We exclusively use either the unix command line or IPython to interact with FiPy. The commands in the examples
are written with the assumption that they will be executed from the command line. For instance, from within the main
FiPy directory, you can type:
$ python examples/diffusion/mesh1D.py
A viewer should appear and you should be prompted through a series of examples.
In order to customize the examples, or to develop your own scripts, some knowledge of Python syntax is required. We
recommend you familiarize yourself with the excellent Python tutorial [11] or with Dive Into Python [12].
As you gain experience, you may want to browse through the Command-line Flags and Environment Variables that
affect FiPy.
This command runs all the test cases in FiPy’s modules, but doesn’t include any of the tests in FiPy’s examples. To
run the test cases in both modules and examples, use:
$ python setup.py test
in an unpacked FiPy archive. The test suite can be run with a number of different configurations depending on which
solver suite is available and other factors. See Command-line Flags and Environment Variables for more details.
FiPy will skip tests that depend on Optional Packages that have not been installed. For example, if Mayavi and Gmsh
are not installed, FiPy will warn:
19
FiPy Manual, Release 3.1.3
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Skipped 131 doctest examples because `gmsh` cannot be found on the $PATH
Skipped 42 doctest examples because the `tvtk` package cannot be imported
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
If FiPy is configured for Solving in Parallel, you can run the tests on multiple processor cores with:
$ mpirun -np {# of processors} python setup.py test --trilinos
or:
$ mpirun -np {# of processors} python -c "import fipy; fipy.test('--trilinos')"
FiPy chooses a default run time configuration based on the available packages on the system. The Command-line
Flags and Environment Variables sections below describe how to override FiPy‘s default behavior.
You can add any of the following case-insensitive flags after the name of a script you call from the command line, e.g:
$ python myFiPyScript --someflag
--inline
Causes many mathematical operations to be performed in C, rather than Python, for improved performance.
Requires the scipy.weave package.
The following flags take precedence over the FIPY_SOLVERS environment variable:
--pysparse
Forces the use of the PySparse solvers.
--trilinos
Forces the use of the Trilinos solvers, but uses PySparse to construct the matrices.
--no-pysparse
Forces the use of the Trilinos solvers without any use of PySparse.
--scipy
Forces the use of the SciPy solvers.
--pyamg
Forces the use of the PyAMG preconditioners in conjunction with the SciPy solvers.
--lsmlib
Forces the use of the LSMLIB level set solver.
--skfmm
Forces the use of the Scikit-fmm level set solver.
You can set any of the following environment variables in the manner appropriate for your shell. If you are not
running in a shell (e.g., you are invoking FiPy scripts from within IPython or IDLE), you can set these variables via
the os.environ dictionary, but you must do so before importing anything from the fipy package.
FIPY_DISPLAY_MATRIX
If present, causes the graphical display of the solution matrix of each equation at each call of solve() or
sweep(). Setting the value to “terms,” causes the display of the matrix for each Term that composes the
equation. Requires the Matplotlib package.
FIPY_INLINE
If present, causes many mathematical operations to be performed in C, rather than Python. Requires the
scipy.weave package.
FIPY_INLINE_COMMENT
If present, causes the addition of a comment showing the Python context that produced a particular piece of
scipy.weave C code. Useful for debugging.
FIPY_SOLVERS
Forces the use of the specified suite of linear solvers. Valid (case-insensitive) choices are “pysparse”,
“trilinos”, “no-pysparse”, “scipy” and “pyamg”.
FIPY_VERBOSE_SOLVER
If present, causes the linear solvers to print a variety of diagnostic information.
FIPY_VIEWER
Forces the use of the specified viewer. Valid values are any <viewer> from the
fipy.viewers.<viewer>Viewer modules. The special value of dummy will allow the script to
run without displaying anything.
FIPY_INCLUDE_NUMERIX_ALL
If present, causes the inclusion of all funcions and variables of the numerix module in the fipy namespace.
FiPy can use Trilinos to solve equations in parallel. Most mesh classes in fipy.meshes can solve in parallel. This
includes all “...Grid...” and “...Gmsh...” class meshes. Currently, the only remaining serial-only meshes
are Tri2D and SkewedGrid2D.
Attention: FiPy requires mpi4py to work in parallel. See the mpi4py installation guide.
Note: Parallel efficiency is greatly improved by installing PySparse in addition to Trilinos. If PySparse is not installed
be sure to use the --no-pysparse flag when running in parallel.
It should not generally be necessary to change anything in your script. Simply invoke:
$ mpirun -np {# of processors} python myScript.py --trilinos
instead of:
$ python myScript.py
To confirm that FiPy and Trilinos are properly configured to solve in parallel, the easiest way to tell is to run one of
the examples, e.g.,:
$ mpirun -np 2 examples/diffusion/mesh1D.py
You should see two viewers open with half the simulation running in one of them and half in the other. If this does not
look right (e.g., you get two viewers, both showing the entire simultion), or if you just want to be sure, you can run a
diagnostic script:
$ mpirun -np 3 python examples/parallel.py
If there is a problem with your parallel environment, it should be clear that there is either a problem importing one of
the required packages or that there is some problem with the MPI environment. For example:
mpi4py: processor 2 of 3 :: PyTrilinos: processor 0 of 1 :: FiPy: 10 cells on processor 0 of 1
[my.machine.com:69815] WARNING: There were 4 Windows created but not freed.
mpi4py: processor 1 of 3 :: PyTrilinos: processor 0 of 1 :: FiPy: 10 cells on processor 0 of 1
[my.machine.com:69814] WARNING: There were 4 Windows created but not freed.
mpi4py: processor 0 of 3 :: PyTrilinos: processor 0 of 1 :: FiPy: 10 cells on processor 0 of 1
[my.machine.com:69813] WARNING: There were 4 Windows created but not freed.
indicates mpi4py is properly communicating with MPI and is running in parallel, but that Trilinos is not, and is running
three separate serial environments. As a result, FiPy is limited to three separate serial operations, too. In this instance,
the problem is that although Trilinos was compiled with MPI enabled, it was compiled against a different MPI library
than is currently available (and which mpi4py was compiled against). The solution is to rebuild Trilinos against the
active MPI libraries.
When solving in parallel, FiPy essentially breaks the problem up into separate sub-domains and solves them (some-
what) independently. FiPy generally “does the right thing”, but if you find that you need to do something with the
entire solution, you can use var.globalValue.
Note: Trilinos solvers frequently give intermediate output that FiPy cannot suppress. The most commonly encoun-
tered messages are
Gen_Prolongator warning : Max eigen <= 0.0 which is not significant to FiPy.
Aztec status AZ_loss: loss of precision which indicates that there was some diffi-
culty in solving the problem to the requested tolerance due to precision limitations, but usually does
not prevent the solver from finding an adequate solution.
Aztec status AZ_ill_cond: GMRES hessenberg ill-conditioned which indi-
cates that GMRES is having trouble with the problem, and may indicate that trying a different
solver or preconditioner may give more accurate results if GMRES fails.
FiPy works with arbitrary polygonal meshes generated by Gmsh. FiPy provides two wrappers classes (Gmsh2D and
Gmsh3D) enabling Gmsh to be used directly from python. The classes can be instantiated with a set of Gmsh style
commands (see examples.diffusion.circle). The classes can also be instantiated with the path to either a
Gmsh geometry file (.geo) or a Gmsh mesh file (.msh) (see examples.diffusion.anisotropy).
As well as meshing arbitrary geometries, Gmsh partitions meshes for parallel simulations. Mesh partitioning automat-
ically occurs whenever a parallel communicator is passed to the mesh on instantiation. This is the default setting for
all meshes that work in parallel including Gmsh2D and Gmsh3D.
Note: FiPy solution accuracy can be compromised with highly non-orthogonal or non-conjunctional meshes.
Equations can now be coupled together so that the contributions from all the equations appear in a single system
matrix. This results in tighter coupling for equations with spatial and temporal derivatives in more than one variable.
In FiPy equations are coupled together using the & operator:
>>> eqn0 = ...
>>> eqn1 = ...
>>> coupledEqn = eqn0 & eqn1
The coupledEqn will use a combined system matrix that includes four quadrants for each of the different variable
and equation combinations. In previous versions of FiPy there has been no need to specify which variable a given
term acts on when generating equations. The variable is simply specified when calling solve or sweep and this
functionality has been maintained in the case of single equations. However, for coupled equations the variable that a
given term operates on now needs to be specified when the equation is generated. The syntax for generating coupled
equations has the form:
>>> eqn0 = Term00(coeff=..., var=var0) + Term01(coeff..., var=var1) == source0
>>> eqn1 = Term10(coeff=..., var=var0) + Term11(coeff..., var=var1) == source1
>>> coupledEqn = eqn0 & eqn1
FiPy tries to make sensible decisions regarding each term’s location in the matrix and the ordering of the variable
column array. For example, if Term01 is a transient term then Term01 would appear in the upper left diagonal and
the ordering of the variable column array would be reversed.
The use of coupled equation is described in detail in examples.diffusion.coupled. Other ex-
amples that demonstrate the use of coupled equations are examples.phase.binaryCoupled,
To apply a fixed value boundary condition use the constrain() method. For example, to fix var to have a value of
2 along the upper surface of a domain, use
>>> var.constrain(2., where=mesh.facesTop)
To apply a fixed Gradient boundary condition use the faceGrad.constrain() method. For example, to fix var to
have a gradient of (0,2) along the upper surface of a 2D domain, use
>>> var.faceGrad.constrain(((0,),(2,)), where=mesh.facesTop)
Generally these can be implemented with a judicious use of faceGrad.constrain(). Failing that, an exterior
flux term can be added to the equation. Firstly, set the terms’ coefficients to be zero on the exterior faces,
>>> diffCoeff.constrain(0., mesh.exteriorFaces)
>>> convCoeff.constrain(0., mesh.exteriorFaces)
then create an equation with an extra term to account for the exterior flux,
>>> eqn = (TransientTerm() + ConvectionTerm(convCoeff)
... == DiffusionCoeff(diffCoeff)
... + (mesh.exteriorFaces * exteriorFlux).divergence)
Convection terms default to a no flux boundary condition unless the exterior faces are associated with a constraint, in
which case either an inlet or an outlet boundary condition is applied depending on the flow direction.
The use of spatial varying boundary conditions is best demonstrated with an example. Given a 2D equation in the
domain 0 < 𝑥 < 1 and 0 < 𝑦 < 1 with boundary conditions,
where 𝐹⃗ represents the flux. The boundary conditions in FiPy can be written with the following code,
>>> X, Y = mesh.faceCenters
>>> mask = ((X < 0.5) | (Y < 0.5))
>>> var.faceGrad.constrain(0, where=mesh.exteriorFaces & mask)
>>> var.constrain(X * Y, where=mesh.exteriorFaces & ~mask)
then
>>> eqn.solve(...)
Applying internal boundary conditions can be achieved through the use of implicit and explicit sources. An equation
of the form
>>> eqn = TransientTerm() == DiffusionTerm()
can be constrained to have a fixed internal value at a position given by mask with the following alterations
>>> eqn = TransientTerm() == DiffusionTerm() - ImplicitSourceTerm(mask * largeValue) + mask * largeVa
The parameter largeValue must be chosen to be large enough to completely dominate the matrix diagonal and the
RHS vector in cells that are masked. The mask variable would typically be a CellVariable boolean constructed
using the cell center values.
One must be careful to distinguish between constraining internal cell values during the solve step and simply applying
arbitrary constraints to a CellVariable. Applying a constraint,
>>> var.constrain(value, where=mask)
simply fixes the returned value of var at mask to be value. It does not have any effect on the implicit value of var
at the mask location during the linear solve so it is not a substitute for the source term machinations described above.
Future releases of FiPy may implicitly deal with this discrepancy, but the current release does not. A simple example
can be used to demonstrate this:
>>> m = Grid1D(nx=2, dx=1.)
>>> var = CellVariable(mesh=m)
Apply a constraint to the faces for a right side boundary condition (which works).
>>> var.constrain(1., where=m.facesRight)
Create the equation with the source term constraint described above
However, if a constraint is used without the source term constraint an unexpected value is obtained
>>> var.constrain(0.25, where=mask)
>>> eqn = DiffusionTerm()
>>> eqn.solve(var)
>>> print var
[ 0.25 1. ]
It is possible to run FiPy scripts under Python 3, but there is admittedly little advantage in doing so at this time. We
still develop and use FiPy under Python 2.x. To use, you must first convert FiPy‘s code to Python 3 syntax. From
within the main FiPy directory:
$ 2to3 --write .
$ 2to3 --write --doctests_only .
5.8 Manual
You can view the manual online at <http://www.ctcms.nist.gov/fipy> or you can download the latest manual from
<http://www.ctcms.nist.gov/fipy/download/>. Alternatively, it may be possible to build a fresh copy by issuing the
following command in the base directory:
$ python setup.py build_docs --pdf --html
Note: This mechanism is intended primarily for the developers. At a minimum, you will need at least version 1.1.2
of Sphinx, plus all of its prerequisites, although we build the documentation witih the latest development code (you
will need hg installed):
$ pip install --upgrade -e hg+https://bitbucket.org/birkenfeld/sphinx#egg=sphinx
$ hg clone https://bitbucket.org/birkenfeld/sphinx-contrib/
5.8. Manual 27
FiPy Manual, Release 3.1.3
This chapter describes the numerical methods used to solve equations in the FiPy programming environment. FiPy
uses the finite volume method (FVM) to solve coupled sets of partial differential equations (PDEs). For a good
introduction to the FVM see Nick Croft’s PhD thesis [13], Patankar [18] or Versteeg and Malalasekera [19].
Essentially, the FVM consists of dividing the solution domain into discrete finite volumes over which the state variables
are approximated with linear or higher order interpolations. The derivatives in each term of the equation are satisfied
with simple approximate interpolations in a process known as discretization. The (FVM) is a popular discretization
technique employed to solve coupled PDEs used in many application areas (e.g., Fluid Dynamics).
The FVM can be thought of as a subset of the Finite Element Method (FEM), just as the Finite Difference Method
(FDM) is a subset of the FVM. A system of equations fully equivalent to the FVM can be obtained with the FEM
using as weighting functions the characteristic functions of FV cells, i.e., functions equal to unity [20]. Analogously,
the discretization of equations with the FVM reduces to the FDM on Cartesian grids.
The equations that model the evolution of physical, chemical and biological systems often have a remarkably universal
form. Indeed, PDEs have proven necessary to model complex physical systems and processes that involve variations
in both space and time. In general, given a variable of interest 𝜑 such as species concentration, pH, or temperature,
there exists an evolution equation of the form
𝜕𝜑
= 𝐻(𝜑, 𝜆𝑖 ) (6.1)
𝜕𝑡
where 𝐻 is a function of 𝜑, other state variables 𝜆𝑖 , and higher order derivatives of all of these variables. Examples of
such systems are wide ranging, but include problems that exhibit a combination of diffusing and reacting species, as
well as such diverse problems as determination of the electric potential in heart tissue, of fluid flow, stress evolution,
and even the Schroedinger equation.
A general conservation equation, solved using FiPy, can include any combination of the following terms,
𝜕(𝜌𝜑) 𝑛
+ ∇ · (⃗𝑢𝜑) = [∇ · (Γ𝑖 ∇)] 𝜑 + 𝑆𝜑
𝜕𝑡
⏟ ⏞ ⏟ ⏞ ⏟ ⏞ ⏟ ⏞ (6.2)
transient convection diffusion source
where 𝜌, ⃗𝑢 and Γ𝑖 represent coefficients in the transient, convection and diffusion terms, respectively. These coeffi-
cients can be arbitrary functions of any parameters or variables in the system. The variable 𝜑 represents the unknown
quantity in the equation. The diffusion term can represent any higher order diffusion-like term, where the order is given
by the exponent 𝑛. For example, the diffusion term can represent conventional Fickean diffusion [i.e., ∇ · (Γ∇𝜑)]
when the exponent 𝑛 = 1 or a Cahn-Hilliard term [i.e., ∇ · (Γ1 ∇[∇ · Γ2 ∇𝜑)]) [14] [15] [16]] when 𝑛 = 2, or a phase
field crystal term [i.e., ∇ · (Γ1 ∇[∇ · Γ2 ∇{∇ · Γ3 ∇𝜑)})]) [17]] when 𝑛 = 3, although spectral methods are probably
a better approach. Higher order terms (𝑛 > 3) are also possible, but the matrix condition number becomes quite poor.
29
. . . . . . .. = ..
,
., . .
. .
ρφV − (ρφV )old
+
FiPy Manual, Release 3.1.3
[($n · $u)Aφ]face = . [ΓA$ . n · ∇φ]face φn+ V Sφ bn
∆t . a nn
face face
' ()
6.2 Finite*Volume ' Method () * ' () * '()*
transient convection ∂(ρφ) diffusion source
To use the FVM, the solution domain must first be divided into+ non-overlapping
∇ · ($uφ)polyhedral = ∇elements · (Γ∇φ) or cells. +
A Sφ
* In the'FVM() * ' () * '()*
solution domain divided in such a way is generally∂t known as a mesh (as we will see, a Mesh is also a FiPy object).
A mesh consists of vertices, faces and cells (see'Figure
()Mesh). the variables of interest are averaged over
convection
control volumes (CVs). The CVs are either defined by the cells or are centered on the vertices. diffusion source
transient
+ + + +
∂(ρφ)
fa∂t dV + ($n · $u)φ dS = Γ($n · ∇φ) dS +
ce vertex
'V () * ' S () * 'S () * '
cell
transient convection diffusion
Fig. 6.1: , ,
ρφV − (ρφV )old Mesh
In the VC-FVM, the CV is centered around the vertices and the cells are divided into sub-control volumes that make
up the main CVs (see Figure CV structure for an unstructured mesh). The vertices “store” the average variable values
over the CVs. The CV faces are constructed within the cells rather than using the cell faces as in the CC-FVM. The
face fluxes use all the vertex values from the cell where the face is located to calculate interpolations. For this reason,
the VC-FVM is less efficient and requires more storage (a larger matrix band width) than the CC-FVM. However, the
mesh topology does not have the same restrictions as the CC-FVM. FiPy does not have a VC-FVM capability.
6.3 Discretization
The first step in the discretization of Equation (??) using the CC-FVM is to integrate over a CV and then make
appropriate approximations for fluxes across the boundary of each CV. In this section, each term in Equation (??) will
be examined separately.
1
FiPy Manual, Release 3.1.3
Ω2
2
Ω3
Ω1
3
a
1
Ω4
(b)
2
Ωa
a
3
1
(a)
6.3. Discretization 31
FiPy Manual, Release 3.1.3
For the transient term, the discretization of the integral over the volume of a CV is given by
∫︀
𝑉
where 𝜑𝑃 represents the average value of 𝜑 in a CV centered on a point 𝑃 and the superscript “old” represents the
previous time-step value. The value 𝑉𝑃 is the volume of the CV and ∆𝑡 is the time step size.
This term is represented in FiPy as
>>> TransientTerm(coeff=rho)
where we have∫︀ used the divergence theorem to transform the integral over
∑︀ the CV volume 𝑉 into an integral over the
∫︀
CV surface 𝑆 . The summation over the faces of a CV is denoted by 𝑓 and 𝐴𝑓 is the area of each face. The vector
⃗𝑛 is the normal to the face pointing out of the CV into an adjacent CV centered on point 𝐴. When using a first order
approximation, the value of 𝜑𝑓 must depend on the average value in adjacent cell 𝜑𝐴 and the average value in the cell
of interest 𝜑𝑃 , such that
𝜑𝑓 = 𝛼𝑓 𝜑𝑃 + (1 − 𝛼𝑓 )𝜑𝐴 .
The weighting factor 𝛼𝑓 is determined by the convection scheme, described in Numerical Schemes.
This term is represented in FiPy as
>>> <SpecificConvectionTerm>(coeff=u)
{. . .} indicates recursive application of the specified operation on 𝜑, depending on the order of the diffusion term. The
estimation for the flux, (⃗𝑛 · ∇{. . .})𝑓 , is obtained via
{. . .}𝐴 − {. . .}𝑃
(⃗𝑛 · ∇{. . .})𝑓 ≃
𝑑𝐴𝑃
where the value of 𝑑𝐴𝑃 is the distance between neighboring cell centers. This estimate relies on the orthogonality of
the mesh, and becomes increasingly inaccurate as the non-orthogonality increases. Correction terms have been derived
to improve this error but are not currently included in FiPy [13].
This term is represented in FiPy as
>>> DiffusionTerm(coeff=Gamma1)
or
>>> ExplicitDiffusionTerm(coeff=Gamma1)
Higher order diffusion expressions, such as ∇4 𝜑 or ∇ · (Γ1 ∇ (∇ · (Γ2 ∇𝜑))) for Cahn-Hilliard are represented as
>>> DiffusionTerm(coeff=(Gamma1, Gamma2))
The number of elements supplied for coeff determines the order of the term.
Any term that cannot be written in one of the previous forms is considered a source 𝑆𝜑 . The discretization for the
source term is given by,
∫︁
𝑆𝜑 𝑑𝑉 ≃ 𝑆𝜑 𝑉𝑃 . (6.6)
𝑉
Including any negative dependence of 𝑆𝜑 on 𝜑 increases solution stability. The dependence can only be included in a
linear manner so Equation (6.6) becomes
𝑉𝑃 (𝑆0 + 𝑆1 𝜑𝑃 ),
where 𝑆0 is the source which is independent of 𝜑 and 𝑆1 is the coeficient of the source which is linearly dependent on
𝜑.
A source term is represented in FiPy essentially as it appears in mathematical form, e.g., 3𝜅2 +𝑏 sin 𝜃 would be written
>>> 3 * kappa**2 + b * numrix.sin(theta)
Note: Functions like sin() can be obtained from the fipy.tools.numerix module.
Warning: Generally, things will not work as expected if the equivalent function is used from the NumPy or SciPy
library.
6.3. Discretization 33
FiPy Manual, Release 3.1.3
If, however, the source depends on the variable that is being solved for, it can be advantageous to linearize the source
and cast part of it as an implicit source term, e.g., 3𝜅2 + 𝜑 sin 𝜃 might be written as
>>> 3 * kappa**2 + ImplicitSourceTerm(coeff=sin(theta))
The aim of the discretization is to reduce the continuous general equation to a set of discrete linear equations that can
then be solved to obtain the value of the dependent variable at each CV center. This results in a sparse linear system
that requires an efficient iterative scheme to solve. The iterative schemes available to FiPy are currently encapsulated
in the PySparse and PyTrilinos suites of solvers and include most common solvers such as the conjugate gradient
method and LU decomposition.
Combining Equations (6.3), (6.4), (6.5) and (6.6), the complete discretization for equation (??) can now be written for
each CV as
𝜌𝑃 (𝜑𝑃 − 𝜑old
𝑃 )𝑉𝑃
∑︁
+ (⃗𝑛 · ⃗𝑢)𝑓 𝐴𝑓 [𝛼𝑓 𝜑𝑃 + (1 − 𝛼𝑓 ) 𝜑𝐴 ]
∆𝑡
𝑓
∑︁ (𝜑𝐴 − 𝜑𝑃 )
= Γ 𝑓 𝐴𝑓 + 𝑉𝑃 (𝑆0 + 𝑆1 𝜑𝑃 ).
𝑑𝐴𝑃
𝑓
Equation (6.4) is now in the form of a set of linear combinations between each CV value and its neighboring values
and can be written in the form
∑︁
𝑎𝑃 𝜑𝑃 = 𝑎𝐴 𝜑𝐴 + 𝑏𝑃 , (6.7)
𝑓
where
𝜌𝑃 𝑉𝑃 ∑︁
𝑎𝑃 = + (𝑎𝐴 + 𝐹𝑓 ) − 𝑉𝑃 𝑆1 ,
∆𝑡
𝑓
𝑎𝐴 = 𝐷𝑓 − (1 − 𝛼𝑓 )𝐹𝑓 ,
𝜌𝑃 𝑉𝑃 𝜑old
𝑃
𝑏𝑃 = 𝑉𝑃 𝑆0 + .
∆𝑡
The face coefficients, 𝐹𝑓 and 𝐷𝑓 , represent the convective strength and diffusive conductance respectively, and are
given by
𝐹𝑓 = 𝐴𝑓 (⃗𝑢 · ⃗𝑛)𝑓 ,
𝐴𝑓 Γ 𝑓
𝐷𝑓 = .
𝑑𝐴𝑃
The coefficients of equation (??) must remain positive, since an increase in a neighboring value must result in an
increase in 𝜑𝑃 to obtain physically realistic solutions. Thus, the inequalities 𝑎𝐴 > 0 and 𝑎𝐴 + 𝐹𝑓 > 0 must be
satisfied. The Peclet number 𝑃𝑓 ≡ 𝐹𝑓 /𝐷𝑓 is the ratio between convective strength and diffusive conductance. To
achieve physically realistic solutions, the inequality
1 1
> 𝑃𝑓 > − (6.8)
1 − 𝛼𝑓 𝛼𝑓
must be satisfied. The parameter 𝛼𝑓 is defined by the chosen scheme, depending on Equation (6.8). The various
differencing schemes are:
the central differencing scheme, where
1
𝛼𝑓 = (6.9)
2
so that |𝑃𝑓 | < 2 satisfies Equation (6.8). Thus, the central differencing scheme is only numerically stable for a
low values of 𝑃𝑓 .
the upwind scheme, where
{︃
1 if 𝑃𝑓 > 0,
𝛼𝑓 = (6.10)
0 if 𝑃𝑓 < 0.
Equation (6.10) satisfies the inequality in Equation (6.8) for all values of 𝑃𝑓 . However the solution over predicts
the diffusive term leading to excessive numerical smearing (“false diffusion”).
the exponential scheme, where
This formulation can be derived from the exact solution, and thus, guarantees positive coefficients while not
over-predicting the diffusive terms. However, the computation of exponentials is slow and therefore a faster
scheme is generally used, especially in higher dimensions.
the hybrid scheme, where
⎧ 𝑃𝑓 −1
⎨ 𝑃𝑓
⎪ if 𝑃𝑓 > 2,
𝛼𝑓 = 1
if |𝑃𝑓 | < 2, (6.12)
⎪2
− 𝑃1𝑓 if 𝑃𝑓 < −2.
⎩
if 𝑃𝑓 < −10.
⎩ 1
− 𝑃𝑓
The power law scheme overcomes the inaccuracies of the hybrid scheme, while improving on the computational
time for the exponential scheme.
All of the numerical schemes presented here are available in FiPy and can be selected by the user.
The goal of FiPy is to provide a highly customizable, open source code for modeling problems involving coupled sets
of PDEs. FiPy allows users to select and customize modules from within the framework. FiPy has been developed to
address model problems in materials science such as poly-crystals, dendritic growth and electrochemical deposition.
These applications all contain various combinations of PDEs with differing forms in conjunction with other unusual
physics (over varying length scales) and unique solution procedures. The philosophy of FiPy is to enable customization
while providing a library of efficient modules for common objects and data types.
7.1 Design
The solution algorithms given in the FiPy examples involve combining sets of PDEs while tracking an interface
where the parameters of the problem change rapidly. The phase field method and the level set method are specialized
techniques to handle the solution of PDEs in conjunction with a deforming interface. FiPy contains several examples
of both methods.
FiPy uses the well-known Finite Volume Method (FVM) to reduce the model equations to a form tractable to linear
solvers.
FiPy is programmed in an object-oriented manner. The benefit of object oriented programming mainly lies in encap-
sulation and inheritance. Encapsulation refers to the tight integration between certain pieces of data and methods that
act on that data. Encapsulation allows parts of the code to be separated into clearly defined independent modules that
can be re-applied or extended in new ways. Inheritance allows code to be reused, overridden, and new capabilities
to be added without altering the original code. An object is treated by its users as an abstraction; the details of its
implementation and behavior are internal.
FiPy has been developed with a large number of test cases. These test cases are in two categories. The lower level tests
operate on the core modules at the individual method level. The aim is that every method within the core installation
has a test case. The high level test cases operate in conjunction with example solutions and serve to test global solution
algorithms and the interaction of various modules.
With this two-tiered battery of tests, at any stage in code development, the test cases can be executed and errors can
be identified. A comprehensive test base provides reassurance that any code breakages will be clearly demonstrated
with a broken test case. A test base also aids dissemination of the code by providing simple examples and knowledge
of whether the code is working on a particular computer environment.
37
FiPy Manual, Release 3.1.3
In recent years, there has been a movement to release software under open source and associated unrestrictied licenses,
especially within the scientific community. These licensing terms allow users to develop their own applications with
complete access to the source code and then either contribute back to the main source repository or freely distribute
their new adapted version.
As a product of the National Institute of Standards and Technology, the FiPy framework is placed in the public domain
as a matter of U. S. Federal law. Furthermore, FiPy is built upon existing open source tools. Others are free to use
FiPy as they see fit and we welcome contributions to make FiPy better.
Programming languages can be broadly lumped into two categories: compiled languages and interpreted (or scripting)
languages. Compiled languages are converted from a human-readable text source file to a machine-readable binary
application file by a sequence of operations generally referred to as “compiling” and “linking.” The binary application
can then be run as many times as desired, but changes will provoke a new cycle of compiling and linking. Interpreted
languages are converted from human-readable to machine-readable on the fly, each time the script is executed. Because
the conversion happens every time 1 , interpreted code is usually slower when running than compiled code. On the other
hand, code development and debugging tends to be much easier and fluid when it’s not necessary to wait for compile
and link cycles after every change. Furthermore, because the conversion happens in real time, it is possible to have
interactive sessions in a scripting language that are not generally possible in compiled languages.
Another distinction, somewhat orthogonal, but closely related, to that between compiled and interpreted languages,
is between low-level languages and high-level languages. Low-level languages describe actions in simple terms that
are closer to the way the computer actually functions. High-level languages describe actions in more complex and
abstract terms that are closer to the way the programmer thinks about the problem at hand. This increased complexity
in the meaning of an expression renders simpler code, because the details of the implementation are hidden away in
the language internals or in an external library. For example, a low-level matrix multiplication written in C might be
rendered as
if (Acols != Brows)
error "these matrix shapes cannot be multiplied";
Note that the dimensions of the arrays must be supplied externally, as C provides no intrinsic mechanism for deter-
mining the shape of an array. An equivalent high-level construction might be as simple as
C = A * B
All of the error checking, dimension measuring, and space allocation is handled automatically by low-level code that
is intrinsic to the high-level matrix multiplication operator. The high-level code “knows” that matrices are involved,
how to get their shapes, and to interpret ‘*‘ as a matrix multiplier instead of an arithmetic one. All of this allows the
programmer to think about the operation of interest and not worry about introducing bugs in low-level code that is not
unique to their application.
1 ... neglecting such common optimizations as byte-code interpreters.
Although it needn’t be true, for a variety of reasons, compiled languages tend to be low-level and interpreted languages
tend to be high-level. Because low-level languages operate closer to the intrinsic “machine language” of the computer,
they tend to be faster at running a given task than high-level languages, but programs written in them take longer to
write and debug. Because running performance is a paramount concern, most scientific codes are written in low-level
compiled languages like FORTRAN or C.
A rather common scenario in the development of scientific codes is that the first draft hard-codes all of the problem
parameters. After a few (hundred) iterations of recompiling and relinking the application to explore changes to the
parameters, code is added to read an input file containing a list of numbers. Eventually, the point is reached where it is
impossible to remember which parameter comes in which order or what physical units are required, so code is added
to, for example, interpret a line beginning with ‘#‘ as a comment. At this point, the scientist has begun developing a
scripting language without even knowing it. Unfortunately for them, very few scientists have actually studied computer
science or actually know anything about the design and implementation of script interpreters. Even if they have the
expertise, the time spent developing such a language interpreter is time not spent actually doing research.
In contrast, a number of very powerful scripting languages, such as Tcl, Java, Python, Ruby, and even the vener-
able BASIC, have open source interpreters that can be embedded directly in an application, giving scientific codes
immediate access to a high-level scripting language designed by someone who actually knew what they were doing.
We have chosen to go a step further and not just embed a full-fledged scripting language in the FiPy framework,
but instead to design the framework from the ground up in a scripting language. While runtime performance is
unquestionably important, many scientific codes are run relatively little, in proportion to the time spent developing
them. If a code can be developed in a day instead of a month, it may not matter if it takes another day to run instead of
an hour. Furthermore, there are a variety of mechanisms for diagnosing and optimizing those portions of a code that
are actually time-critical, rather than attempting to optimize all of it by using a language that is more palatable to the
computer than to the programmer. Thus FiPy, rather than taking the approach of writing the fast numerical code first
and then dealing with the issue of user interaction, initially implements most modules in high-level scripting language
and only translates to low-level compiled code those portions that prove inefficient.
Acknowledging that several scripting languages offer a number, if not all, of the features described above, we have
selected Python for the implementation of FiPy. Python is
• an interpreted language that combines remarkable power with very clear syntax,
• freely usable and distributable, even for commercial use,
• fully object oriented,
• distributed with powerful automated testing tools (doctest, unittest),
• actively used and extended by other scientists and mathemeticians (SciPy, NumPy, ScientificPython, PySparse).
• easily integrated with low-level languages such as C (weave, blitz, PyRex).
7.2 Implementation
The Python classes that make up FiPy are described in detail in fipy Package Documentation, but we give a brief
overview here. FiPy is based around three fundamental Python classes: Mesh, Variable, and Term. Using the
terminology of Theoretical and Numerical Background:
A Mesh object represents the domain of interest. FiPy contains many different specific mesh classes to describe
different geometries.
7.2. Implementation 39
FiPy Manual, Release 3.1.3
A Variable object represents a quantity or field that can change during the problem evolution. A particular type of
Variable, called a CellVariable, represents 𝜑 at the centers of the cells of the Mesh. A CellVariable
describes the values of the field 𝜑, but it is not concerned with their geometry; that role is taken by the Mesh.
An important property of Variable objects is that they can describe dependency relationships, such that:
>>> a = Variable(value = 3)
>>> b = a * 4
does not assign the value 12 to b, but rather it assigns a multiplication operator object to b, which depends on
the Variable object a:
>>> b
(Variable(value = 3) * 4)
>>> a.setValue(5)
>>> b
(Variable(value = 5) * 4)
The numerical value of the Variable is not calculated until it is needed (a process known as “lazy evaluation”):
>>> print b
20
A Term object represents any of the terms in Equation (??) or any linear combination of such terms. Early in the
development of FiPy, a distinction was made between Equation objects, which represented all of Equation
(??), and Term objects, which represented the individual terms in that equation. The Equation object has
since been eliminated as redundant. Term objects can be single entities such as a DiffusionTerm or a linear
combination of other Term objects that build up to form an expression such as Equation (??).
Beyond these three fundamental classes of Mesh, Variable, and Term, FiPy is composed of a number of related
classes.
Cell
Face
Vertex Solver
Term
BoundaryCondition
SparseMatrix
A Mesh object is composed of cells. Each cell is defined by its bounding faces and each face is defined by its
bounding vertices. A Term object encapsulates the contributions to the _SparseMatrix that defines the solution
of an equation. BoundaryCondition objects are used to describe the conditions on the boundaries of the Mesh,
and each Term interprets the BoundaryCondition objects as necessary to modify the _SparseMatrix. An
equation constructed from Term objects can apply a unique Solver to invert its _SparseMatrix in the most
expedient and stable fashion. At any point during the solution, a Viewer can be invoked to display the values of the
solved Variable objects.
At this point, it will be useful to examine some of the example problems in Examples. More classes are introduced in
the examples, along with illustrations of their instantiation and use.
As explained in Theoretical and Numerical Background, the canonical governing equation that can be solved by FiPy
for the dependent CellVariable 𝜑 is
𝜕(𝜌𝜑) 𝑛
+ ∇ · (⃗𝑢𝜑) = [∇ · (Γ𝑖 ∇)] 𝜑 + 𝑆𝜑
𝜕𝑡
⏟ ⏞ ⏟ ⏞ ⏟ ⏞ ⏟ ⏞
transient convection diffusion source
Terms of the form 𝜕𝑖 Γ𝑖𝑗 𝜕𝑗 𝜑 can be posed in FiPy by using a list, tuple rank 1 or rank 2 FaceVariable to represent
a vector or tensor diffusion coefficient. For example, if we wished to represent a diffusion term with an anisotropy
ratio of 5 aligned along the x-coordinate axis, we could write the term as,
>>> DiffusionTerm([[[5, 0], [0, 1]]])
which represents 5𝜕𝑥2 + 𝜕𝑦2 . Notice that the tensor, written in the form of a list, is contained within a list. This is
because the first index of the list refers to the order of the term not the first index of the tensor (see Higher order
diffusion). This notation, although succinct can sometimes be confusing so a number of cases are interpreted below.
>>> DiffusionTerm([[5, 1]])
This represents the same term as the case examined above. The vector notation is just a short-hand representation for
the diagonal of the tensor. Off-diagonals are assumed to be zero.
>>> DiffusionTerm([5, 1])
)︀2
This simply represents a fourth order isotropic diffusion term of the form 5 𝜕𝑥2 + 𝜕𝑦2 .
(︀
Nominally, this should represent a fourth order diffusion term of the form 𝜕𝑥2 𝜕𝑦2 , but FiPy does not currently support
anisotropy for higher order diffusion terms so this may well throw an error or give anomalous results.
41
FiPy Manual, Release 3.1.3
>>> x, y = mesh.cellCenters
>>> DiffusionTerm(CellVariable(mesh=mesh,
... value=[[x**2, x * y], [-x * y, -y**2]])
This represents an anisotropic diffusion coefficient that varies spatially so that the term has the form 𝜕𝑥 (𝑥2 𝜕𝑥 +𝑥𝑦𝜕𝑦 )+
𝜕𝑦 (−𝑥𝑦𝜕𝑥 − 𝑦 2 𝜕𝑦 ) ≡ 𝑥𝜕𝑥 − 𝑦𝜕𝑦 + 𝑥2 𝜕𝑥2 − 𝑦 2 𝜕𝑦2 .
Generally, anisotropy is not conveniently aligned along the coordinate axes; in these cases, it is necessary to apply
a rotation matrix in order to calculate the correct tensor values, see examples.diffusion.anisotropy for
details.
8.1.2 How do I represent a ... term that doesn’t involve the dependent variable?
It is important to realize that, even though an expression may superficially resemble one of those shown in Discretiza-
tion, if the dependent variable for that PDE does not appear in the appropriate place, then that term should be treated
as a source.
𝜕𝜑
= ∇ · (𝐷1 ∇𝜑) + ∇ · (𝐷2 ∇𝜉)
𝜕𝑡
then the first term is a TransientTerm and the second term is a DiffusionTerm, but the third term is simply an
explicit source, which is written in Python as
>>> (D2 * xi.faceGrad).divergence
Higher order diffusive sources can be obtained by simply nesting the references to faceGrad and divergence.
Note: We use faceGrad, rather than grad, in order to obtain a second-order spatial discretization of the diffusion
term in 𝜉, consistent with the matrix that is formed by DiffusionTerm for 𝜑.
𝜕𝜑
= ∇ · (⃗𝑢𝜉)
𝜕𝑡
can be rendered as
>>> (u * xi.arithmeticFaceValue).divergence
if ⃗𝑢 is a rank-1 CellVariable.
𝜕(𝜌1 𝜑) 𝜕(𝜌2 𝜉)
=
𝜕𝑡 𝜕𝑡
does not have an abstract form in FiPy and should be discretized directly, in the manner of Equation (??), as
>>> TransientTerm(coeff=rho1) == rho2 * (xi - xi.old) / timeStep
8.1.3 What if my term involves the dependent variable, but not where FiPy puts it?
Frequently, viewing the term from a different perspective will allow it to be cast in one of the canonical forms. For
example, the third term in
𝜕𝜑
= ∇ · (𝐷1 ∇𝜑) + ∇ · (𝐷2 𝜑∇𝜉)
𝜕𝑡
might be considered as the diffusion of the independent variable 𝜉 with a mobility 𝐷2 𝜑 that is a function of the
dependent variable 𝜑. For FiPy‘s purposes, however, this term represents the convection of 𝜑, with a velocity 𝐷2 ∇𝜉,
due to the counter-diffusion of 𝜉, so
>>> eq = TransientTerm() == (DiffusionTerm(coeff=D1)
... + <Specific>ConvectionTerm(coeff=D2 * xi.faceGrad))
Note: With the advent of Coupled and Vector Equations in FiPy 3.x, it is now possible to represent both terms with
DiffusionTerm.
8.1.4 What if the coefficient of a term depends on the variable that I’m solving for?
A non-linear coefficient, such as the diffusion coefficient in ∇ · [Γ1 (𝜑)∇𝜑] = ∇ · [Γ0 𝜑(1 − 𝜑)∇𝜑] is not a problem
for FiPy. Simply write it as it appears:
>>> diffTerm = DiffusionTerm(coeff=Gamma0 * phi * (1 - phi))
Note: Due to the nonlinearity of the coefficient, it will probably be necessary to “sweep” the solution to convergence
as discussed in Iterations, timesteps, and sweeps? Oh, my!.
The way to save your calculations depends on how you plan to make use of the data. If you want to save it
for “restart” (so that you can continue or redirect a calculation from some intermediate stage), then you’ll want
to “pickle” the Python data with the dump module. This is illustrated in examples.phase.anisotropy,
examples.phase.impingement.mesh40x1, examples.phase.impingement.mesh20x20, and
examples.levelSet.electroChem.howToWriteAScript.
On the other hand, pickled FiPy data is of little use to anything besides Python and FiPy. If you want to import your
calculations into another piece of software, whether to make publication-quality graphs or movies, or to perform some
analysis, or as input to another stage of a multiscale model, then you can save your data as an ASCII text file of
tab-separated-values with a TSVViewer. This is illustrated in examples.diffusion.circle.
Some of the viewers have a button or other mechanism in the user interface for saving an image file. Also, you can
supply an optional keyword filename when you tell the viewer to plot(), e.g.
>>> viewer.plot(filename="myimage.ext")
which will save a file named myimage.ext in your current working directory. The type of image is determined by
the file extension “.ext”. Different viewers have different capabilities:
Matplotlib accepts “.eps,” “.jpg” (Joint Photographic Experts Group), and “.png” (Portable Network Graphics).
Attention: Actually, Matplotlib supports different extensions, depending on the chosen backend, but our
MatplotlibViewer classes don’t properly support this yet.
To our knowledge, this is only supported by Matplotlib, as is explained in the Matplotlib FAQ on image backends.
Basically, you need to tell Matplotlib to use an “image backend,” such as “Agg” or “Cairo.” Backends are discussed
at http://matplotlib.sourceforge.net/backends.html.
FiPy has no facilities for making movies. You will need to save individual frames (see the previous question) and then
stitch them together into a movie, using one of a variety of different free, shareware, or commercial software packages.
The guidance in the Matplotlib FAQ on movies should be adaptable to other Viewers.
FiPy‘s viewers are utilitarian. They’re designed to let you see something with a minimum of effort. Because different
plotting packages have different capabilities and some are easier to install on some platforms than on others, we have
tried to support a range of Python plotters with a minimal common set of features. Many of these packages are capable
of much more, however. Often, you can invoke the Viewer you want, and then issue supplemental commands for
the underlying plotting package. The better option is to make a “subclass” of the FiPy Viewer that comes closest to
producing the image you want. You can then override just the behavior you wan to change, while letting FiPy do most
of the heavy lifting. See examples.phase.anisotropy and examples.phase.polyxtal for examples of
creating a custom Matplotlib Viewer class; see examples.cahnHilliard.sphere for an example of creating
a custom Mayavi Viewer class.
Any non-linear solution of partial differential equations is an approximation. These approximations benefit from
repetetive solution to achieve the best possible answer. In FiPy (and in many similar PDE solvers), there are three
layers of repetition.
iterations This is the lowest layer of repetition, which you’ll generally need to spend the least time thinking about.
FiPy solves PDEs by discretizing them into a set of linear equations in matrix form, as explained in Discretiza-
tion and Linear Equations. It is not always practical, or even possible, to exactly solve these matrix equations
on a computer. FiPy thus employs “iterative solvers”, which make successive approximations until the linear
equations have been satisfactorily solved. FiPy chooses a default number of iterations and solution tolerance,
which you will not generally need to change. If you do wish to change these defaults, you’ll need to create a
new Solver object with the desired number of iterations and solution tolerance, e.g.
>>> mySolver = LinearPCGSolver(iterations=1234, tolerance=5e-6)
:
:
>>> eq.solve(..., solver=mySolver, ...)
Note: The older Solver steps= keyword is now deprecated in favor of iterations= to make the role
clearer.
Note: Despite references to the “previous timestep,” sweeping is not limited to time-evolving problems. Non-
linear sets of quasi-static or steady-state PDEs can require sweeping, too.
We need to distinguish between the value of the variable at the last timestep and the value of the variable at the
last sweep (the last cycle where we tried to solve the current timestep). This is done by first modifying the way
the variable is created:
>>> myVar = CellVariable(..., hasOld=True)
and then by explicitly moving the current value of the variable into the “old” value only when we want to:
>>> myVar.updateOld()
Finally, we will need to repeatedly solve the equation until it gives a stable result. To clearly distinguish that a
single cycle will not truly “solve” the equation, we invoke a different method “sweep():
>>> for sweep in range(sweeps):
... eq.sweep(var=myVar, ...)
Even better than sweeping a fixed number of cycles is to do it until the non-linear PDE has been solved satisfac-
torily:
>>> while residual > desiredResidual:
... residual = eq.sweep(var=myVar, ...)
equilibrium or steady-state solution is desired, it may not be possible to simply solve that directly, due to non-
linear coupling between equations or to boundary conditions or initial conditions. Some types of PDEs have
fundamental limits to how large a timestep they can take before they become either unstable or inaccurate.
Note: Stability and accuracy are distinctly different. An unstable solution is often said to “blow up”, with
radically different values from point to point, often diverging to infinity. An inaccurate solution may look
perfectly reasonable, but will disagree significantly from an analytical solution or from a numerical solution
obtained by taking either smaller or larger timesteps.
For all of these reasons, you will frequently need to advance a problem in time and to choose an appropriate
interval between solutions. This can be simple:
>>> timeStep = 1.234e-5
>>> for step in range(steps):
... eq.solve(var=myVar, dt=timeStep, ...)
or more elaborate:
>>> timeStep = 1.234e-5
>>> elapsedTime = 0
>>> while elapsedTime < totalElapsedTime:
... eq.solve(var=myVar, dt=timeStep, ...)
... elapsedTime += timeStep
... timeStep = SomeFunctionOfVariablesAndTime(myVar1, myVar2, elapedTime)
A majority of the examples in this manual illustrate time evolving behavior. Notably, boundary conditions
are made a function of elapsed time in examples.diffusion.mesh1D. The timestep is chosen based on
the expected interfacial velocity in examples.phase.simple. The timestep is gradually increased as the
kinetics slow down in examples.cahnHilliard.mesh2DCoupled.
Finally, we can (and often do) combine all three layers of repetition:
>>> myVar = CellVariable(..., hasOld=1)
:
:
>>> mySolver = LinearPCGSolver(iterations=1234, tolerance=5e-6)
:
:
>>> while elapsedTime < totalElapsedTime:
... myVar.updateOld()
... while residual > desiredResidual:
... residual = eq.sweep(var=myVar, dt=timeStep, ...)
... elapsedTime += timeStep
FiPy solves field variables on the cell centers. Transient and source terms describe the change in the value of a field at
the cell center, and so they take a CellVariable coefficient. Diffusion and convection terms involve fluxes between
cell centers, and are calculated on the face between two cells, and so they take a FaceVariable coefficient.
Note: If you supply a CellVariable var when a FaceVariable is expected, FiPy will automatically substitute
var.arithmeticFaceValue. This can have undesirable consequences, however. For one thing, the arithmetic
face average of a non-linear function is not the same as the same non-linear function of the average argument, e.g., for
𝑓 (𝑥) = 𝑥2 ,
ValueError: frames are not aligned This error most likely means that you have provided a
CellVariable when FiPy was expecting a FaceVariable (or vice versa).
MA.MA.MAError: Cannot automatically convert masked array to Numeric because data is masked
This not-so-helpful error message could mean a number of things, but the most likely explanation is that the
solution has become unstable and is diverging to ±∞. This can be caused by taking too large a timestep or by
using explicit terms instead of implicit ones.
repairing catalog by removing key This message (not really an error, but may cause test failures) can
result when using the scipy.weave package via the --inline flag. It is due to a bug in SciPy that
has been patched in their source repository: http://www.scipy.org/mailinglists/mailman?fn=scipy-dev/2005-
June/003010.html.
numerix Numeric 23.6 This is neither an error nor a warning. It’s just a sloppy message left in SciPy:
http://thread.gmane.org/gmane.comp.python.scientific.user/4349.
FiPy tries to make reasonable choices, based on what packages it finds installed, but there may be times that you wish
to override these behaviors. See the Command-line Flags and Environment Variables section for more details.
FiPy has experienced three major API changes. The steps necessary to upgrade older scripts are discussed in Updating
FiPy.
Please post your question to the mailing list <http://www.ctcms.nist.gov/fipy/mail.html> or file an issue at
<https://github.com/usnistgov/fipy/issues/new>.
Buildbot The BuildBot is a system to automate the compile/test cycle required by most software projects to validate
code changes. See http://trac.buildbot.net/.
FiPy The eponymous software package. See http://www.ctcms.nist.gov/fipy.
Gmsh A free and Open Source 3D (and 2D!) finite element grid generator. It also has a CAD engine and post-
processor that FiPy does not make use of. See http://www.geuz.org/gmsh.
IPython An improved Python shell that integrates nicely with Matplotlib. See http://ipython.scipy.org/.
Matplotlib matplotlib Python package displays publication quality results. It displays both 1D X-Y type plots
and 2D contour plots for structured data. It does not display unstructured 2D data or 3D data. It works on
all common platforms and produces publication quality hard copies. See http://matplotlib.sourceforge.net and
Matplotlib.
Mayavi The mayavi Data Visualizer is a free, easy to use scientific data visualizer. It displays 1D, 2D and 3D data.
It is the only FiPy viewer available for 3D data. Other viewers are probably better for 1D or 2D viewing. See
http://code.enthought.com/projects/mayavi and Mayavi.
MayaVi The predecessor to Mayavi. Yes, it’s confusing.
numarray An archaic predecessor to NumPy.
Numeric An archaic predecessor to NumPy.
NumPy The numpy Python package provides array arithmetic facilities. See http://www.scipy.org/NumPy.
pip “pip installs python” is a tool for installing and managing Python packages, such as those found in PyPI. See
http://www.pip-installer.org.
PyAMG A suite of python-based preconditoners. See http://code.google.com/p/pyamg/ and PyAMG.
PyPI The Python Package Index is a repository of software for the Python programming language. See
http://pypi.python.org/pypi.
Pyrex A mechanism for mixing C and Python code. See http://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/.
PySparse The pysparse Python package provides sparse matrix storage, solvers, and linear algebra routines. See
http://pysparse.sourceforge.net and PySparse.
Python The programming language that FiPy (and your scripts) are written in. See http://www.python.org/.
Python 3 The (likely) future of the Python programming language. Third-party packages are slowly being adapted,
but many that FiPy uses are not yet available. See http://docs.python.org/py3k/ and PEP 3000.
PyTrilinos Python wrapper for Trilinos. See http://trilinos.sandia.gov/packages/pytrilinos/.
PyxViewer A now defunct python viewer.
ScientificPython A collection of useful utilities for scientists. See http://dirac.cnrs-
orleans.fr/plone/software/scientificpython.
49
FiPy Manual, Release 3.1.3
SciPy The scipy package provides a wide range of scientific and mathematical operations. FiPy can use
scipy.weave for enhanced performance with C language inlining and Scipy‘s solver suite for linear solu-
tions. See http://www.scipy.org/. and SciPy.
Sphinx The tools used to generate the FiPy documentation. See http://sphinx.pocoo.org/.
Trilinos This package provides sparse matrix storage, solvers, and preconditioners, and can be used instead of PyS-
parse. Trilinos preconditioning allows for iterative solutions to some difficult problems that PySparse cannot
solve. See http://trilinos.sandia.gov and Trilinos.
50 Chapter 9. Glossary
Part II
Examples
51
FiPy Manual, Release 3.1.3
• Each example can be invoked such that when it has finished running, you will be left in an interactive Python
shell:
$ python -i examples/something/input.py
At this point, you can enter Python commands to manipulate the model or to make queries about the example’s
variable values. For instance, the interactive Python sessions in the example documentation can be typed in
directly to see that the expected results are obtained.
• Alternatively, these interactive Python sessions, known as doctest blocks, can be invoked as automatic tests:
$ python setup.py test --examples
In this way, the documentation and the code are always certain to be consistent.
• Finally, and most importantly, the examples can be used as a templates to design your own problem scripts.
Note: The examples shown in this manual have been written with particular emphasis on serving as
both documentation and as comprehensive tests of the FiPy framework. As explained at the end of
examples/diffusion/steadyState/mesh1D.py, your own scripts can be much more succint, if you
wish, and include only the text that follows the “>>>” and “...” prompts shown in these examples.
To obtain a copy of an example, containing just the script instructions, type:
$ python setup.py copy_script --From x.py --To y.py
In addition to those presented in this manual, there are dozens of other files in the examples/ directory, that demon-
strate other uses of FiPy. If these examples do not help you construct your own problem scripts, please contact us.
53
FiPy Manual, Release 3.1.3
54
Chapter 10
Diffusion Examples
10.1 examples.diffusion.mesh1D
at the command line. Different stages of the example should be displayed, along with prompting messages in the
terminal.
This example takes the user through assembling a simple problem with FiPy. It describes different approaches to a 1D
diffusion problem with constant diffusivity and fixed value boundary conditions such that,
𝜕𝜑
= 𝐷∇2 𝜑. (10.1)
𝜕𝑡
The first step is to define a one dimensional domain with 50 solution points. The Grid1D object represents a linear
structured grid. The parameter dx refers to the grid spacing (set to unity here).
>>> from fipy import *
>>> nx = 50
>>> dx = 1.
>>> mesh = Grid1D(nx=nx, dx=dx)
FiPy solves all equations at the centers of the cells of the mesh. We thus need a CellVariable object to hold the
values of the solution, with the initial condition 𝜑 = 0 at 𝑡 = 0,
>>> phi = CellVariable(name="solution variable",
... mesh=mesh,
... value=0.)
We’ll let
55
FiPy Manual, Release 3.1.3
>>> D = 1.
for now.
The set of boundary conditions are given to the equation as a Python tuple or list (the distinction is not generally
important to FiPy). The boundary conditions
{︃
0 at 𝑥 = 1,
𝜑=
1 at 𝑥 = 0.
Note: Only faces around the exterior of the mesh can be used for boundary conditions.
For example, here the exterior faces on the left of the domain are extracted by mesh.facesLeft. The boundary
conditions is applied using phi. constrain() with tthese faces and a value (valueLeft).
>>> phi.constrain(valueRight, mesh.facesRight)
>>> phi.constrain(valueLeft, mesh.facesLeft)
Note: If no boundary conditions are specified on exterior faces, the default boundary condition is equivalent to a zero
gradient, equivalent to ⃗𝑛 · ∇𝜑|someFaces = 0.
If you have ever tried to numerically solve Eq. (10.1), you most likely attempted “explicit finite differencing” with
code something like:
for step in range(steps):
for j in range(cells):
phi_new[j] = phi_old[j] \
+ (D * dt / dx**2) * (phi_old[j+1] - 2 * phi_old[j] + phi_old[j-1])
time += dt
plus additional code for the boundary conditions. In FiPy, you would write
>>> eqX = TransientTerm() == ExplicitDiffusionTerm(coeff=D)
The largest stable timestep that can be taken for this explicit 1D diffusion problem is ∆𝑡 ≤ ∆𝑥2 /(2𝐷).
We limit our steps to 90% of that value for good measure
>>> timeStepDuration = 0.9 * dx**2 / (2 * D)
>>> steps = 100
If we’re running interactively, we’ll want to view the result, but not if this example is being run automatically as a
test. We accomplish this by having Python check if this script is the “__main__” script, which will only be true if
we explicitly launched it and not if it has been imported by another script such as the automatic tester. The factory
function Viewer() returns a suitable viewer depending on available viewers and the dimension of the mesh.
>>> phiAnalytical = CellVariable(name="analytical value",
... mesh=mesh)
>>> try:
... from scipy.special import erf
... phiAnalytical.setValue(1 - erf(x / (2 * numerix.sqrt(D * t))))
... except ImportError:
... print "The SciPy library is not available to test the solution to \
... the transient diffusion equation"
1
solution variable
analytical value
0.8
0.6
0.4
0.2
0
0 10 20 30 40 50
10.1. examples.diffusion.mesh1D 57
FiPy Manual, Release 3.1.3
Although explicit finite differences are easy to program, we have just seen that this 1D transient diffusion problem is
limited to taking rather small time steps. If, instead, we represent Eq. (10.1) as:
phi_new[j] = phi_old[j] \
+ (D * dt / dx**2) * (phi_new[j+1] - 2 * phi_new[j] + phi_new[j-1])
it is possible to take much larger time steps. Because phi_new appears on both the left and right sides of the
equation, this form is called “implicit”. In general, the “implicit” representation is much more difficult to program
than the “explicit” form that we just used, but in FiPy, all that is needed is to write
>>> eqI = TransientTerm() == DiffusionTerm(coeff=D)
1
solution variable
analytical value
0.8
0.6
0.4
0.2
0
0 10 20 30 40 50
Note that although much larger stable timesteps can be taken with this implicit version (there is, in fact, no limit to
how large an implicit timestep you can take for this particular problem), the solution is less accurate. One way to
achieve a compromise between stability and accuracy is with the Crank-Nicholson scheme, represented by:
phi_new[j] = phi_old[j] + (D * dt / (2 * dx**2)) * \
((phi_new[j+1] - 2 * phi_new[j] + phi_new[j-1])
+ (phi_old[j+1] - 2 * phi_old[j] + phi_old[j-1]))
which is essentially an average of the explicit and implicit schemes from above. This can be rendered in FiPy as easily
as
>>> eqCN = eqX + eqI
and apply the Crank-Nicholson scheme until the end, when we apply one step of the fully implicit scheme to drive
down the error (see, e.g., section 19.2 of [22]).
>>> for step in range(steps - 1):
... eqCN.solve(var=phi,
... dt=timeStepDuration)
... if __name__ == '__main__':
... viewer.plot()
>>> eqI.solve(var=phi,
... dt=timeStepDuration)
>>> if __name__ == '__main__':
... viewer.plot()
10.1. examples.diffusion.mesh1D 59
FiPy Manual, Release 3.1.3
As mentioned above, there is no stable limit to how large a time step can be taken for the implicit diffusion problem.
In fact, if the time evolution of the problem is not interesting, it is possible to eliminate the time step altogether by
omitting the TransientTerm. The steady-state diffusion equation
𝐷∇2 𝜑 = 0
is represented in FiPy by
>>> DiffusionTerm(coeff=D).solve(var=phi)
The analytical solution to the steady-state problem is no longer an error function, but simply a straight line, which we
can confirm to a tolerance of 10−10 .
>>> L = nx * dx
>>> print phi.allclose(valueLeft + (valueRight - valueLeft) * x / L,
... rtol = 1e-10, atol = 1e-10)
1
1
solution variable
analytical value
0.8
0.6
0.4
0.2
0
0 10 20 30 40 50
Often, boundary conditions may be functions of another variable in the system or of time.
For example, to have
{︃
(1 + sin 𝑡)/2 on 𝑥 = 0
𝜑=
0 on 𝑥 = 𝐿
When we update time at each timestep, the left-hand boundary condition will automatically update,
>>> dt = .1
>>> while time() < 15:
... time.setValue(time() + dt)
... eqI.solve(var=phi, dt=dt)
... if __name__ == '__main__':
... viewer.plot()
10.1. examples.diffusion.mesh1D 61
FiPy Manual, Release 3.1.3
1
solution variable
analytical value
0.8
0.6
0.4
0.2
0
0 10 20 30 40 50
Many interesting problems do not have simple, uniform diffusivities. We consider a steady-state diffusion problem
∇ · (𝐷∇𝜑) = 0,
and with boundary conditions 𝜑 = 0 at 𝑥 = 0 and 𝐷 𝜕𝜑 𝜕𝑥 = 1 at 𝑥 = 𝐿, where 𝐿 is the length of the solution domain.
Exact numerical answers to this problem are found when the mesh has cell centers that lie at 𝐿/4 and 3𝐿/4, or when
the number of cells in the mesh 𝑁𝑖 satisfies 𝑁𝑖 = 4𝑖 + 2, where 𝑖 is an integer. The mesh we’ve been using thus far is
satisfactory, with 𝑁𝑖 = 50 and 𝑖 = 12.
Because FiPy considers diffusion to be a flux from one cell to the next, through the intervening face, we must define
the non-uniform diffusion coefficient on the mesh faces
>>> D = FaceVariable(mesh=mesh, value=1.0)
>>> X = mesh.faceCenters[0]
>>> D.setValue(0.1, where=(L / 4. <= X) & (X < 3. * L / 4.))
>>> fluxRight = 1.
to the right:
>>> phi = CellVariable(mesh=mesh)
>>> phi.faceGrad.constrain([fluxRight], mesh.facesRight)
>>> phi.constrain(valueLeft, mesh.facesLeft)
and obtain the steady-state solution with one implicit solution step
>>> DiffusionTerm(coeff = D).solve(var=phi)
or
>>> x = mesh.cellCenters[0]
>>> phiAnalytical.setValue(x)
>>> phiAnalytical.setValue(10 * x - 9. * L / 4. ,
... where=(L / 4. <= x) & (x < 3. * L / 4.))
>>> phiAnalytical.setValue(x + 18. * L / 4. ,
... where=3. * L / 4. <= x)
>>> print phi.allclose(phiAnalytical, atol = 1e-8, rtol = 1e-8)
1
10.1. examples.diffusion.mesh1D 63
FiPy Manual, Release 3.1.3
solution variable
300
solution variable
250
200
150
100
50
0
0 10 20 30 40 50
Often, the diffusivity is not only non-uniform, but also depends on the value of the variable, such that
𝜕𝜑
= ∇ · [𝐷(𝜑)∇𝜑]. (10.2)
𝜕𝑡
With such a non-linearity, it is generally necessary to “sweep” the solution to convergence. This means that each
time step should be calculated over and over, using the result of the previous sweep to update the coefficients of
the equation, without advancing in time. In FiPy, this is accomplished by creating a solution variable that explicitly
retains its “old” value by specifying hasOld when you create it. The variable does not move forward in time until it
is explicity told to updateOld(). In order to compare the effects of different numbers of sweeps, let us create a list
of variables: phi[0] will be the variable that is actually being solved and phi[1] through phi[4] will display the
result of taking the corresponding number of sweeps (phi[1] being equivalent to not sweeping at all).
>>> valueLeft = 1.
>>> valueRight = 0.
>>> phi = [
... CellVariable(name="solution variable",
... mesh=mesh,
... value=valueRight,
... hasOld=1),
... CellVariable(name="1 sweep",
... mesh=mesh),
... CellVariable(name="2 sweeps",
... mesh=mesh),
... CellVariable(name="3 sweeps",
... mesh=mesh),
... CellVariable(name="4 sweeps",
... mesh=mesh)
... ]
𝐷 = 𝐷0 (1 − 𝜑)
Note: Because of the non-linearity, the Crank-Nicholson scheme does not work for this problem.
We apply the same boundary conditions that we used for the uniform diffusivity cases
>>> phi[0].constrain(valueRight, mesh.facesRight)
>>> phi[0].constrain(valueLeft, mesh.facesLeft)
Although this problem does not have an exact transient solution, it can be solved in steady-state, with
√︂
𝑥
𝜑(𝑥) = 1 −
𝐿
>>> x = mesh.cellCenters[0]
>>> phiAnalytical.setValue(1. - numerix.sqrt(x/L))
We create a viewer to compare the different numbers of sweeps with the analytical solution from before.
>>> if __name__ == '__main__':
... viewer = Viewer(vars=phi + [phiAnalytical],
... datamin=0., datamax=1.)
... viewer.plot()
As described above, an inner “sweep” loop is generally required for the solution of non-linear or multiple equation
sets. Often a conditional is required to exit this “sweep” loop given some convergence criteria. Instead of using
the solve() method equation, when sweeping, it is often useful to call sweep() instead. The sweep() method
behaves the same way as solve(), but returns the residual that can then be used as part of the exit condition.
We now repeatedly run the problem with increasing numbers of sweeps.
>>> for sweeps in range(1,5):
... phi[0].setValue(valueRight)
... for step in range(steps):
... # only move forward in time once per time step
... phi[0].updateOld()
...
... # but "sweep" many times per time step
... for sweep in range(sweeps):
... res = eq.sweep(var=phi[0],
... dt=timeStepDuration)
... if __name__ == '__main__':
... viewer.plot()
...
... # copy the final result into the appropriate display variable
... phi[sweeps].setValue(phi[0])
... if __name__ == '__main__':
... viewer.plot()
10.1. examples.diffusion.mesh1D 65
FiPy Manual, Release 3.1.3
As can be seen, sweeping does not dramatically change the result, but the “residual” of the equation (a measure of
how accurately it has been solved) drops about an order of magnitude with each additional sweep.
Attention: Choosing an optimal balance between the number of time steps, the number of sweeps, the number of
solver iterations, and the solver tolerance is more art than science and will require some experimentation on your
part for each new problem.
Finally, we can increase the number of steps to approach equilibrium, or we can just solve for it directly
>>> eq = DiffusionTerm(coeff=D0 * (1 - phi[0]))
>>> phi[0].setValue(valueRight)
>>> res = 1e+10
>>> while res > 1e-6:
... res = eq.sweep(var=phi[0],
... dt=timeStepDuration)
1
solution variable
1 sweep
2 sweeps
3 sweeps
0.8 4 sweeps
analytical value
0.6
0.4
0.2
0
0 10 20 30 40 50
Fully implicit solutions are not without their pitfalls, particularly in steady state. Consider a localized block of material
diffusing in a closed box.
>>> phi = CellVariable(mesh=mesh, name=r"$\phi$")
>>> phi.value = 0.
>>> phi.setValue(1., where=(x > L/2. - L/10.) & (x < L/2. + L/10.))
>>> if __name__ == '__main__':
... viewer = Viewer(vars=phi, datamin=-0.1, datamax=1.1)
φ
φ
1.0
0.8
0.6
0.4
0.2
0.0
0 10 20 30 40 50
We assign no explicit boundary conditions, leaving the default no-flux boundary conditions, and solve
𝜕𝜑/𝜕𝑡 = ∇ · (𝐷∇𝜑)
>>> D = 1.
>>> eq = TransientTerm() == DiffusionTerm(D)
10.1. examples.diffusion.mesh1D 67
FiPy Manual, Release 3.1.3
φ
φ
1.0
0.8
0.6
0.4
0.2
0.0
0 10 20 30 40 50
and see that 𝜑 dissipates to the expected average value of 0.2 with reasonable accuracy.
>>> print numerix.allclose(phi, 0.2, atol=1e-5)
True
φ
φ
1.0
0.8
0.6
0.4
0.2
0.0
0 10 20 30 40 50
we find that the value is uniformly zero! What happened to our no-flux boundary conditions?
10.1. examples.diffusion.mesh1D 69
FiPy Manual, Release 3.1.3
the initial condition 𝜑old no longer appears and 𝜑 = 0 is a perfectly legitimate solution to this matrix equation.
The solution is to run the transient problem and to take one enormous time step
>>> phi.value = 0.
>>> phi.setValue(1., where=(x > L/2. - L/10.) & (x < L/2. + L/10.))
>>> if __name__ == '__main__':
... viewer.plot()
φ
φ
1.0
0.8
0.6
0.4
0.2
0.0
0 10 20 30 40 50
If this example had been written primarily as a script, instead of as documentation, we would delete every line that
does not begin with either “>>>” or “...”, and then delete those prefixes from the remaining lines, leaving:
#!/usr/bin/env python
nx = 50
dx = 1.
mesh = Grid1D(nx = nx, dx = dx)
phi = CellVariable(name="solution variable",
mesh=mesh,
value=0)
eq = DiffusionTerm(coeff=D0 * (1 - phi[0]))
phi[0].setValue(valueRight)
res = 1e+10
10.1. examples.diffusion.mesh1D 71
FiPy Manual, Release 3.1.3
Your own scripts will tend to look like this, although you can always write them as doctest scripts if you choose. You
can obtain a plain script like this from some .../example.py by typing:
$ python setup.py copy_script --From .../example.py --To myExample.py
10.2 examples.diffusion.coupled
𝜕4𝑣 𝜕2𝑣
+ =0
𝜕𝑥4 𝜕𝑡2
cannot be represented as a single equation. We need to decompose the biharmonic equation into two equations that
are first order in time in the following way,
𝜕 2 𝑣0 𝜕𝑣1
2
+ =0
𝜕𝑥 𝜕𝑡
𝜕 2 𝑣1 𝜕𝑣0
− =0
𝜕𝑥2 𝜕𝑡
Historically, FiPy required systems of coupled equations to be solved successively, “sweeping” the equations to con-
vergence. As a practical example, we use the following system
𝜕𝑣0
= 0.01∇2 𝑣0 − ∇2 𝑣1
𝜕𝑡
𝜕𝑣1
= ∇2 𝑣0 + 0.01∇2 𝑣1
𝜕𝑡
subject to the boundary conditions
𝑣0 |𝑥=0 = 0 𝑣0 |𝑥=1 = 1
𝑣1 |𝑥=0 = 1 𝑣1 |𝑥=1 = 0
This system closely resembles the pure biharmonic equation, but has an additional diffusion contribution to improve
numerical stability. The example system is solved with the following block of code using explicit coupling for the
cross-coupled terms.
The uncoupled method still works, but it can be advantageous to solve the two equations simultaneously. In this case,
by coupling the equations, we can eliminate the explicit sources and dramatically increase the time steps:
>>> v0.value = 0.5
>>> v1.value = 0.5
10.2. examples.diffusion.coupled 73
FiPy Manual, Release 3.1.3
Whether you pose your problem in coupled or vector form should be dictated by the underlying physics. If 𝑣0 and
𝑣1 represent the concentrations of two conserved species, then it is natural to write two seperate governing equations
and to couple them. If they represent two components of a vector field, then the vector formulation is obviously more
natural. FiPy will solve the same matrix system either way.
10.3 examples.diffusion.mesh20x20
>>> nx = 20
>>> ny = nx
>>> dx = 1.
>>> dy = dx
>>> L = dx * nx
>>> mesh = Grid2D(dx=dx, dy=dy, nx=nx, ny=ny)
and then create a diffusion equation. This is solved by default with an iterative conjugate gradient solver.
>>> D = 1.
>>> eq = TransientTerm() == DiffusionTerm(coeff=D)
to the top-left and bottom-right corners. Neumann boundary conditions are automatically applied to the top-right and
bottom-left corners.
>>> X, Y = mesh.faceCenters
>>> facesTopLeft = ((mesh.facesLeft & (Y > L / 2))
... | (mesh.facesTop & (X < L / 2)))
>>> facesBottomRight = ((mesh.facesRight & (Y < L / 2))
... | (mesh.facesBottom & (X > L / 2)))
solution variable
20 1.0
0.9
0.8
15
0.7
0.6
solution variable
10 0.5
0.4
0.3
5
0.2
0.1
0 0.0
0 5 10 15 20
10.3. examples.diffusion.mesh20x20 75
FiPy Manual, Release 3.1.3
solution variable
20 1.0
0.9
0.8
15
0.7
0.6
solution variable
10 0.5
0.4
0.3
5
0.2
0.1
0 0.0
0 5 10 15 20
10.4 examples.diffusion.circle
The cellSize is the preferred edge length of each mesh element and the radius is the radius of the circular mesh domain.
In the following code section a file is created with the geometry that describes the mesh. For details of how to write
such geometry files for Gmsh, see the gmsh manual.
The mesh created by Gmsh is then imported into FiPy using the Gmsh2D object.
>>> from fipy import *
>>> mesh = Gmsh2D('''
... cellSize = %(cellSize)g;
10.4. examples.diffusion.circle 77
FiPy Manual, Release 3.1.3
1.0
0.5
0.0
−0.5
−1.0
−1.0 −0.5 0.0 0.5 1.0
We set up a transient diffusion equation
>>> D = 1.
>>> eq = TransientTerm() == DiffusionTerm(coeff=D)
The following line extracts the 𝑥 coordinate values on the exterior faces. These are used as the boundary condition
fixed values.
>>> X, Y = mesh.faceCenters
1
1.0
0.5 0.5
solution variable
0.0 0
−0.5 −0.5
−1.0
−1
−1.0 −0.5 0.0 0.5 1.0
If we wanted to plot or analyze the results of this calculation with another application, we could export tab-separated-
values with
TSVViewer(vars=(phi, phi.grad)).plot(filename="myTSV.tsv")
The values are listed at the cell centers. Particularly for irregular meshes, no specific ordering should be relied upon.
Vector quantities are listed in multiple columns, one for each mesh dimension.
10.4. examples.diffusion.circle 79
FiPy Manual, Release 3.1.3
This problem again has an analytical solution that depends on the error function, but it’s a bit more complicated due
to the varying boundary conditions and the different horizontal diffusion length at different vertical positions
>>> x, y = mesh.cellCenters
>>> t = timeStepDuration * steps
As in the earlier examples, we can also directly solve the steady-state diffusion problem.
>>> DiffusionTerm(coeff=D).solve(var=phi)
1
1.0
0.5 0.5
solution variable
0.0 0
−0.5 −0.5
−1.0
−1
−1.0 −0.5 0.0 0.5 1.0
10.5 examples.diffusion.electrostatics
>>> nx = 200
>>> dx = 0.01
>>> L = nx * dx
>>> mesh = Grid1D(dx = dx, nx = nx)
the permittivity 𝜖,
>>> permittivity = 1
the concentration 𝐶𝑗 of the 𝑗 th component with valence 𝑧𝑗 (we consider only a single component 𝐶e− with valence
with 𝑧e− = −1)
10.5. examples.diffusion.electrostatics 81
FiPy Manual, Release 3.1.3
Because this equation admits an infinite number of potential profiles, we must constrain the solution by fixing the
potential at one point:
>>> potential.constrain(0., mesh.facesLeft)
First, we obtain a uniform charge distribution by setting a uniform concentration of electrons 𝐶e− = 1.
>>> electrons.setValue(1.)
𝑥2
𝜓(𝑥) = − 2𝑥
2
>>> x = mesh.cellCenters[0]
>>> analytical = CellVariable(mesh=mesh, name="analytical solution",
... value=(x**2)/2 - 2*x)
0
charge
potential
analytical solution
-0.5
-1
-1.5
-2
0 0.5 1 1.5 2
>>> x = mesh.cellCenters[0]
>>> electrons.setValue(0.)
>>> electrons.setValue(1., where=x > L / 2.)
>>> analytical.setValue(-x)
>>> analytical.setValue(((x-1)**2)/2 - x, where=x > L/2)
10.5. examples.diffusion.electrostatics 83
FiPy Manual, Release 3.1.3
0
charge
potential
-0.2 analytical solution
-0.4
-0.6
-0.8
-1
-1.2
-1.4
-1.6
0 0.5 1 1.5 2
Finally, we segregate all of the electrons to the left side of the domain
{︃
1 for 𝑥 ≤ 𝐿/2,
𝐶e− =
0 for 𝑥 > 𝐿/2.
>>> electrons.setValue(1.)
>>> electrons.setValue(0., where=x > L / 2.)
0
charge
potential
analytical solution
-0.2
-0.4
-0.6
-0.8
-1
0 0.5 1 1.5 2
10.6 examples.diffusion.nthOrder.input4thOrder1D
𝜕4𝜑
=0
𝜕𝑥4
on a 1D mesh of length
>>> L = 1000.
>>> nx = 500
>>> dx = L / nx
>>> mesh = Grid1D(dx=dx, nx=nx)
10.6. examples.diffusion.nthOrder.input4thOrder1D 85
FiPy Manual, Release 3.1.3
𝜑 = 𝛼1 at 𝑥 = 0
𝜕𝜑
= 𝛼2 at 𝑥 = 𝐿
𝜕𝑥
𝜕2𝜑
= 𝛼3 at 𝑥 = 0
𝜕𝑥2
𝜕3𝜑
= 𝛼4 at 𝑥 = 𝐿.
𝜕𝑥3
or
>>> alpha1 = 2.
>>> alpha2 = 1.
>>> alpha3 = 4.
>>> alpha4 = -3.
x1e9
1.0
solution variable
analytical value
0.8
0.6
0.4
0.2
0.0
0 200 400 600 800 1000
10.7 examples.diffusion.anisotropy
10.7. examples.diffusion.anisotropy 87
FiPy Manual, Release 3.1.3
11.1 examples.convection.exponential1D.mesh1D
∇ · (𝐷∇𝜑 + ⃗𝑢𝜑) = 0
We define a 1D mesh
>>> from fipy import *
>>> L = 10.
>>> nx = 10
>>> mesh = Grid1D(dx=L / nx, nx=nx)
>>> valueLeft = 0.
>>> valueRight = 1.
with
>>> var.constrain(valueLeft, mesh.facesLeft)
>>> var.constrain(valueRight, mesh.facesRight)
89
FiPy Manual, Release 3.1.3
The equation is created with the DiffusionTerm and ExponentialConvectionTerm. The scheme used by
the convection term needs to calculate a Peclet number and thus the diffusion term instance must be passed to the
convection term.
>>> eq = (DiffusionTerm(coeff=diffCoeff)
... + ExponentialConvectionTerm(coeff=convCoeff))
More details of the benefits and drawbacks of each type of convection term can be found in Numerical Schemes. Es-
sentially, the ExponentialConvectionTerm and PowerLawConvectionTerm will both handle most types
of convection-diffusion cases, with the PowerLawConvectionTerm being more efficient.
We solve the equation
>>> eq.solve(var=var)
1 − exp(−𝑢𝑥 𝑥/𝐷)
𝜑=
1 − exp(−𝑢𝑥 𝐿/𝐷)
or
>>> axis = 0
>>> x = mesh.cellCenters[axis]
>>> CC = 1. - numerix.exp(-convCoeff[axis] * x / diffCoeff)
>>> DD = 1. - numerix.exp(-convCoeff[axis] * L / diffCoeff)
>>> analyticalArray = CC / DD
>>> print var.allclose(analyticalArray)
1
11.2 examples.convection.exponential1DSource.mesh1D
∇ · (𝐷∇𝜑 + ⃗𝑢𝜑) + 𝑆0 = 0.
>>> diffCoeff = 1.
>>> convCoeff = (10.,)
>>> sourceCoeff = 1.
We define a 1D mesh
>>> from fipy import *
>>> nx = 1000
>>> L = 10.
>>> mesh = Grid1D(dx=L / 1000, nx=nx)
>>> valueLeft = 0.
>>> valueRight = 1.
with
>>> var.constrain(valueLeft, mesh.facesLeft)
>>> var.constrain(valueRight, mesh.facesRight)
>>> eq.solve(var=var,
... solver=DefaultAsymmetricSolver(tolerance=1.e-15, iterations=10000))
11.3 examples.convection.robin
11.3. examples.convection.robin 91
FiPy Manual, Release 3.1.3
𝜕2𝐶 𝜕𝐶
0= 2
−𝑃 − 𝐷𝐶 0<𝑥<1
𝜕𝑥 𝜕𝑥
𝜕𝐶
𝑥=0:𝑃 =− + 𝑃𝐶
𝜕𝑥
𝜕𝐶
𝑥=1: =0
𝜕𝑥
The analytical solution for this equation is given by,
where
√︀
𝐴= 𝑃 + 4𝐷2
>>> D = 2.0
>>> P = 3.0
>>> eq = PowerLawConvectionTerm((P,)) == \
... DiffusionTerm() - ImplicitSourceTerm(D)
>>> A = numerix.sqrt(P**2 + 4 * D)
>>> x = mesh.cellCenters[0]
>>> CAnalytical = CellVariable(mesh=mesh)
>>> CAnalytical.setValue(2 * P * numerix.exp(P * x / 2) * ((P + A) * numerix.exp(A / 2 * (1 - x))
... - (P - A) * numerix.exp(-A / 2 *(1 - x)))/
... ((P + A)**2*numerix.exp(A / 2)- (P - A)**2 * numerix.exp(-A / 2)))
11.4 examples.convection.source
𝜕𝜑
+ 𝛼𝜑 = 0
𝜕𝑥
with 𝜑 (0) = 1 at 𝑥 = 0. The boundary condition at 𝑥 = 𝐿 is an outflow boundary condition requiring the use of
an artificial constraint to be set on the right hand side faces. Exterior faces without constraints are considered to have
zero outflow. An ImplicitSourceTerm object will be used to represent this term. The derivative of 𝜑 can be
represented by a ConvectionTerm with a constant unitary velocity field from left to right. The following is an
example code that includes a test against the analytical result.
>>> from fipy import *
>>> L = 10.
>>> nx = 5000
>>> dx = L / nx
>>> mesh = Grid1D(dx=dx, nx=nx)
>>> phi0 = 1.0
>>> alpha = 1.0
>>> phi = CellVariable(name=r"$\phi$", mesh=mesh, value=phi0)
>>> solution = CellVariable(name=r"solution", mesh=mesh, value=phi0 * numerix.exp(-alpha * mesh.cellC
11.4. examples.convection.source 93
FiPy Manual, Release 3.1.3
12.1 examples.phase.simple
1 𝜕𝜑 𝜕𝑓
= 𝜅𝜑 ∇2 𝜑 − (12.1)
𝑀𝜑 𝜕𝑡 𝜕𝜑
𝑔(𝜑) = 𝜑2 (1 − 𝜑)2
95
FiPy Manual, Release 3.1.3
>>> L = 1.
>>> nx = 400
>>> dx = L / nx
>>> x = mesh.cellCenters[0]
>>> phase.setValue(1.)
>>> phase.setValue(0., where=x > L/2)
We build the equation by assembling the appropriate terms. Since, with 𝑇 = 𝑇𝑀 we are interested in a steady-state
solution, we omit the transient term (1/𝑀𝜑 ) 𝜕𝜑
𝜕𝑡 .
The analytical solution for this steady-state phase field problem, in an infinite domain, is
[︃ ]︃
1 𝑥 − 𝐿/2
𝜑= 1 − tanh √︀ (12.2)
2 2 𝜅/𝑊
or
>>> x = mesh.cellCenters[0]
>>> analyticalArray = 0.5*(1 - numerix.tanh((x - L/2)/(2*numerix.sqrt(kappa/W))))
Note: “Diffusion” in FiPy is not limited to the movement of atoms, but rather refers to the spontaneous spreading of
any quantity (e.g., solute, temperature, or in this case “phase”) by flow “down” the gradient of that quantity.
12.1. examples.phase.simple 97
FiPy Manual, Release 3.1.3
On inspection, we can see that this occurs because, for our step-function initial condition, 𝑚𝜑 = 0 everwhere, hence
we are actually only solving the simple implicit diffusion equation 𝜅𝜑 ∇2 𝜑 = 0, which has exactly the uninteresting
solution we obtained.
The resolution to this problem is to apply relaxation to obtain the desired answer, i.e., the solution is allowed to relax
in time from the initial condition to the desired equilibrium solution. To do so, we reintroduce the transient term from
Equation (12.1)
>>> eq = TransientTerm() == DiffusionTerm(coeff=kappa) + S0
>>> phase.setValue(1.)
>>> phase.setValue(0., where=x > L/2)
After 13 time steps, the solution has converged to the analytical solution
>>> print phase.allclose(analyticalArray, rtol = 1e-4, atol = 1e-4)
1
>>> if __name__ == '__main__':
... raw_input("Relaxation, explicit. Press <return> to proceed...")
Note: The solution is only found accurate to ≈ 4.3 × 10−5 because the infinite-domain analytical solution (12.2) is
not an exact representation for the solution in a finite domain of length 𝐿.
Setting fixed-value boundary conditions of 1 and 0 would still require the relaxation method with the fully explicit
source.
Solution performance can be improved if we exploit the dependence of the source on 𝜑. By doing so, we can make
the source semi-implicit, improving the rate of convergence over the fully explicit approach. The source can only be
semi-implicit because we employ sparse linear algebra routines to solve the PDEs, i.e., there is no fully implicit way
⃗ − ⃗𝑏 = 0.
to represent a term like 𝜑4 in the linear set of equations M𝜑
By linearizing a source as 𝑆 = 𝑆0 − 𝑆1 𝜑, we make it more implicit by adding the coefficient 𝑆1 to the matrix diagonal.
For numerical stability, this linear coefficient must never be negative.
There are an infinite number of choices for this linearization, but many do not converge very well. One choice is that
used by Ryo Kobayashi:
>>> S0 = mPhi * phase * (mPhi > 0)
>>> S1 = mPhi * ((mPhi < 0) - phase)
>>> eq = DiffusionTerm(coeff=kappa) + S0 \
... + ImplicitSourceTerm(coeff = S1)
Note: Because mPhi is a variable field, the quantities (mPhi > 0) and (mPhi < 0) evaluate to variable fields
of True and False, instead of single boolean values.
This expression converges to the same value given by the explicit relaxation approach, but in only 8 sweeps (note that
because there is no transient term, these sweeps are not time steps, but rather repeated iterations at the same time step
to reach a converged solution).
Note: We use solve() instead of sweep() because we don’t care about the residual. Either function would work,
but solve() is a bit faster.
>>> phase.setValue(1.)
>>> phase.setValue(0., where=x > L/2)
12.1. examples.phase.simple 99
FiPy Manual, Release 3.1.3
In general, the best convergence is obtained when the linearization gives a good representation of the relationship
between the source and the dependent variable. The best practical advice is to ⃒ perform a Taylor expansion of the source
⃒
about the previous value of the dependent variable such that 𝑆 = 𝑆old + 𝜕𝜑 ⃒ (𝜑 − 𝜑old ) = (𝑆 − 𝜕𝑆
𝜕𝑆 ⃒ 𝜕𝑆 ⃒
𝜕𝜑 𝜑)old + 𝜕𝜑 ⃒ 𝜑.
⃒ old old
Now, if our source term is represented by 𝑆 = 𝑆0 + 𝑆1 𝜑, then 𝑆1 = 𝜕𝜑 ⃒ and 𝑆0 = (𝑆 − 𝜕𝜑 𝜑)old = 𝑆old − 𝑆1 𝜑old .
𝜕𝑆 ⃒ 𝜕𝑆
old
In this way, the linearized source will be tangent to the curve of the actual source as a function of the dependendent
variable.
For our source, 𝑆 = 𝑚𝜑 𝜑(1 − 𝜑),
𝜕𝑆 𝜕𝑚𝜑
= 𝜑(1 − 𝜑) + 𝑚𝜑 (1 − 2𝜑)
𝜕𝜑 𝜕𝜑
and
𝜕𝑚𝜑 𝑇 − 𝑇𝑀
= 2𝑊 − 30(1 − 2𝜑)𝐿 ,
𝜕𝜑 𝑇𝑀
or
>>> dmPhidPhi = 2 * W - 30 * (1 - 2 * phase) * enthalpy
>>> S1 = dmPhidPhi * phase * (1 - phase) + mPhi * (1 - 2 * phase)
>>> S0 = mPhi * phase * (1 - phase) - S1 * phase
>>> eq = DiffusionTerm(coeff=kappa) + S0 \
... + ImplicitSourceTerm(coeff = S1)
Using this scheme, where the coefficient of the implicit source term is tangent to the source, we reach convergence in
only 5 sweeps
>>> phase.setValue(1.)
>>> phase.setValue(0., where=x > L/2)
Although, for this simple problem, there is no appreciable difference in run-time between the fully explicit source and
the optimized semi-implicit source, the benefit of 60% fewer sweeps should be obvious for larger systems and longer
iterations.
This example has focused on just the region of the phase field interface in equilibrium. Problems of interest, though,
usually involve the dynamics of one phase transforming to another. To that end, let us recast the problem using physical
parameters and dimensions. We’ll need a new mesh
>>> nx = 400
>>> dx = 5e-6 # cm
>>> L = nx * dx
The parameters of the phase field model can be related to the surface energy 𝜎 and the interfacial thickness 𝛿 by
𝜅 = 6𝜎𝛿
6𝜎
𝑊 =
𝛿
𝑇𝑚 𝛽
𝑀𝜑 = .
6𝐿𝛿
We take 𝛿 ≈ ∆𝑥.
>>> delta = 1.5 * dx
>>> sigma = 3.7e-5 # J / cm**2
>>> beta = 0.33 # cm / (K s)
>>> kappa = 6 * sigma * delta # J / cm
>>> W = 6 * sigma / delta # J / cm**3
>>> Mphi = Tm * beta / (6. * Lv * delta) # cm**3 / (J s)
Now we can redefine the transient phase field equation, using the optimal form of the source term shown above
>>> mPhi = -((1 - 2 * phase) * W + 30 * phase * (1 - phase) * enthalpy)
>>> dmPhidPhi = 2 * W - 30 * (1 - 2 * phase) * enthalpy
>>> S1 = dmPhidPhi * phase * (1 - phase) + mPhi * (1 - 2 * phase)
In order to separate the effect of forming the phase field interface from the kinetics of moving it, we first equilibrate at
the melting point. We now use the sweep() method instead of solve() because we require the residual.
>>> timeStep = 1e-6
>>> for i in range(10):
... phase.updateOld()
... res = 1e+10
... while res > 1e-5:
... res = eq.sweep(var=phase, dt=timeStep)
>>> if __name__ == '__main__':
... viewer2.plot()
In order to have a stable numerical solution, the interface must not move more than one grid point per time step, we
thus set the timestep according to the grid spacing ∆𝑥, the linear kinetic coefficient 𝛽, and the undercooling |𝑇𝑚 − 𝑇 |
Again we use the sweep() method as a replacement for solve().
>>> velocity = beta * abs(Tm - T()) # cm / s
>>> timeStep = .1 * dx / velocity # s
>>> elapsed = 0
>>> while elapsed < displacement / velocity:
... phase.updateOld()
... res = 1e+10
... while res > 1e-5:
... res = eq.sweep(var=phase, dt=timeStep)
... elapsed += timeStep
... if __name__ == '__main__':
... viewer2.plot()
A hyperbolic tangent is not an exact steady-state solution given the quintic polynomial we chose for the 𝑝 function,
but it gives a reasonable approximation.
>>> print phase.allclose(analyticalArray, rtol = 5, atol = 2e-3)
1
If we had made another common choice of 𝑝(𝜑) = 𝜑2 (3 − 2𝜑), we would have found much better agreement, as
that case does give an exact tanh solution in steady state. If SciPy is available, another way to compare against the
expected result is to do a least-squared fit to determine the interface velocity and thickness
>>> try:
... def tanhResiduals(p, y, x, t):
... V, d = p
... return y - 0.5 * (1 - numerix.tanh((x - V * t - L / 2.) / (2*d)))
... from scipy.optimize import leastsq
... x = mesh.cellCenters[0]
... (V_fit, d_fit), msg = leastsq(tanhResiduals, [L/2., delta],
... args=(phase.globalValue, x.globalValue, elapsed))
... except ImportError:
... V_fit = d_fit = 0
... print "The SciPy library is unavailable to fit the interface \
... thickness and velocity"
phase
1
phase
0.8
0.6
0.4
0.2
0
0 5e-4 1e-3 1.5e-3 2e-3
12.2 examples.phase.binaryCoupled
>>> nx = 400
>>> dx = 5e-6 # cm
>>> L = nx * dx
>>> mesh = Grid1D(dx=dx, nx=nx)
The Helmholtz free energy functional can be written as the integral [2] [4] [26]
𝜅𝜑 𝜅𝐶
∫︁ {︁ }︁
ℱ (𝜑, 𝐶, 𝑇 ) = 𝑓 (𝜑, 𝐶, 𝑇 ) + |∇𝜑|2 + |∇𝐶|2 𝑑𝑉
𝒱 2 2
composition 𝐶
>>> C = CellVariable(name="composition", mesh=mesh, hasOld=1)
2
and temperature 𝑇
>>> T = Variable(name="temperature")
Frequently, the gradient energy term in concentration is ignored and we can derive governing equations
(︂ )︂
𝜕𝜑 𝜕𝑓
= 𝑀 𝜑 𝜅 𝜑 ∇2 𝜑 − (12.3)
𝜕𝑡 𝜕𝜑
for solute.
The free energy density 𝑓 (𝜑, 𝐶, 𝑇 ) can be constructed in many different ways. One approach is to construct free
energy densities for each of the pure components, as functions of phase, e.g.
𝑊𝐴
𝑓𝐴 (𝜑, 𝑇 ) = 𝑝(𝜑)𝑓𝐴𝑆 (𝑇 ) + (1 − 𝑝(𝜑)) 𝑓𝐴𝐿 (𝑇 ) + 𝑔(𝜑)
2
where 𝑓𝐴𝐿 (𝑇 ), 𝑓𝐵𝐿 (𝑇 ), 𝑓𝐴𝑆 (𝑇 ), and 𝑓𝐵𝑆 (𝑇 ) are the free energy densities of the pure components. There are a variety of
choices for the interpolation function 𝑝(𝜑) and the barrier function 𝑔(𝜑),
such as those shown in examples.phase.simple
>>> def p(phi):
... return phi**3 * (6 * phi**2 - 15 * phi + 10)
The desired thermodynamic model can then be applied to obtain 𝑓 (𝜑, 𝐶, 𝑇 ), such as for a regular solution,
where
>>> R = 8.314 # J / (mol K)
is the gas constant and Ω𝑆 and Ω𝐿 are the regular solution interaction parameters for solid and liquid.
Another approach is useful when the free energy densities 𝑓 𝐿 (𝐶, 𝑇 ) and 𝑓 𝑆 (𝐶, 𝑇 ) of the alloy in the solid and liquid
phases are known. This might be the case when the two different phases have different thermodynamic models or
when one or both is obtained from a Calphad code. In this case, we can construct
[︂ ]︂
𝑊𝐴 𝑊𝐵
𝑓 (𝜑, 𝐶, 𝑇 ) = 𝑝(𝜑)𝑓 𝑆 (𝐶, 𝑇 ) + (1 − 𝑝(𝜑)) 𝑓 𝐿 (𝐶, 𝑇 ) + (1 − 𝐶) +𝐶 𝑔(𝜑).
2 2
1 We will find that we need to “sweep” this non-linear problem (see e.g. the composition-dependent diffusivity example in
examples.diffusion.mesh1D), so we declare 𝜑 and 𝐶 to retain an “old” value.
2 we are going to want to examine different temperatures in this example, so we declare 𝑇 as a Variable
When the thermodynamic models are the same in both phases, both approaches should yield the same result.
We choose the first approach and make the simplifying assumptions of an ideal solution and that
𝑓𝐴𝐿 (𝑇 ) = 0
(︀ 𝐴
)︀
𝐿𝐴 𝑇 − 𝑇𝑀
𝑓𝐴𝑆 (𝑇 ) − 𝑓𝐴𝐿 (𝑇 ) = 𝐴
𝑇𝑀
This relates the difference between the free energy densities of the pure solid and pure liquid phases to the latent heat
𝐿𝐴 and the pure component melting point 𝑇𝑀 𝐴
, such that
(︀ 𝐴
)︀
𝐿𝐴 𝑇 − 𝑇𝑀 𝑊𝐴
𝑓𝐴 (𝜑, 𝑇 ) = 𝐴
𝑝(𝜑) + 𝑔(𝜑).
𝑇𝑀 2
and
[︂ ]︂ [︂ ]︂
𝜕𝑓 𝑅𝑇 𝑅𝑇
= 𝑓𝐵 (𝜑, 𝑇 ) + ln 𝐶 − 𝑓𝐴 (𝜑, 𝑇 ) + ln(1 − 𝐶)
𝜕𝐶 𝑉𝑚 𝑉𝑚
= [𝜇𝐵 (𝜑, 𝐶, 𝑇 ) − 𝜇𝐴 (𝜑, 𝐶, 𝑇 )] /𝑉𝑚
where 𝜇𝐴 and 𝜇𝐵 are the classical chemical potentials for the binary species. 𝑝′ (𝜑) and 𝑔 ′ (𝜑) are the partial derivatives
of of 𝑝 and 𝑔 with respect to 𝜑
>>> def pPrime(phi):
... return 30. * g(phi)
On comparison with examples.phase.simple, we can see that the present form of the phase field equation is
identical to the one found earlier, with the source now composed of the concentration-weighted average of the source
for either pure component. We let the pure component barriers equal the previous value
>>> deltaA = deltaB = 1.5 * dx
>>> sigmaA = 3.7e-5 # J / cm**2
>>> sigmaB = 2.9e-5 # J / cm**2
>>> betaA = 0.33 # cm / (K s)
Using the same gradient energy coefficient and phase field mobility
>>> kappa = (1 - C) * kappaA + C * kappaB
>>> Mphi = TmA * betaA / (6 * LA * deltaA)
When coding explicitly, it is typical to simply write a function to evaluate the chemical potentials 𝜇𝐴 and 𝜇𝐵 and then
perform the finite differences necessary to calculate their gradient and divergence, e.g.,:
def deltaChemPot(phase, C, T):
return ((Vm * (enthalpyB * p(phase) + WA * g(phase)) + R * T * log(1 - C)) -
(Vm * (enthalpyA * p(phase) + WA * g(phase)) + R * T * log(C)))
for j in range(faces):
flux[j] = ((Mc[j+.5] + Mc[j-.5]) / 2) \
* (deltaChemPot(phase[j+.5], C[j+.5], T) \
- deltaChemPot(phase[j-.5], C[j-.5], T)) / dx
for j in range(cells):
diffusion = (flux[j+.5] - flux[j-.5]) / dx
where we neglect the details of the outer boundaries (j = 0 and j = N) or exactly how to translate j+.5 or j-.5
into an array index, much less the complexities of higher dimensions. FiPy can handle all of these issues automatically,
so we could just write:
chemPotA = Vm * (enthalpyA * p(phase) + WA * g(phase)) + R * T * log(C)
chemPotB = Vm * (enthalpyB * p(phase) + WB * g(phase)) + R * T * log(1-C)
flux = Mc * (chemPotB - chemPotA).faceGrad
eq = TransientTerm() == flux.divergence
Although the second syntax would essentially work as written, such an explicit implementation would be very slow.
In order to take advantage of FiPy‘s implicit solvers, it is necessary to reduce Eq. (12.4) to the canonical form of Eq.
(??), hence we must expand Eq. (12.2) as
[︃ (︀ )︀ (︀ )︀ ]︃
𝐵 𝐴
𝜕𝑓 𝐿𝐵 𝑇 − 𝑇𝑀 𝐿𝐴 𝑇 − 𝑇𝑀 𝑅𝑇 𝑊𝐵 − 𝑊𝐴
= 𝐵
− 𝐴
𝑝(𝜑) + [ln 𝐶 − ln(1 − 𝐶)] + 𝑔(𝜑)
𝜕𝐶 𝑇𝑀 𝑇𝑀 𝑉𝑚 2
In either bulk phase, ∇𝑝(𝜑) = ∇𝑔(𝜑) = 0, so we can then reduce Eq. (12.4) to
(︂ {︂ }︂)︂
𝜕𝐶 𝑅𝑇
= ∇ · 𝑀𝐶 ∇ [ln 𝐶 − ln(1 − 𝐶)]
𝜕𝑡 𝑉𝑚
[︂ ]︂
𝑀𝐶 𝑅𝑇
= ∇· ∇𝐶
𝐶(1 − 𝐶)𝑉𝑚
and, by comparison with Fick’s second law
𝜕𝐶
= ∇ · [𝐷∇𝐶] ,
𝜕𝑡
we can associate the mobility 𝑀𝐶 with the intrinsic diffusivity 𝐷𝐶 by 𝑀𝐶 ≡ 𝐷𝐶 𝐶(1 − 𝐶)𝑉𝑚 /𝑅𝑇 and write Eq.
(12.4) as
𝜕𝐶
= ∇ · (𝐷𝐶 ∇𝐶)
𝜕𝑡 (︃ {︃[︃ (︀ 𝐵
)︀ (︀ 𝐴
)︀ ]︃ }︃)︃
𝐷𝐶 𝐶(1 − 𝐶)𝑉𝑚 𝐿𝐵 𝑇 − 𝑇𝑀 𝐿𝐴 𝑇 − 𝑇𝑀 𝑊𝐵 − 𝑊𝐴
+ ∇· 𝐵
− 𝐴
∇𝑝(𝜑) + ∇𝑔(𝜑) .
𝑅𝑇 𝑇𝑀 𝑇𝑀 2
= ∇ · (𝐷𝐶 ∇𝐶)
(︃ {︃[︃ (︀ )︀ (︀ )︀ ]︃ }︃ )︃
𝐵 𝐴
𝐷𝐶 𝐶(1 − 𝐶)𝑉𝑚 𝐿𝐵 𝑇 − 𝑇𝑀 𝐿𝐴 𝑇 − 𝑇𝑀 ′ 𝑊𝐵 − 𝑊𝐴 ′
+ ∇· 𝐵
− 𝐴
𝑝 (𝜑) + 𝑔 (𝜑) ∇𝜑 .
𝑅𝑇 𝑇𝑀 𝑇𝑀 2
The first term is clearly a DiffusionTerm in 𝐶. The second is a DiffusionTerm in 𝜑 with a diffusion coefficent
{︃[︃ (︀ )︀ (︀ )︀ ]︃ }︃
𝐵 𝐴
𝐷𝐶 𝐶(1 − 𝐶)𝑉𝑚 𝐿𝐵 𝑇 − 𝑇𝑀 𝐿𝐴 𝑇 − 𝑇𝑀 ′ 𝑊𝐵 − 𝑊𝐴 ′
𝐷𝜑 (𝐶, 𝜑) = 𝐵
− 𝐴
𝑝 (𝜑) + 𝑔 (𝜑) ,
𝑅𝑇 𝑇𝑀 𝑇𝑀 2
such that
𝜕𝐶
= ∇ · (𝐷𝐶 ∇𝐶) + ∇ · (𝐷𝜑 ∇𝜑)
𝜕𝑡
or
>>> Dl = Variable(value=1e-5) # cm**2 / s
>>> Ds = Variable(value=1e-9) # cm**2 / s
>>> Dc = (Ds - Dl) * phase.arithmeticFaceValue + Dl
We initialize the phase field to a step function in the middle of the domain
>>> phase.setValue(1.)
>>> phase.setValue(0., where=mesh.cellCenters[0] > L/2.)
>>> C.setValue(0.5)
In equilibrium, 𝜇𝐴 (0, 𝐶𝐿 , 𝑇 ) = 𝜇𝐴 (1, 𝐶𝑆 , 𝑇 ) and 𝜇𝐵 (0, 𝐶𝐿 , 𝑇 ) = 𝜇𝐵 (1, 𝐶𝑆 , 𝑇 ) and, for ideal solutions, we can
deduce the liquidus and solidus compositions as
(︂ )︂
𝐿𝐴 (𝑇 −𝑇𝑀𝐴
) 𝑉𝑚
1 − exp − 𝑇𝑀𝐴 𝑅𝑇
𝐶𝐿 = (︂ )︂ (︂ )︂
𝐿𝐵 (𝑇 −𝑇𝑀 𝐵
) 𝑉𝑚 𝐿𝐴 (𝑇 −𝑇𝑀
𝐴
) 𝑉𝑚
exp − 𝑇𝑀𝐵 𝑅𝑇 − exp − 𝑇𝑀𝐴 𝑅𝑇
(︃ (︀ )︀ )︃
𝐵
𝐿𝐵 𝑇 − 𝑇𝑀 𝑉𝑚
𝐶𝑆 = exp − 𝐵
𝐶𝐿
𝑇𝑀 𝑅𝑇
For the special case of fraction = Cavg = 0.5, a little bit of algebra reveals that the temperature that leaves
the phase fraction unchanged is given by
>>> T.setValue((LA + LB) * TmA * TmB / (LA * TmB + LB * TmA))
In this simple, binary, ideal solution case, we can derive explicit expressions for the solidus and liquidus compositions.
In general, this may not be possible or practical. In that event, the root-finding facilities in SciPy can be used.
We’ll need a function to return the two conditions for equilibrium
(︀ 𝐴
)︀
𝐿𝐴 𝑇 − 𝑇𝑀
0 = 𝜇𝐴 (1, 𝐶𝑆 , 𝑇 ) − 𝜇𝐴 (0, 𝐶𝐿 , 𝑇 ) = 𝑉𝑚 + 𝑅𝑇 ln(1 − 𝐶𝑆 ) − 𝑅𝑇 ln(1 − 𝐶𝐿 )
𝑇𝐴
(︀ 𝑀 𝐵 )︀
𝐿𝐵 𝑇 − 𝑇𝑀
0 = 𝜇𝐵 (1, 𝐶𝑆 , 𝑇 ) − 𝜇𝐵 (0, 𝐶𝐿 , 𝑇 ) = 𝐵
𝑉𝑚 + 𝑅𝑇 ln 𝐶𝑆 − 𝑅𝑇 ln 𝐶𝐿
𝑇𝑀
and we’ll have much better luck if we also supply the Jacobian
[︃ 𝑆 𝐿 𝑆 𝐿 ]︃
𝜕(𝜇𝐴 −𝜇𝐴 ) 𝜕(𝜇𝐴 −𝜇𝐴 ) [︂ 1 1 ]︂
𝜕𝐶𝑆 𝜕𝐶𝐿 − 1−𝐶 1−𝐶𝐿
= 𝑅𝑇 1
𝑆
𝜕(𝜇𝑆 − 𝐶1𝐿
𝐿
𝐵 −𝜇𝐵 ) 𝜕(𝜇𝑆 𝐿
𝐵 −𝜇𝐵 )
𝜕𝐶𝑆 𝜕𝐶𝐿 𝐶𝑆
>>> try:
... from scipy.optimize import fsolve
... CsRoot, ClRoot = fsolve(func=equilibrium, x0=[0.5, 0.5],
... fprime=equilibriumJacobian)
... except ImportError:
... ClRoot = CsRoot = 0
... print "The SciPy library is not available to calculate the solidus and \
... liquidus concentrations"
Because the phase field interface will not move, and because we’ve seen in earlier examples that the diffusion problem
is unconditionally stable, we need take only one very large timestep to reach equilibrium
>>> dt = 1.e5
Because the phase field equation is coupled to the composition through enthalpy and W and the diffusion equation
is coupled to the phase field through phaseTransformationVelocity, it is necessary sweep this non-linear
problem to convergence. We use the “residual” of the equations (a measure of how well they think they have solved
the given set of linear equations) as a test for how long to sweep. Because of the ConvectionTerm, the solution
matrix for diffusionEq is asymmetric and cannot be solved by the default LinearPCGSolver. Therefore, we
use a LinearLUSolver for this equation.
We now use the “sweep()” method instead of “solve()” because we require the residual.
>>> solver = LinearLUSolver(tolerance=1e-10)
>>> phase.updateOld()
>>> C.updateOld()
>>> res = 1.
>>> initialRes = None
1
phase
composition
sharp
0.8
0.6
0.4
0.2
0
0 5e-4 1e-3 1.5e-3 2e-3
We verify that the bulk phases have shifted to the predicted solidus and liquidus compositions
>>> X = mesh.faceCenters[0]
>>> print Cs.allclose(C.faceValue[X.value==0], atol=1e-2)
True
>>> print Cl.allclose(C.faceValue[X.value==L], atol=1e-2)
True
Because this lower temperature will induce the phase interface to move (solidify), we will need to take much smaller
timesteps (the time scales of diffusion and of phase transformation compete with each other).
The CFL limit requires that no interface should advect more than one grid spacing in a timestep. We can get a rough
idea for the maximum timestep we can take by looking at the velocity of convection induced by phase transformation
in Eq. (12.2) (even though there is no explicit convection in the coupled form used for this example, the principle
remains the same). If we assume that the phase changes from 1 to 0 in a single grid spacing, that the diffusivity is Dl
at the interface, and that the term due to the difference in barrier heights is negligible:
𝐷𝜑
⃗𝑢𝜑 = ∇𝜑
𝐶 [︃ )︀ ]︃
𝐷𝑙 12 𝑉𝑚 𝐿𝐵 𝑇 − 𝑇𝑀
(︀ 𝐵
)︀ (︀ 𝐴
𝐿𝐴 𝑇 − 𝑇𝑀 1
≈ 𝐵
− 𝐴
𝑅𝑇 𝑇𝑀 𝑇𝑀 ∆𝑥
𝐷𝑙 12 𝑉𝑚 𝑇 𝐴 − 𝑇𝑀
𝐵
1
≈ (𝐿𝐵 + 𝐿𝐴 ) 𝑀𝐴 𝐵
𝑅𝑇 𝑇𝑀 + 𝑇𝑀 ∆𝑥
≈ 0.28 cm/s
To get a CFL = ⃗𝑢𝜑 ∆𝑡/∆𝑥 < 1, we need a time step of about 10−5 s.
>>> dt = 1.e-5
1
phase
composition
sharp
0.8
0.6
0.4
0.2
0
0 5e-4 1e-3 1.5e-3 2e-3
We see that the composition on either side of the interface approach the sharp-interface solidus and liquidus, but it will
take a great many more timesteps to reach equilibrium. If we waited sufficiently long, we could again verify the final
concentrations and phase fraction against the expected values.
12.3 examples.phase.quaternary
>>> nx = 400
>>> dx = 0.01
>>> L = nx * dx
>>> mesh = Grid1D(dx = dx, nx = nx)
interstitial components 𝐶0 . . . 𝐶𝑀
>>> interstitials = [
... CellVariable(mesh=mesh, name='C0', hasOld=1)
... ]
substitutional components 𝐶𝑀 +1 . . . 𝐶𝑁 −1
>>> substitutionals = [
... CellVariable(mesh=mesh, name='C1', hasOld=1),
... CellVariable(mesh=mesh, name='C2', hasOld=1),
... ]
a∑︀“solvent” 𝐶𝑁 that is constrained by the concentrations of the other substitutional species, such that 𝐶𝑁 = 1 −
𝑁 −1
𝑗=𝑀 𝐶𝑗 ,
>>> solvent = 1
>>> for Cj in substitutionals:
... solvent -= Cj
>>> solvent.name = 'CN'
and temperature 𝑇
>>> T = 1000
where
>>> R = 8.314 # J / (mol K)
We consider a very simplified model that has partial molar volumes 𝑉¯0 = · · · = 𝑉¯𝑀 = 0 for the “interstitials” and
𝑉¯𝑀 +1 = · · · = 𝑉¯𝑁 = 1 for the “substitutionals”. This approximation has been used in a number of models where
density effects are ignored, including the treatment of electrons in electrodeposition processes [28] [29]. Under these
constraints
𝑁
𝜕𝑓 ∑︁ 𝜕𝑓𝑗
= 𝐶𝑗
𝜕𝜑 𝑗=0 𝜕𝜑
𝑁
[︂ ]︂
∑︁ 𝑊𝑗 ′
= 𝐶𝑗 𝜇∘𝑆𝐿𝑗 (𝑇 )𝑝′
(𝜑) + 𝑔 (𝜑)
𝑗=0
2
[︂ ]︂
𝜕𝑓 𝐶𝑗
= 𝜇∘𝑗 (𝜑, 𝑇 ) + 𝑅𝑇 ln
𝜕𝐶𝑗 𝜌
= 𝜇𝑗 (𝜑, 𝐶𝑗 , 𝑇 ) for 𝑗 = 0 . . . 𝑀
and
[︂ ]︂ [︂ ]︂
𝜕𝑓 𝐶𝑗 𝐶𝑁
= 𝜇∘𝑗 (𝜑, 𝑇 ) + 𝑅𝑇 ln − 𝜇∘𝑁 (𝜑, 𝑇 ) + 𝑅𝑇 ln
𝜕𝐶𝑗 𝜌 𝜌
= [𝜇𝑗 (𝜑, 𝐶𝑗 , 𝑇 ) − 𝜇𝑁 (𝜑, 𝐶𝑁 , 𝑇 )] for 𝑗 = 𝑀 + 1 . . . 𝑁 − 1
where 𝜇∘𝑆𝐿
𝑗 𝑗 (𝑇 ) − 𝜇𝑗 (𝑇 ) and where 𝜇𝑗 is the classical chemical potential of component 𝑗 for the binary
(𝑇 ) ≡ 𝜇∘𝑆 ∘𝐿
∑︀𝑀
species and 𝜌 = 1 + 𝑗=0 𝐶𝑗 is the total molar density.
>>> rho = 1.
>>> for Cj in interstitials:
... rho += Cj
𝑝′ (𝜑) and 𝑔 ′ (𝜑) are the partial derivatives of of 𝑝 and 𝑔 with respect to 𝜑
>>> def pPrime(phi):
... return 30. * g(phi)
We “cook” the standard potentials to give the desired solid and liquid concentrations, with a solid phase rich in
interstitials and the solvent and a liquid phase rich in the two substitutional species.
>>> interstitials[0].S = 0.3
>>> interstitials[0].L = 0.4
>>> substitutionals[0].S = 0.4
>>> substitutionals[0].L = 0.3
>>> substitutionals[1].S = 0.2
>>> substitutionals[1].L = 0.1
>>> solvent.S = 1.
>>> solvent.L = 1.
>>> for Cj in substitutionals:
... solvent.S -= Cj.S
... solvent.L -= Cj.L
>>> solvent.barrier = R * T
>>> phase.mobility = 1.
>>> phase.gradientEnergy = 25
>>> phase.equation = TransientTerm(coeff=1/phase.mobility) \
... == DiffusionTerm(coeff=phase.gradientEnergy) \
... + S0 + ImplicitSourceTerm(coeff = S1)
We could construct the diffusion equations one-by-one, in the manner of examples.phase.binary, but it is
better to take advantage of the full scripting power of the Python language, where we can easily loop over components
or even make “factory” functions if we desire. For the interstitial diffusion equations, we arrange in canonical form as
before:
𝜕𝐶𝑗
= 𝐷𝑗 ∇2 𝐶𝑗
𝜕𝑡
⏟ ⏞ ⏟ ⏞
transient diffusion
counter diffusion
⎧ ⎫
⎪ phase transformation ⏞ ⏟ ⎪
⎪
⎪ ⏞ [︂ ⏟ ⎪
⎪
⎪ ]︂ 𝑀 ⎪
𝐶𝑗 ⎨ 𝜌 ∘𝑆𝐿 𝑊 𝑗
∑︁ ⎬
+ 𝐷𝑗 ∇ · ∑︀𝑀 𝜇𝑗 ∇𝑝(𝜑) + ∇𝑔(𝜑) − ∇𝐶𝑖
1 + 𝑘=0 𝐶𝑘 ⎪
⎪ 𝑅𝑇 2 𝑖=0
⎪
⎪
𝑘̸=𝑗
⎪ ⎪
⎪
⎩ 𝑖̸=𝑗 ⎪
⎭
⏟ ⏞
convection
𝜕𝐶𝑗
= 𝐷𝑗 ∇2 𝐶𝑗
𝜕𝑡
⏟ ⏞ ⏟ ⏞
transient diffusion
phase transformation counter diffusion
⎧ ⎫
⎪
⎪ ⏞ ⏟ ⏞ ⏟ ⎪
⎪
⎪
⎪ [︂ ]︂ 𝑁 −1
⎪
⎪
𝐶𝑗 ⎨ 𝐶𝑁 (︀ ∘𝑆𝐿 ∘𝑆𝐿 𝑊 𝑗 − 𝑊 𝑁
∑︁ ⎬
+ 𝐷𝑗 ∇ ·
)︀
∑︀𝑁 −1 𝜇𝑗 − 𝜇𝑁 ∇𝑝(𝜑) + ∇𝑔(𝜑) + ∇𝐶𝑖
⎪ 𝑅𝑇
1 − 𝑘=𝑀 +1 𝐶𝑘 ⎪ 2 ⎪
⎪ 𝑖=𝑀 +1 ⎪
⎪
𝑘̸=𝑗 ⎪
⎩ 𝑖̸=𝑗 ⎪
⎭
⏟ ⏞
convection
>>> x = mesh.cellCenters[0]
>>> phase.setValue(1.)
>>> phase.setValue(0., where=x > L / 2)
and with uniform concentration fields, initially equal to the average of the solidus and liquidus concentrations
>>> for Cj in interstitials + substitutionals:
... Cj.setValue((Cj.S + Cj.L) / 2.)
>>> dt = 10000
>>> for i in range(5):
... for field in [phase] + substitutionals + interstitials:
... field.updateOld()
... phase.equation.solve(var = phase, dt = dt)
... for field in substitutionals + interstitials:
... field.equation.solve(var = field,
... dt = dt,
... solver = solver)
... if __name__ == '__main__':
... viewer.plot()
1
phase
C0
C1
C2
CN
0.8
0.6
0.4
0.2
0
0 0.5 1 1.5 2 2.5 3 3.5 4
True
and that the concentration fields have appropriately segregated into their equilibrium values in each phase
>>> equilibrium = True
>>> for Cj in interstitials + substitutionals:
... equilibrium &= numerix.allclose(Cj.faceValue[X.value==0], Cj.S, rtol = 3e-3, atol = 3e-3).val
... equilibrium &= numerix.allclose(Cj.faceValue[X.value==L], Cj.L, rtol = 3e-3, atol = 3e-3).val
>>> print equilibrium
True
12.4 examples.phase.anisotropy
We consider the simultaneous evolution of a “phase field” variable 𝜑 (taken to be 0 in the liquid phase and 1 in the
solid)
>>> phase = CellVariable(name=r'$\phi$', mesh=mesh, hasOld=True)
The hasOld flag causes the storage of the value of variable from the previous timestep. This is necessary for solving
equations with non-linear coefficients or for coupling between PDEs.
The governing equation for the temperature field is the heat flux equation, with a source due to the latent heat of
solidification
𝜕∆𝑇 𝜕𝜑
= 𝐷𝑇 ∇2 ∆𝑇 +
𝜕𝑡 𝜕𝑡
>>> DT = 2.25
>>> heatEq = (TransientTerm()
... == DiffusionTerm(DT)
... + (phase - phase.old) / dt)
1−Φ2 𝜕𝜑/𝜕𝑦
where 𝛽 = 1+Φ2 , 2 𝜓 , 𝜓 = 𝜃 + arctan 𝜕𝜑/𝜕𝑥 , 𝜃 is the orientation, and 𝑁 is the symmetry.
(︀ 𝑁 )︀
Φ = tan
>>> alpha = 0.015
>>> c = 0.02
>>> N = 6.
>>> theta = numerix.pi / 8.
>>> psi = theta + numerix.arctan2(phase.faceGrad[1],
... phase.faceGrad[0])
>>> Phi = numerix.tan(N * psi / 2)
>>> PhiSq = Phi**2
>>> beta = (1. - PhiSq) / (1. + PhiSq)
>>> DbetaDpsi = -N * 2 * Phi / (1 + PhiSq)
>>> Ddia = (1.+ c * beta)
>>> Doff = c * DbetaDpsi
>>> I0 = Variable(value=((1,0), (0,1)))
>>> I1 = Variable(value=((0,-1), (1,0)))
>>> D = alpha**2 * (1.+ c * beta) * (Ddia * I0 + Doff * I1)
With these expressions defined, we can construct the phase field equation as
>>> tau = 3e-4
>>> kappa1 = 0.9
>>> kappa2 = 20.
>>> phaseEq = (TransientTerm(tau)
... == DiffusionTerm(D)
... + ImplicitSourceTerm((phase - 0.5 - kappa1 / numerix.pi * numerix.arctan(kappa2 * dT))
... * (1 - phase)))
and quench the entire simulation domain below the melting point
>>> dT.setValue(-0.5)
In a real solidification process, dendritic branching is induced by small thermal fluctuations along an otherwise smooth
surface, but the granularity of the Mesh is enough “noise” in this case, so we don’t need to explicitly introduce
randomness, the way we did in the Cahn-Hilliard problem.
FiPy’s viewers are utilitarian, striving to let the user see something, regardless of their operating system or installed
packages, so you won’t be able to simultaneously view two fields “out of the box”, but, because all of Python is
accessible and FiPy is object oriented, it is not hard to adapt one of the existing viewers to create a specialized display:
The non-uniform temperature results from the release of latent heat at the solidifying interface. The dendrite arms
grow fastest where the temperature gradient is steepest.
We note that this FiPy simulation is written in about 50 lines of code (excluding the custom viewer), compared with
over 800 lines of (fairly lucid) FORTRAN code used for the figures in [9].
12.5 examples.phase.impingement.mesh40x1
>>> nx = 40
>>> Lx = 2.5 * nx / 100.
>>> dx = Lx / nx
>>> mesh = Grid1D(dx=dx, nx=nx)
This problem simulates the wet boundary that forms between grains of different orientations. The phase equation is
given by
𝜕𝜑
𝜏𝜑 = 𝛼2 ∇2 𝜑 + 𝜑(1 − 𝜑)𝑚1 (𝜑, 𝑇 ) − 2𝑠𝜑|∇𝜃| − 𝜖2 𝜑|∇𝜃|2
𝜕𝑡
where
1
𝑚1 (𝜑, 𝑇 ) = 𝜑 − − 𝑇 𝜑(1 − 𝜑)
2
and the orientation equation is given by
[︂ (︂ )︂ ]︂
2 𝜕𝜃 𝑠
𝑃 (𝜖|∇𝜃|)𝜏𝜃 𝜑 = ∇· 𝜑 2 2
+ 𝜖 ∇𝜃
𝜕𝑡 |∇𝜃|
where
𝜇
𝑃 (𝑤) = 1 − exp (−𝛽𝑤) + exp (−𝛽𝑤)
𝜖
The initial conditions for this problem are set such that 𝜑 = 1 for 0 ≤ 𝑥 ≤ 𝐿𝑥 and
{︃
1 for 0 ≤ 𝑥 < 𝐿𝑥 /2,
𝜃=
0 for 𝐿𝑥 /2 ≤ 𝑥 ≤ 𝐿𝑥 .
Here the phase and orientation equations are solved with an explicit and implicit technique respectively.
The parameters for these equations are
>>> timeStepDuration = 0.02
>>> phaseTransientCoeff = 0.1
>>> thetaSmallValue = 1e-6
>>> beta = 1e5
>>> mu = 1e3
>>> thetaTransientCoeff = 0.01
>>> gamma= 1e3
>>> epsilon = 0.008
>>> s = 0.01
>>> alpha = 0.015
Because theta is an 𝑆 1 -valued variable (i.e. it maps to the circle) and thus intrinsically has 2𝜋-peridocity, we must
use ModularVariable instead of a CellVariable. A ModularVariable confines theta to −𝜋 < 𝜃 ≤ 𝜋
by adding or subtracting 2𝜋 where necessary and by defining a new subtraction operator between two angles.
The left and right halves of the domain are given different orientations.
>>> theta.setValue(0., where=mesh.cellCenters[0] > Lx / 2.)
The source term is linearized in the manner demonstrated in examples.phase.simple (Kobayashi, semi-
implicit).
>>> thetaMag = theta.old.grad.mag
>>> implicitSource = mPhiVar * (phase - (mPhiVar < 0))
>>> implicitSource += (2 * s + epsilon**2 * thetaMag) * thetaMag
The theta equation is built in the following way. The details for this equation are fairly involved, see J.A. Warren et
al.. The main detail is that a source must be added to correct for the discretization of theta on the circle.
>>> phaseMod = phase + ( phase < thetaSmallValue ) * thetaSmallValue
>>> phaseModSq = phaseMod * phaseMod
>>> expo = epsilon * beta * theta.grad.mag
>>> expo = (expo < 100.) * (expo - 100.) + 100.
>>> pFunc = 1. + numerix.exp(-expo) * (mu / epsilon - 1.)
The source term requires the evaluation of the face gradient without the modular operator.
thetagetFaceGradNoMod() evelautes the gradient without modular arithmetic.
>>> thetaGradDiff = theta.faceGrad - theta.faceGradNoMod
>>> sourceCoeff = (diffusionCoeff * thetaGradDiff).divergence
If the example is run interactively, we create viewers for the phase and orientation variables.
The solution is compared with test data. The test data was created with steps = 10 with a FORTRAN code written
by Ryo Kobayashi for phase field modeling. The following code opens the file mesh40x1.gz extracts the data and
compares it with the theta variable.
>>> import os
>>> testData = numerix.loadtxt(os.path.splitext(__file__)[0] + '.gz')
>>> testData = CellVariable(mesh=mesh, value=testData)
>>> print theta.allclose(testData)
1
12.6 examples.phase.impingement.mesh20x20
This defines four solid regions with different orientations. Solidification occurs and then boundary wetting occurs
where the orientation varies.
The parameters for this example are
>>> timeStepDuration = 0.02
>>> phaseTransientCoeff = 0.1
>>> thetaSmallValue = 1e-6
>>> beta = 1e5
>>> mu = 1e3
>>> thetaTransientCoeff = 0.01
>>> gamma= 1e3
>>> epsilon = 0.008
>>> s = 0.01
>>> alpha = 0.015
The orientation is initialized to a uniform value to denote the randomly oriented liquid phase
>>> theta = ModularVariable(
... name='theta',
... mesh=mesh,
... value=-numerix.pi + 0.0001,
... hasOld=1
... )
Four different solid circular domains are created at each corner of the domain with appropriate orientations
>>> x, y = mesh.cellCenters
>>> for a, b, thetaValue in ((0., 0., 2. * numerix.pi / 3.),
... (L, 0., -2. * numerix.pi / 3.),
... (0., L, -2. * numerix.pi / 3. + 0.3),
... (L, L, 2. * numerix.pi / 3.)):
... segment = (x - a)**2 + (y - b)**2 < (L / 2.)**2
... phase.setValue(1., where=segment)
... theta.setValue(thetaValue, where=segment)
The phase equation is built in the following way. The source term is linearized in the manner demonstrated in
examples.phase.simple (Kobayashi, semi-implicit). Here we use a function to build the equation, so that it
can be reused later.
>>> def buildPhaseEquation(phase, theta):
...
... mPhiVar = phase - 0.5 + temperature * phase * (1 - phase)
... thetaMag = theta.old.grad.mag
... implicitSource = mPhiVar * (phase - (mPhiVar < 0))
... implicitSource += (2 * s + epsilon**2 * thetaMag) * thetaMag
...
... return TransientTerm(phaseTransientCoeff) == \
... ExplicitDiffusionTerm(alpha**2) \
... - ImplicitSourceTerm(implicitSource) \
... + (mPhiVar > 0) * mPhiVar * phase
The theta equation is built in the following way. The details for this equation are fairly involved, see J.A. Warren et
al.. The main detail is that a source must be added to correct for the discretization of theta on the circle. The source
term requires the evaluation of the face gradient without the modular operators.
>>> def buildThetaEquation(phase, theta):
...
... phaseMod = phase + ( phase < thetaSmallValue ) * thetaSmallValue
... phaseModSq = phaseMod * phaseMod
... expo = epsilon * beta * theta.grad.mag
... expo = (expo < 100.) * (expo - 100.) + 100.
... pFunc = 1. + numerix.exp(-expo) * (mu / epsilon - 1.)
...
... phaseFace = phase.arithmeticFaceValue
... phaseSq = phaseFace * phaseFace
... gradMag = theta.faceGrad.mag
... eps = 1. / gamma / 10.
... gradMag += (gradMag < eps) * eps
... IGamma = (gradMag > 1. / gamma) * (1 / gradMag - gamma) + gamma
... diffusionCoeff = phaseSq * (s * IGamma + epsilon**2)
...
... thetaGradDiff = theta.faceGrad - theta.faceGradNoMod
... sourceCoeff = (diffusionCoeff * thetaGradDiff).divergence
...
... return TransientTerm(thetaTransientCoeff * phaseModSq * pFunc) == \
... DiffusionTerm(diffusionCoeff) \
... + sourceCoeff
If the example is run interactively, we create viewers for the phase and orientation variables. Rather than viewing the
raw orientation, which is not meaningful in the liquid phase, we weight the orientation by the phase
>>> if __name__ == '__main__':
... phaseViewer = Viewer(vars=phase, datamin=0., datamax=1.)
... thetaProd = -numerix.pi + phase * (theta + numerix.pi)
... thetaProductViewer = Viewer(vars=thetaProd,
... datamin=-numerix.pi, datamax=numerix.pi)
... phaseViewer.plot()
... thetaProductViewer.plot()
The solution will be tested against data that was created with steps = 10 with a FORTRAN code written by
Ryo Kobayashi for phase field modeling. The following code opens the file mesh20x20.gz extracts the data and
compares it with the theta variable.
>>> import os
>>> testData = numerix.loadtxt(os.path.splitext(__file__)[0] + '.gz').flat
The following code shows how to restart a simulation from some saved data. First, reset the variables to their original
values.
>>> phase.setValue(0)
>>> theta.setValue(-numerix.pi + 0.0001)
>>> x, y = mesh.cellCenters
>>> for a, b, thetaValue in ((0., 0., 2. * numerix.pi / 3.),
... (L, 0., -2. * numerix.pi / 3.),
... (0., L, -2. * numerix.pi / 3. + 0.3),
... (L, L, 2. * numerix.pi / 3.)):
... segment = (x - a)**2 + (y - b)**2 < (L / 2.)**2
... phase.setValue(1., where=segment)
... theta.setValue(thetaValue, where=segment)
We confirm that the solution has not yet converged to that given by Ryo Kobayashi’s FORTRAN code:
>>> print theta.allclose(testData)
0
12.7 examples.phase.polyxtal
To convert a liquid material to a solid, it must be cooled to a temperature below its melting point (known as “under-
cooling” or “supercooling”). The rate of solidification is often assumed (and experimentally found) to be proportional
to the undercooling. Under the right circumstances, the solidification front can become unstable, leading to dendritic
patterns. Warren, Kobayashi, Lobkovsky and Carter [9] have described a phase field model (“Allen-Cahn”, “non-
conserved Ginsberg-Landau”, or “model A” of Hohenberg & Halperin) of such a system, including the effects of
discrete crystalline orientations (anisotropy).
We start with a regular 2D Cartesian mesh
>>> from fipy import *
>>> dx = dy = 0.025
>>> if __name__ == "__main__":
... nx = ny = 200
... else:
... nx = ny = 200
>>> mesh = Grid2D(dx=dx, dy=dy, nx=nx, ny=ny)
We consider the simultaneous evolution of a “phase field” variable 𝜑 (taken to be 0 in the liquid phase and 1 in the
solid)
>>> phase = CellVariable(name=r'$\phi$', mesh=mesh, hasOld=True)
The hasOld flag causes the storage of the value of variable from the previous timestep. This is necessary for solving
equations with non-linear coefficients or for coupling between PDEs.
The governing equation for the temperature field is the heat flux equation, with a source due to the latent heat of
solidification
𝜕∆𝑇 𝜕𝜑
= 𝐷𝑇 ∇2 ∆𝑇 + + 𝑐 (𝑇0 − 𝑇 )
𝜕𝑡 𝜕𝑡
>>> DT = 2.25
>>> q = Variable(0.)
>>> T_0 = -0.1
>>> heatEq = (TransientTerm()
... == DiffusionTerm(DT)
... + (phase - phase.old) / dt
... + q * T_0 - ImplicitSourceTerm(q))
represents a source of anisotropy. The coefficient D is an anisotropic diffusion tensor in two dimensions
[︃ ]︃
𝜕𝛽
2 1 + 𝑐𝛽 −𝑐 𝜕𝜓
D = 𝛼 (1 + 𝑐𝛽) 𝜕𝛽
𝑐 𝜕𝜓 1 + 𝑐𝛽
1−Φ2 𝜕𝜑/𝜕𝑦
where 𝛽 = 1+Φ2 , 2 𝜓 , 𝜓 = 𝜃 + arctan 𝜕𝜑/𝜕𝑥 , 𝜃 is the orientation, and 𝑁 is the symmetry.
(︀ 𝑁 )︀
Φ = tan
>>> alpha = 0.015
>>> c = 0.02
>>> N = 4.
With these expressions defined, we can construct the phase field equation as
>>> tau_phase = 3e-4
>>> kappa1 = 0.9
>>> kappa2 = 20.
>>> epsilon = 0.008
>>> s = 0.01
>>> thetaMag = theta.grad.mag
>>> phaseEq = (TransientTerm(tau_phase)
... == DiffusionTerm(D)
... + ImplicitSourceTerm((phase - 0.5 - kappa1 / numerix.pi * numerix.arctan(kappa2 * dT))
... * (1 - phase)
... - (2 * s + epsilon**2 * thetaMag) * thetaMag))
The source term requires the evaluation of the face gradient without the modular operator.
thetagetFaceGradNoMod() evaluates the gradient without modular arithmetic.
>>> thetaEq = (TransientTerm(tau_theta * phaseMod**2 * Pfunc)
... == DiffusionTerm(D_theta)
... + (D_theta * (theta.faceGrad - theta.faceGradNoMod)).divergence)
and quench the entire simulation domain below the melting point
>>> dT.setValue(-0.5)
In a real solidification process, dendritic branching is induced by small thermal fluctuations along an otherwise smooth
surface, but the granularity of the Mesh is enough “noise” in this case, so we don’t need to explicitly introduce
randomness, the way we did in the Cahn-Hilliard problem.
FiPy’s viewers are utilitarian, striving to let the user see something, regardless of their operating system or installed
packages, so you the default color scheme of grain orientation won’t be very informative “out of the box”. Because
all of Python is accessible and FiPy is object oriented, it is not hard to adapt one of the existing viewers to create a
specialized display:
>>> if __name__ == "__main__":
... try:
... class OrientationViewer(Matplotlib2DGridViewer):
... def __init__(self, phase, orientation, title=None, limits={}, **kwlimits):
... self.phase = phase
... Matplotlib2DGridViewer.__init__(self, vars=(orientation,), title=title,
... limits=limits, colorbar=None, **kwlimits)
...
... # make room for non-existent colorbar
... # stolen from matplotlib.colorbar.make_axes
... # https://github.com/matplotlib/matplotlib/blob
... # /ec1cd2567521c105a451ce15e06de10715f8b54d/lib
... # /matplotlib/colorbar.py#L838
... fraction = 0.15
... pb = self.axes.get_position(original=True).frozen()
... pad = 0.05
... x1 = 1.0-fraction
... pb1, pbx, pbcb = pb.splitx(x1-pad, x1)
... panchor = (1.0, 0.5)
... self.axes.set_position(pb1)
... self.axes.set_anchor(panchor)
...
... # make the gnomon
... fig = self.axes.get_figure()
... self.gnomon = fig.add_axes([0.85, 0.425, 0.15, 0.15], polar=True)
... self.gnomon.set_thetagrids([180, 270, 0, 90],
... [r"$\pm\pi$", r"$-\frac{\pi}{2}$", "$0$", r"$+\frac{\p
... frac=1.3)
... self.gnomon.set_theta_zero_location("N")
... self.gnomon.set_theta_direction(-1)
... self.gnomon.set_rgrids([1.], [""])
... N = 100
... theta = numerix.arange(-numerix.pi, numerix.pi, 2 * numerix.pi / N)
... radii = numerix.ones((N,))
... bars = self.gnomon.bar(theta, radii, width=2 * numerix.pi / N, bottom=0.0)
... colors = self._orientation_and_phase_to_rgb(orientation=numerix.array([theta]), p
... for c, t, bar in zip(colors[0], theta, bars):
... bar.set_facecolor(c)
... bar.set_edgecolor(c)
...
... def _reshape(self, var):
... '''return values of var in an 2D array'''
... return numerix.reshape(numerix.array(var),
... var.mesh.shape[::-1])[::-1]
...
... @staticmethod
... def _orientation_and_phase_to_rgb(orientation, phase):
... from matplotlib import colors
...
... hsv = numerix.empty(orientation.shape + (3,))
... hsv[..., 0] = (orientation / numerix.pi + 1) / 2.
... hsv[..., 1] = 1.
... hsv[..., 2] = phase
...
... return colors.hsv_to_rgb(hsv)
...
... @property
... def _data(self):
... '''convert phase and orientation to rgb image array
...
... orientation (-pi, pi) -> hue (0, 1)
... phase (0, 1) -> value (0, 1)
... '''
... orientation = self._reshape(self.vars[0])
... phase = self._reshape(self.phase)
...
... return self._orientation_and_phase_to_rgb(orientation, phase)
...
... def _plot(self):
... self.image.set_data(self._data)
...
... from matplotlib import pyplot
... pyplot.ion()
... w, h = pyplot.figaspect(1.)
... fig = pyplot.figure(figsize=(2*w, h))
... timer = fig.text(0.1, 0.9, "t = %.3f" % 0, fontsize=18)
...
... viewer = MultiViewer(viewers=(MatplotlibViewer(vars=dT,
... cmap=pyplot.cm.hot,
... datamin=-0.5,
... datamax=0.5,
... axes=fig.add_subplot(121)),
... OrientationViewer(phase=phase,
... orientation=theta,
... title=theta.name,
... axes=fig.add_subplot(122))))
... except ImportError:
... viewer = MultiViewer(viewers=(Viewer(vars=dT,
... datamin=-0.5,
... datamax=0.5),
... Viewer(vars=phase,
... datamin=0.,
... datamax=1.),
... Viewer(vars=theta,
... datamin=-numerix.pi,
... datamax=numerix.pi)))
... viewer.plot()
The non-uniform temperature results from the release of latent heat at the solidifying interface. The dendrite arms
grow fastest where the temperature gradient is steepest.
12.8 examples.phase.polyxtalCoupled
Simultaneously solve the dendritic growth of nuclei and subsequent grain impingement.
To convert a liquid material to a solid, it must be cooled to a temperature below its melting point (known as “under-
cooling” or “supercooling”). The rate of solidification is often assumed (and experimentally found) to be proportional
to the undercooling. Under the right circumstances, the solidification front can become unstable, leading to dendritic
patterns. Warren, Kobayashi, Lobkovsky and Carter [9] have described a phase field model (“Allen-Cahn”, “non-
conserved Ginsberg-Landau”, or “model A” of Hohenberg & Halperin) of such a system, including the effects of
discrete crystalline orientations (anisotropy).
We start with a regular 2D Cartesian mesh
>>> from fipy import *
>>> dx = dy = 0.025
>>> if __name__ == "__main__":
... nx = ny = 200
... else:
... nx = ny = 200
>>> mesh = Grid2D(dx=dx, dy=dy, nx=nx, ny=ny)
We consider the simultaneous evolution of a “phase field” variable 𝜑 (taken to be 0 in the liquid phase and 1 in the
solid)
>>> phase = CellVariable(name=r'$\phi$', mesh=mesh, hasOld=True)
The hasOld flag causes the storage of the value of variable from the previous timestep. This is necessary for solving
equations with non-linear coefficients or for coupling between PDEs.
The governing equation for the temperature field is the heat flux equation, with a source due to the latent heat of
solidification
𝜕∆𝑇 𝜕𝜑
= 𝐷𝑇 ∇2 ∆𝑇 + + 𝑐 (𝑇0 − 𝑇 )
𝜕𝑡 𝜕𝑡
>>> DT = 2.25
>>> q = Variable(0.)
>>> T_0 = -0.1
>>> heatEq = (TransientTerm(var=dT)
... == DiffusionTerm(coeff=DT, var=dT)
... + TransientTerm(var=phase)
... + q * T_0 - ImplicitSourceTerm(coeff=q, var=dT))
𝜕𝜑
𝜏𝜑 = ∇ · D∇𝜑 + 𝜑(1 − 𝜑)𝑚(𝜑, ∆𝑇 ) − 2𝑠𝜑|∇𝜃| − 𝜖2 𝜑|∇𝜃|2
𝜕𝑡
where
1 𝜅1
𝑚(𝜑, ∆𝑇 ) = 𝜑 − − arctan (𝜅2 ∆𝑇 )
2 𝜋
represents a source of anisotropy. The coefficient D is an anisotropic diffusion tensor in two dimensions
[︃ ]︃
𝜕𝛽
2 1 + 𝑐𝛽 −𝑐 𝜕𝜓
D = 𝛼 (1 + 𝑐𝛽) 𝜕𝛽
𝑐 𝜕𝜓 1 + 𝑐𝛽
1−Φ2 𝜕𝜑/𝜕𝑦
where 𝛽 = 1+Φ2 , 2 𝜓 , 𝜓 = 𝜃 + arctan 𝜕𝜑/𝜕𝑥 , 𝜃 is the orientation, and 𝑁 is the symmetry.
(︀ 𝑁 )︀
Φ = tan
>>> alpha = 0.015
>>> c = 0.02
>>> N = 4.
With these expressions defined, we can construct the phase field equation as
>>> tau_phase = 3e-4
>>> kappa1 = 0.9
The source term requires the evaluation of the face gradient without the modular operator.
thetagetFaceGradNoMod() evaluates the gradient without modular arithmetic.
>>> thetaEq = (TransientTerm(coeff=tau_theta * phaseMod**2 * Pfunc, var=theta)
... == DiffusionTerm(coeff=D_theta, var=theta)
... + PowerLawConvectionTerm(coeff=v_theta * (theta.faceGrad - theta.faceGradNoMod), var=p
and quench the entire simulation domain below the melting point
>>> dT.setValue(-0.5)
In a real solidification process, dendritic branching is induced by small thermal fluctuations along an otherwise smooth
surface, but the granularity of the Mesh is enough “noise” in this case, so we don’t need to explicitly introduce
randomness, the way we did in the Cahn-Hilliard problem.
FiPy’s viewers are utilitarian, striving to let the user see something, regardless of their operating system or installed
packages, so you the default color scheme of grain orientation won’t be very informative “out of the box”. Because
all of Python is accessible and FiPy is object oriented, it is not hard to adapt one of the existing viewers to create a
specialized display:
>>> if __name__ == "__main__":
... try:
... class OrientationViewer(Matplotlib2DGridViewer):
... def __init__(self, phase, orientation, title=None, limits={}, **kwlimits):
... self.phase = phase
... Matplotlib2DGridViewer.__init__(self, vars=(orientation,), title=title,
... limits=limits, colorbar=None, **kwlimits)
...
... # make room for non-existent colorbar
... # stolen from matplotlib.colorbar.make_axes
... # https://github.com/matplotlib/matplotlib/blob
... # /ec1cd2567521c105a451ce15e06de10715f8b54d/lib
... # /matplotlib/colorbar.py#L838
... fraction = 0.15
... pb = self.axes.get_position(original=True).frozen()
... pad = 0.05
... x1 = 1.0-fraction
... pb1, pbx, pbcb = pb.splitx(x1-pad, x1)
... panchor = (1.0, 0.5)
... self.axes.set_position(pb1)
... self.axes.set_anchor(panchor)
...
... # make the gnomon
... fig = self.axes.get_figure()
... self.gnomon = fig.add_axes([0.85, 0.425, 0.15, 0.15], polar=True)
... self.gnomon.set_thetagrids([180, 270, 0, 90],
... [r"$\pm\pi$", r"$-\frac{\pi}{2}$", "$0$", r"$+\frac{\p
... frac=1.3)
... self.gnomon.set_theta_zero_location("N")
... self.gnomon.set_theta_direction(-1)
... self.gnomon.set_rgrids([1.], [""])
... N = 100
... theta = numerix.arange(-numerix.pi, numerix.pi, 2 * numerix.pi / N)
... radii = numerix.ones((N,))
... bars = self.gnomon.bar(theta, radii, width=2 * numerix.pi / N, bottom=0.0)
... colors = self._orientation_and_phase_to_rgb(orientation=numerix.array([theta]), p
... for c, t, bar in zip(colors[0], theta, bars):
... bar.set_facecolor(c)
... bar.set_edgecolor(c)
...
... def _reshape(self, var):
... '''return values of var in an 2D array'''
... return numerix.reshape(numerix.array(var),
... var.mesh.shape[::-1])[::-1]
...
... @staticmethod
... def _orientation_and_phase_to_rgb(orientation, phase):
... total_time = dt * 10
>>> elapsed = 0.
>>> save_interval = 0.002
>>> save_at = save_interval
The non-uniform temperature results from the release of latent heat at the solidifying interface. The dendrite arms
grow fastest where the temperature gradient is steepest.
13.1 examples.levelSet.distanceFunction.mesh1D
𝜕𝜑
=1
𝜕𝑥
with the boundary condition, 𝜑 = 0 at 𝑥 = 𝐿/2.
The solution to this problem will be demonstrated in the following script. Firstly, setup the parameters.
>>> from fipy import *
>>> dx = 0.5
>>> nx = 10
Once the initial positive and negative regions have been initialized the calcDistanceFunction() method can be used to
recalculate var as a distance function from the zero level set.
>>> var.calcDistanceFunction()
The problem can then be solved by executing the solve() method of the equation.
139
FiPy Manual, Release 3.1.3
13.2 examples.levelSet.distanceFunction.circle
|∇𝜑| = 1
and the boundary condition for a circle is given by, 𝜑 = 0 at (𝑥 − 𝐿/2)2 + (𝑦 − 𝐿/2)2 = (𝐿/4)2 .
The solution to this problem will be demonstrated in the following script. Firstly, setup the parameters.
>>> from fipy import *
>>> dx = 1.
>>> dy = 1.
>>> nx = 11
>>> ny = 11
>>> Lx = nx * dx
>>> Ly = ny * dy
>>> x, y = mesh.cellCenters
>>> var.setValue(1, where=(x - Lx / 2.)**2 + (y - Ly / 2.)**2 < (Lx / 4.)**2)
>>> var.calcDistanceFunction(order=1)
13.3 examples.levelSet.advection.mesh1D
Solve the distance function equation in one dimension and then advect it.
This example first solves the distance function equation in one dimension:
|∇𝜑| = 1
with 𝜑 = 0 at 𝑥 = 𝐿/5.
The variable is then advected with,
𝜕𝜑
+ ⃗𝑢 · ∇𝜑 = 0
𝜕𝑡
The scheme used in the FirstOrderAdvectionTerm preserves the var as a distance function.
The solution to this problem will be demonstrated in the following script. Firstly, setup the parameters.
>>> from fipy import *
>>> velocity = 1.
>>> dx = 1.
>>> nx = 10
>>> timeStepDuration = 1.
>>> steps = 2
>>> L = nx * dx
>>> interfacePosition = L / 5.
13.4 examples.levelSet.advection.circle
>>> L = 1.
>>> N = 25
>>> velocity = 1.
>>> cfl = 0.1
>>> velocity = 1.
>>> distanceToTravel = L / 10.
>>> radius = L / 4.
>>> dL = L / N
>>> timeStepDuration = cfl * dL / velocity
>>> steps = int(distanceToTravel / dL / cfl)
If the advection equation is built with the AdvectionTerm() the result is more accurate,
>>> var.setValue(initialArray)
>>> advEqn = TransientTerm() + AdvectionTerm(velocity)
>>> for step in range(steps):
... var.updateOld()
... advEqn.solve(var, dt=timeStepDuration)
>>> solution = numerix.where(answer < 0., -1001., numerix.array(var))
>>> numerix.allclose(answer, solution, atol=1.02e-3)
1
State of the art manufacturing of semiconductor devices involves the electrodeposition of copper for on-chip wiring
of integrated circuits. In the Damascene process interconnects are fabricated by first patterning trenches in a dielectric
medium and then filling by metal electrodeposition over the entire wafer surface. This metalization process, pioneered
by IBM, depends on the use of electrolyte additives that effect the local metal deposition rate.
13.5.2 Superfill
The additives in the electrolyte affect the local deposition rate in such a way that bottom-up filling occurs in trenches
or vias. This process, known as superconformal electrodeposition or superfill, is demonstrated in the following figure.
The figure shows sequential images of bottom-up superfilling of submicrometer trenches by copper deposition from
an electrolyte containing PEG-SPS-Cl. Preferential metal deposition at the bottom of the trenches followed by bump
formation above the filled trenches is evident.
•As a function of TIME
This process has been demonstrated to depend critically on the inclusion of additives in the electrolyte. Recent publi-
cations propose Curvature Enhanced Accelerator Coverage (CEAC) as the mechanism behind the superfilling process
[8]. In this mechanism, molecules that accelerate local metal deposition displace molecules that inhibit local metal
deposition on the metal/electrolyte interface. For electrolytes that yield superconformal filling of fine features, this
buildup happens relatively slowly because the concentration of accelerator species is muchFig.more
1
dilute compared to
the inhibitor species in the electrolyte. The mechanism that leads to the increased rate of metal deposition along the
bottom of the filling trench is the concurrent local increase of the accelerator coverage due to decreasing local surface
area, which scales with the local curvature (hence the name of the mechanism). A good overview of this mechanism
can be found in [32].
Example ?? provides a simple way to use FiPy to model the superfill process. The example includes a detailed
description of the governing equations and feature geometry. It requires the user to import and execute a function at
the python prompt. The model parameters can be passed as arguments to this function. In future all superfill examples
will be provided with this type of interface. Example ?? has the same functionality as ?? but demonstrates how to
write a new script in the case where the existing suite of scripts do not meet the required needs.
In general it is a good idea to obtain the Mayavi plotting package for which a specialized superfill viewer class has been
created, see Installation. The other standard viewers mentioned in Installation are still adequate although they do not
give such clear images that are tailored for the superfill problem. The images below demonstrate the Mayavi viewing
capability. Each contour represents sequential positions of the interface and the color represents the concentration of
accelerator as a surfactant. The areas of high surfactant concentration have an increased deposition rate.
13.6 examples.levelSet.electroChem.simpleTrenchSystem
at the command line. The results of the simulation will be displayed and the word finished in the terminal at the end
of the simulation. To run with a different number of time steps change the numberOfSteps argument as follows,
>>> runSimpleTrenchSystem(numberOfSteps=2, displayViewers=False)
1
Change the displayViewers argument to True if you wish to see the results displayed on the screen. Example
examples.levelSet.electroChem.simpleTrenchSystem gives explanation for writing new scripts or
modifying existing scripts that are encapsulated by functions.
Any argument parameter can be changed. For example if the initial catalyst coverage is not 0, then it can be reset,
>>> runSimpleTrenchSystem(numberOfSteps=2, catalystCoverage=0.1, displayViewers=False)
0
The following image shows a schematic of a trench geometry along with the governing equations for mod-
eling electrodeposition with the CEAC mechanism. All of the given equations are implemented in the
examples.levelSet.electroChem.simpleTrenchSystem.runSimpleTrenchSystem() function.
As stated above, all the parameters in the equations can be changed with function arguments.
S
cm = c∞
m cθ = c∞
θ
The following table shows the symbols used in the governing equations and their corresponding arguments to the
runSimpleTrenchSystem() function. The boundary layer depth is intentionally small in this example in order
not to complicate the mesh. Further examples will simulate more realistic boundary layer depths but will also have
more complex meshes requiring the gmsh software.
If the MayaVi plotting software is installed (see Installation) then a plot should appear that is updated every 20 time
steps and will eventually resemble the image below.
13.7 examples.levelSet.electroChem.gold
at the command line. The results of the simulation will be displayed and the word finished in the terminal at the
end of the simulation. The simulation will only run for 10 time steps. To run with a different number of time steps
change the numberOfSteps argument as follows,
>>> runGold(numberOfSteps=10, displayViewers=False)
1
Change the displayViewers argument to True if you wish to see the results displayed on the screen. This
example has a more realistic default boundary layer depth and thus requires gmsh to construct a more complex mesh.
There are a few differences between the gold superfill model presented in this example and in
examples.levelSet.electroChem.simpleTrenchSystem. Most default values have changed to account
for a different metal ion (gold) and catalyst (lead). In this system the catalyst is not present in the electrolyte but in-
stead has a non-zero initial coverage. Thus quantities associated with bulk catalyst and catalyst accumulation are not
defined. The current density is given by,
𝑐𝑚
𝑖= (𝑏0 + 𝑏1 𝜃) .
𝑐∞
𝑚
The more common representation of the current density includes an exponential part. Here it is buried in 𝑏0 and 𝑏1 .
The governing equation for catalyst evolution includes a term for catalyst consumption on the interface and is given
by
𝜃˙ = 𝐽𝑣𝜃 − 𝑘𝑐 𝑣𝜃
where 𝑘𝑐 is the consumption coefficient (consumptionRateConstant). The trench geometry is also given a
slight taper, given by taperAngle.
If the MayaVi plotting software is installed (see Installation) then a plot should appear that is updated every 10 time
steps and will eventually resemble the image below.
13.8 examples.levelSet.electroChem.leveler
$ python examples/levelSet/electroChem/leveler.py
at the command line. The results of the simulation will be displayed and the word finished in the terminal at the
end of the simulation. The simulation will only run for 200 time steps. To run with a different number of time steps
change the numberOfSteps argument as follows,
>>> runLeveler(numberOfSteps=10, displayViewers=False, cellSize=0.25e-7)
1
Change the displayViewers argument to True if you wish to see the results displayed on the screen. This
example requires gmsh to construct the mesh.
This example models the case when suppressor, accelerator and leveler additives are present in the electrolyte. The
suppressor is assumed to absorb quickly compared with the other additives. Any unoccupied surface sites are im-
mediately covered with suppressor. The accelerator additive has more surface affinity than suppressor and is thus
preferential adsorbed. The accelerator can also remove suppressor when the surface reaches full coverage. Similarly,
the leveler additive has more surface affinity than both the suppressor and accelerator. This forms a simple set of
assumptions for understanding the behavior of these additives.
The following is a complete description of the equations for the model described here. Any equations that have been
omitted are the same as those given in examples.levelSet.electroChem.simpleTrenchSystem. The
current density is governed by
[︂ (︂ )︂]︂
𝑐𝑚 ∑︁ −𝛼𝑗 𝐹 𝜂 (1 − 𝛼𝑗 ) 𝐹 𝜂
𝑖= ∞ 𝑖𝑗 𝜃𝑗 exp − exp
𝑐𝑚 𝑗 𝑅𝑇 𝑅𝑇
where 𝑗 represents 𝑆 for suppressor, 𝐴 for accelerator, 𝐿 for leveler and 𝑉 for vacant. This model assumes a linear
interpolation between the three cases of complete coverage for each additive or vacant substrate. The governing
equations for the surfactants are given by,
+ + −𝛼𝑘 𝐹 𝜂
𝑘𝐴 = 𝑘𝐴0 exp ,
𝑅𝑇
− 𝐴
𝑘𝐴 = 𝐵𝑑 + + exp (𝐵𝑏 (𝜂 + 𝑉𝑑 ))
exp (𝐵𝑎 (𝜂 + 𝑉𝑑 ))
𝑞 = 𝑚𝜂 + 𝑏.
The following table shows the symbols used in the governing equations and their corresponding arguments for the
runLeveler() function.
The following images show accelerator and leveler contour plots that can be obtained by running this example.
13.9 examples.levelSet.electroChem.howToWriteAScript
at the command line. The results of the simulation will be displayed and the word finished in the terminal at the
end of the simulation. To obtain this example in a plain script file in order to edit and run type:
$ python setup.py copy_script --From examples/levelSet/electroChem/howToWriteAScript.py --To myScript
in the base FiPy directory. The file myScript.py will contain the script.
The following is an explicit explanation of the input commands required to set up and run the problem. At the top of
the file all the parameter values are set. Their use will be explained during the instantiation of various objects and are
the same as those explained in examples.levelSet.electroChem.simpleTrenchSystem.
The following parameters (all in S.I. units) represent,
• physical constants,
The hydrodynamic boundary layer depth (boundaryLayerDepth) is intentionally small in this example to keep
the mesh at a reasonable size.
Build the mesh:
>>> from fipy.tools.parser import parse
>>> numberOfElements = parse('--numberOfElements', action='store',
... type='int', default=-1)
>>> numberOfSteps = parse('--numberOfSteps', action='store',
... type='int', default=2)
The electrolyte region will be the positive region of the domain while the metal region will be negative.
>>> bottomHeight = cellsBelowTrench * cellSize
>>> trenchHeight = bottomHeight + trenchDepth
>>> trenchWidth = trenchDepth / aspectRatio
>>> sideWidth = (trenchSpacing - trenchWidth) / 2
>>> x, y = mesh.cellCenters
>>> distanceVar.setValue(1., where=(y > trenchHeight)
... | ((y > bottomHeight)
... & (x < xCells * cellSize - sideWidth)))
>>> distanceVar.calcDistanceFunction(order=2)
The distanceVariable has now been created to mark the interface. Some other variables need to be created that
govern the concentrations of various species.
Create the catalyst surfactant coverage, 𝜃, variable. This variable influences the deposition rate.
>>> catalystVar = SurfactantVariable(
... name="catalyst variable",
... value=catalystCoverage,
... distanceVar=distanceVar)
... mesh=mesh,
... value=catalystConcentration)
Build the extension velocity variable 𝑣ext . The extension velocity uses the extensionEquation to spread the
velocity at the interface to the rest of the domain.
>>> extensionVelocityVariable = CellVariable(
... name='extension velocity',
... mesh=mesh,
... value=depositionRateVariable)
Using the variables created above the governing equations will be built. The governing equation for surfactant conser-
vation is given by,
𝜃˙ = 𝐽𝑣𝜃 + 𝑘𝑐𝑖𝜃 (1 − 𝜃)
where 𝜃 is the coverage of catalyst at the interface, 𝐽 is the curvature of the interface, 𝑣 is the normal velocity of the
interface, 𝑐𝑖𝜃 is the concentration of catalyst in the bulk at the interface. The value 𝑘 is given by an empirical function
of overpotential,
𝑘 = 𝑘0 + 𝑘3 𝜂 3
The above equation is represented by the AdsorbingSurfactantEquation in FiPy:
The diffusion of metal ions from the far field to the interface is governed by,
𝜕𝑐𝑚
= ∇ · 𝐷∇𝑐𝑚
𝜕𝑡
where,
{︃
𝐷𝑚 when 𝜑 > 0,
𝐷=
0 when 𝜑 ≤ 0
The surfactant bulk diffusion equation solves the bulk diffusion of a species with a source term for the jump from the
bulk to an interface. The governing equation is given by,
𝜕𝑐
= ∇ · 𝐷∇𝑐
𝜕𝑡
where,
{︃
𝐷𝜃 when 𝜑 > 0
𝐷=
0 when 𝜑 ≤ 0
The jump condition at the interface is defined by Langmuir adsorption. Langmuir adsorption essentially states that the
ability for a species to jump from an electrolyte to an interface is proportional to the concentration in the electrolyte,
available site density and a jump coefficient. The boundary condition at 𝜑 = 0 is given by,
𝑛 · ∇𝑐 = −𝑘𝑐(1 − 𝜃).
𝐷ˆ
The surfactant bulk diffusion equation is set up with the following commands.
The levelSetUpdateFrequency defines how often to call the distanceEquation to reinitialize the
distanceVariable to a distance function.
>>> levelSetUpdateFrequency = int(0.8 * narrowBandWidth \
... / (cellSize * cflNumber * 2))
The following loop runs for numberOfSteps time steps. The time step is calculated with the CFL number and the
maximum extension velocity. 𝑣 to 𝑣ext throughout the whole domain using ∇𝜑 · ∇𝑣ext = 0.
>>> for step in range(numberOfSteps):
...
... if viewer is not None:
... viewer.plot()
...
... if step % levelSetUpdateFrequency == 0:
... distanceVar.calcDistanceFunction(order=2)
...
... extensionVelocityVariable.setValue(depositionRateVariable())
...
... distanceVar.updateOld()
... distanceVar.extendVariable(extensionVelocityVariable, order=2)
... dt = cflNumber * cellSize / extensionVelocityVariable.max()
... advectionEquation.solve(distanceVar, dt=dt)
... surfactantEquation.solve(catalystVar, dt=dt)
... metalEquation.solve(var=metalVar, dt=dt)
... bulkCatalystEquation.solve(var=bulkCatalystVar, dt=dt, solver=GeneralSolver())
The following is a short test case. It uses saved data from a simulation with 5 time steps. It is not a test for accuracy
14.1 examples.cahnHilliard.mesh2DCoupled
where 𝜑 is a conserved order parameter, possibly representing alloy composition or spin. The double-well free energy
function 𝑓 = (𝑎2 /2)𝜑2 (1 − 𝜑)2 penalizes states with intermediate values of 𝜑 between 0 and 1. The gradient energy
term 𝜖2 ∇2 𝜑, on the other hand, penalizes sharp changes of 𝜑. These two competing effects result in the segregation of
𝜑 into domains of 0 and 1, separated by abrupt, but smooth, transitions. The parameters 𝑎 and 𝜖 determine the relative
weighting of the two effects and 𝐷 is a rate constant.
We can simulate this process in FiPy with a simple script:
>>> from fipy import *
(Note that all of the functionality of NumPy is imported along with FiPy, although much is augmented for FiPy‘s
needs.)
>>> if __name__ == "__main__":
... nx = ny = 20
... else:
... nx = ny = 10
>>> mesh = Grid2D(nx=nx, ny=ny, dx=0.25, dy=0.25)
>>> phi = CellVariable(name=r"$\phi$", mesh=mesh)
>>> psi = CellVariable(name=r"$\psi$", mesh=mesh)
161
FiPy Manual, Release 3.1.3
We factor the Cahn-Hilliard equation into two 2nd-order PDEs and place them in canonical form for FiPy to solve
them as a coupled set of equations.
𝜕𝜑
= ∇ · 𝐷∇𝜓
𝜕𝑡
𝜕2𝑓 𝜕𝑓
𝜓= (𝜑 − 𝜑old ) + − 𝜖2 ∇2 𝜑
𝜕𝜑2 𝜕𝜑
We need to perform the partial derivatives
𝜕𝑓
= (𝑎2 /2)2𝜑(1 − 𝜑)(1 − 2𝜑)
𝜕𝜑
𝜕2𝑓
= (𝑎2 /2)2 [1 − 6𝜑(1 − 𝜑)]
𝜕𝜑2
manually.
>>> D = a = epsilon = 1.
>>> dfdphi = a**2 * 2 * phi * (1 - phi) * (1 - 2 * phi)
>>> dfdphi_ = a**2 * 2 * (1 - phi) * (1 - 2 * phi)
>>> d2fdphi2 = a**2 * 2 * (1 - 6 * phi * (1 - phi))
>>> eq1 = (TransientTerm(var=phi) == DiffusionTerm(coeff=D, var=psi))
>>> eq2 = (ImplicitSourceTerm(coeff=1., var=psi)
... == ImplicitSourceTerm(coeff=-d2fdphi2, var=phi) - d2fdphi2 * phi + dfdphi
... - DiffusionTerm(coeff=epsilon**2, var=phi))
>>> eq3 = (ImplicitSourceTerm(coeff=1., var=psi)
... == ImplicitSourceTerm(coeff=dfdphi_, var=phi)
... - DiffusionTerm(coeff=epsilon**2, var=phi))
Because the evolution of a spinodal microstructure slows with time, we use exponentially increasing time steps to keep
the simulation “interesting”. The FiPy user always has direct control over the evolution of their problem.
>>> dexp = -5
>>> elapsed = 0.
>>> if __name__ == "__main__":
... duration = .5e-1
... else:
... duration = .5e-1
These equations can also be solved in FiPy using a vector equation. The variables 𝜑 and 𝜓 are now stored in a single
variable
>>> var = CellVariable(mesh=mesh, elementshape=(2,))
>>> var[0] = noise
>>> D = a = epsilon = 1.
>>> v0 = var[0]
>>> dfdphi = a**2 * 2 * v0 * (1 - v0) * (1 - 2 * v0)
>>> dfdphi_ = a**2 * 2 * (1 - v0) * (1 - 2 * v0)
>>> d2fdphi2 = a**2 * 2 * (1 - 6 * v0 * (1 - v0))
The source terms have to be shaped correctly for a vector. The implicit source coefficient has to have a shape of (2, 2)
while the explicit source has a shape (2,)
>>> source = (- d2fdphi2 * v0 + dfdphi) * (0, 1)
>>> impCoeff = -d2fdphi2 * ((0, 0),
... (1., 0)) + ((0, 0),
... (0, -1.))
This is the same equation as the previous definition of eq, but now in a vector format.
>>> eq = TransientTerm(((1., 0.), 1
... (0., 0.))) == DiffusionTerm([((0., D),
... (-epsilon**2, 0.))]) + ImplicitSourceTerm(impCo
>>> dexp = -5
>>> elapsed = 0.
14.2 examples.cahnHilliard.sphere
FiPy doesn’t plot or output anything unless you tell it to: If MayaviClient is available, we can customize the view
with a sublcass of MayaviDaemon.
>>> if __name__ == "__main__":
... try:
... viewer = MayaviClient(vars=phi,
... datamin=0., datamax=1.,
... daemon_file="examples/cahnHilliard/sphereDaemon.py")
... except:
... viewer = Viewer(vars=phi,
... datamin=0., datamax=1.,
... xmin=-2.5, zmax=2.5)
For FiPy, we need to perform the partial derivative 𝜕𝑓 /𝜕𝜑 manually and then put the equation in the canonical form
by decomposing the spatial derivatives so that each Term is of a single, even order:
𝜕𝜑
= ∇ · 𝐷𝑎2 [1 − 6𝜑 (1 − 𝜑)] ∇𝜑 − ∇ · 𝐷∇𝜖2 ∇2 𝜑.
𝜕𝑡
FiPy would automatically interpolate D * a**2 * (1 - 6 * phi * (1 - phi)) onto the faces, where the
diffusive flux is calculated, but we obtain somewhat more accurate results by performing a linear interpolation from
phi at cell centers to PHI at face centers. Some problems benefit from non-linear interpolations, such as harmonic or
geometric means, and FiPy makes it easy to obtain these, too.
>>> PHI = phi.arithmeticFaceValue
>>> D = a = epsilon = 1.
>>> eq = (TransientTerm()
... == DiffusionTerm(coeff=D * a**2 * (1 - 6 * PHI * (1 - PHI)))
... - DiffusionTerm(coeff=(D, epsilon**2)))
Because the evolution of a spinodal microstructure slows with time, we use exponentially increasing time steps to keep
the simulation “interesting”. The FiPy user always has direct control over the evolution of their problem.
>>> dexp = -5
>>> elapsed = 0.
>>> if __name__ == "__main__":
... duration = 1000.
... else:
... duration = 1e-2
>>> while elapsed < duration:
... dt = min(100, numerix.exp(dexp))
... elapsed += dt
... dexp += 0.01
... eq.solve(phi, dt=dt, solver=DefaultSolver(precon=None))
... if __name__ == "__main__":
... viewer.plot()
15.1 examples.flow.stokesCavity
∇𝜇 · ∇⃗𝑢 = ∇𝑝
∇ · ⃗𝑢 = 0
where ⃗𝑢 is the fluid velocity, 𝑝 is the pressure and 𝜇 is the viscosity. The domain in this example is a square cavity
of unit dimensions with a moving lid of unit speed. This example uses the SIMPLE algorithm with Rhie-Chow
interpolation for collocated grids to solve the pressure-momentum coupling. Some of the details of the algorithm will
be highlighted below but a good reference for this material is Ferziger and Peric [33] and Rossow [rossow:2003]. The
solution has a high degree of error close to the corners of the domain for the pressure but does a reasonable job of
predicting the velocities away from the boundaries. A number of aspects of FiPy need to be improved to have a first
class flow solver. These include, higher order spatial diffusion terms, proper wall boundary conditions, improved mass
flux evaluation and extrapolation of cell values to the boundaries using gradients.
In the table below a comparison is made with the Dolfyn open source code on a 100 by 100 grid. The table shows the
frequency of values that fall within the given error confidence bands. Dolfyn has the added features described above.
When these features are switched off the results of Dolfyn and FiPy are identical.
% frequency of cells x-velocity error (%) y-velocity error (%) pressure error (%)
90 < 0.1 < 0.1 <5
5 0.1 to 0.6 0.1 to 0.3 5 to 11
4 0.6 to 7 0.3 to 4 11 to 35
1 7 to 96 4 to 80 35 to 179
0 > 96 > 80 > 179
To start, some parameters are declared.
>>> from fipy import *
>>> #from fipy.meshes.grid2D import Grid2D
167
FiPy Manual, Release 3.1.3
>>> L = 1.0
>>> N = 50
>>> dL = L / N
>>> viscosity = 1
>>> U = 1.
>>> #0.8 for pressure and 0.5 for velocity are typical relaxation values for SIMPLE
>>> pressureRelaxation = 0.8
>>> velocityRelaxation = 0.5
>>> if __name__ == '__main__':
... sweeps = 300
... else:
... sweeps = 5
The velocity is required as a rank-1 FaceVariable for calculating the mass flux. This is required by the Rhie-Chow
correction to avoid pressure/velocity decoupling.
>>> velocity = FaceVariable(mesh=mesh, rank=1)
In this example the SIMPLE algorithm is used to couple the pressure and momentum equations. Let us assume we
have solved the discretized momentum equations using a guessed pressure field 𝑝* to obtain a velocity field ⃗𝑢* . That
is ⃗𝑢* is found from
∑︁
𝑎𝑃 ⃗𝑢*𝑃 = 𝑎𝐴 ⃗𝑢*𝐴 − 𝑉𝑃 (∇𝑝* )𝑃
𝑓
We would like to somehow correct these initial fields to satisfy both the discretized momentum and continuity equa-
tions. We now try to correct these initial fields with a correction such that ⃗𝑢 = ⃗𝑢* + ⃗𝑢′ and 𝑝 = 𝑝* + 𝑝′ , where ⃗𝑢 and
𝑝 now satisfy the momentum and continuity equations. Substituting the exact solution into the equations we obtain,
∇𝜇 · ∇⃗𝑢′ = 𝑝⃗′
and
∇ · ⃗𝑢* + ∇ · ⃗𝑢′ = 0
We now use the discretized form of the equations to write the velocity correction in terms of the pressure correction.
The discretized form of the above equation results in an equation for 𝑝 = 𝑝′ ,
∑︁
𝑎𝑃 ⃗𝑢′𝑃 = 𝑎𝐴 ⃗𝑢′𝐴 − 𝑉𝑃 (∇𝑝′ )𝑃
𝑓
where notation from Linear Equations is used. The SIMPLE algorithm drops the second term in the above equation
to leave,
𝑉𝑃 (∇𝑝′ )𝑃
⃗𝑢′𝑃 = −
𝑎𝑃
By substituting the above expression into the continuity equations we obtain the pressure correction equation,
𝑉𝑃
∇ · ∇𝑝′ = ∇ · ⃗𝑢*
𝑎𝑃
In the discretized version of the above equation 𝑉𝑃 /𝑎𝑃 is approximated at the face by 𝐴𝑓 𝑑𝐴𝑃 /(𝑎𝑃 )𝑓 . In FiPy the
pressure correction equation can be written as,
>>> ap = CellVariable(mesh=mesh, value=1.)
>>> coeff = 1./ ap.arithmeticFaceValue*mesh._faceAreas * mesh._cellDistances
>>> pressureCorrectionEq = DiffusionTerm(coeff=coeff) - velocity.divergence
Above would work good on a staggered grid, however, on a colocated grid as FiPy uses, the term
velocity.divergence will cause oscillations in the pressure solution as velocity is a face variable. We can
apply the Rhie-Chow correction terms for this. In this an intermediate velocity term 𝑢◇ is considered which does not
contain the pressure corrections:
𝑉𝑃 ∑︁ 𝑎𝐴
⃗𝑢◇𝑃 = ⃗𝑢*𝑃 + (∇𝑝* )𝑃 = ⃗𝑢*
𝑎𝑃 𝑎𝑃 𝐴
𝑓
This velocity is interpolated at the edges, after which the pressure correction term is added again, but now considered
at the edge:
(︂ )︂
1 𝑉
⃗𝑢𝑓 = (⃗𝑢◇𝐿 + ⃗𝑢◇𝑅 )) − (∇𝑝*𝑓 )
2 𝑎𝑃 avg L,R
(︁ )︁
where 𝑎𝑉𝑃 is assumed a good approximation at the edge. Here L and R denote the two cells adjacent to the
avg L,R
face. Expanding the not calculated terms we arrive at
(︂ )︂ (︂ )︂
1 * * 1 𝑉 * * 𝑉
⃗𝑢𝑓 = (⃗𝑢𝐿 + ⃗𝑢𝑅 )) + (∇𝑝𝐿 + ∇𝑝𝑅 ) − (∇𝑝*𝑓 )
2 2 𝑎𝑃 avg L,R 𝑎𝑃 avg L,R
where we have replaced the coefficients of the cell pressure gradients by an averaged value over the edge. This formula
has the consequence that the velocity on a face depends not only on the pressure of the adjacent cells, but also on the
cells further away, which removes the unphysical pressure oscillations. We start by introducing needed terms
>>> from fipy.variables.faceGradVariable import _FaceGradVariable
>>> volume = CellVariable(mesh=mesh, value=mesh.cellVolumes, name='Volume')
>>> contrvolume=volume.arithmeticFaceValue
And set up the velocity with this formula in the SIMPLE loop. Now, set up the no-slip boundary conditions
>>> xVelocity.constrain(0., mesh.facesRight | mesh.facesLeft | mesh.facesBottom)
>>> xVelocity.constrain(U, mesh.facesTop)
>>> yVelocity.constrain(0., mesh.exteriorFaces)
>>> X, Y = mesh.faceCenters
>>> pressureCorrection.constrain(0., mesh.facesLeft & (Y < dL))
Below, we iterate for a set number of sweeps. We use the sweep() method instead of solve() because we
require the residual for output. We also use the cacheMatrix(), getMatrix(), cacheRHSvector() and
getRHSvector() because both the matrix and RHS vector are required by the SIMPLE algorithm. Additionally,
the sweep() method is passed an underRelaxation factor to relax the solution. This argument cannot be passed
to solve().
16.1 examples.reactiveWetting.liquidVapor1D
where 𝜌 is the density. This free energy supports a two phase equilibrium with densities given by 𝜌𝑙 and 𝜌𝑣 in the
liquid and vapor phases, respectively. The densities are determined by solving the following system of equations,
𝑃 𝜌𝑙 = 𝑃 (𝜌𝑣 ) (16.2)
(︀ )︀
and
𝜇 𝜌𝑙 = 𝜇 (𝜌𝑣 ) (16.3)
(︀ )︀
The equilibrium densities are verified by substitution into Eqs. (16.2) and (16.3). Firstly, Eqs. (16.1), (16.4) and (16.5)
are defined as python functions,
173
FiPy Manual, Release 3.1.3
and
>>> print numerix.allclose(P(liquidDensity), P(vaporDensity))
True
Using standard dissipation laws, we write the governing equations for mass and momentum conservation,
𝜕𝜌
+ 𝜕𝑗 (𝜌𝑢𝑗 ) = 0 (16.6)
𝜕𝑡
and
𝜕 (𝜌𝑢𝑖 )
+ 𝜕𝑗 (𝜌𝑢𝑖 𝑢𝑗 ) = 𝜕𝑗 (𝜈 [𝜕𝑗 𝑢𝑖 + 𝜕𝑖 𝑢𝑗 ]) − 𝜌𝜕𝑖 𝜇𝑁 𝐶 (16.7)
𝜕𝑡
where the non-classical potential, 𝜇𝑁 𝐶 , is given by,
𝛿𝐹
𝜇𝑁 𝐶 = = 𝜇 − 𝜖𝑇 𝜕𝑗2 𝜌 (16.8)
𝛿𝜌
As usual, to proceed, we define a mesh
>>> Lx = 1e-6
>>> nx = 100
>>> dx = Lx / nx
>>> mesh = Grid1D(nx=nx, dx=dx)
The system of equations is solved in a fully coupled manner using a block matrix. Defining 𝜇𝑁 𝐶 as an independent
variable makes it easier to script the equations without using higher order terms.
In order to solve the equations numerically, an interpolation method is used to prevent the velocity and density fields
decoupling. The following velocity correction equation (expressed in discretized form) prevents decoupling from
occuring,
𝐴𝑓 𝑑𝑓 (︁ )︁
𝑢𝑐𝑖,𝑓 = 𝜌𝜕𝑖 𝜇𝑁 𝐶 𝑓 − 𝜌𝑓 𝜕𝑖,𝑓 𝜇𝑁 𝐶 (16.9)
𝑑𝑓
where 𝐴𝑓 is the face area, 𝑑𝑓 is the distance between the adjacent cell centers and 𝑎𝑓 is the momentum conservation
equation’s matrix diagonal. The overbar refers to an averaged value between the two adjacent cells to the face. The
notation 𝜕𝑖,𝑓 refers to a derivative evaluated directly at the face (not averaged). The variable 𝑢𝑐𝑖 is used to modify the
velocity used in Eq. (16.6) such that,
𝜕𝜌
+ 𝜕𝑗 (𝜌 [𝑢𝑗 + 𝑢𝑐𝑖 ]) = 0 (16.10)
𝜕𝑡
Equation (16.10) becomes
>>> matrixDiagonal = CellVariable(mesh=mesh, name=r'$a_f$', value=1e+20, hasOld=True)
>>> correctionCoeff = mesh._faceAreas * mesh._cellDistances / matrixDiagonal.faceValue
>>> massEqn = TransientTerm(var=density) \
... + VanLeerConvectionTerm(coeff=velocity.faceValue + correctionCoeff \
... * (density * potentialNC.grad).faceValue, \
... var=density) \
... - DiffusionTerm(coeff=correctionCoeff * density.faceValue**2, var=potentialNC)
where the first term on the LHS of Eq. (16.9) is calculated in an explicit manner in the VanLeerConvectionTerm
and the second term is calculated implicitly as a DiffusionTerm with 𝜇𝑁 𝐶 as the independent variable.
In order to write Eq. (16.7) as a FiPy expression, the last term is rewritten such that,
𝜌𝜕𝑖 𝜇𝑁 𝐶 = 𝜕𝑖 𝜌𝜇𝑁 𝐶 − 𝜇𝑁 𝐶 𝜕𝑖 𝜌
(︀ )︀
which results in
>>> viscosity = 1e-3
>>> ConvectionTerm = CentralDifferenceConvectionTerm
>>> momentumEqn = TransientTerm(coeff=density, var=velocity) \
... + ConvectionTerm(coeff=[[1]] * density.faceValue * velocity.faceValue, var=velocity
... == DiffusionTerm(coeff=2 * viscosity, var=velocity) \
... - ConvectionTerm(coeff=density.faceValue * [[1]], var=potentialNC) \
... + ImplicitSourceTerm(coeff=density.grad[0], var=potentialNC)
The only required boundary condition eliminates flow in or out of the domain.
>>> velocity.constrain(0, mesh.exteriorFaces)
As previously stated, the 𝜇𝑁 𝐶 variable will be solved implicitly. To do this the Eq. (16.8) is linearized in 𝜌 such that
(︂ )︂*
𝜕𝜇
𝜇 𝑁𝐶
=𝜇 +*
(𝜌 − 𝜌* ) − 𝜖𝑇 𝜕𝑗2 𝜌 (16.11)
𝜕𝜌
𝜕𝜇
The * superscript denotes the current held value. In FiPy, 𝜕𝜌 is written as,
and 𝜇* is simply,
>>> potential = mu(density)
Due to a quirk in FiPy, the gradient of 𝜇𝑁 𝐶 needs to be constrained on the boundary. This is because
ConvectionTerm‘s will automatically assume a zero flux, which is not what we need in this case.
>>> potentialNC.faceGrad.constrain(value=[0], where=mesh.exteriorFaces)
All three equations are defined and an are combined together with
>>> coupledEqn = massEqn & momentumEqn & potentialNCEqn
The system will be solved as a phase separation problem with an initial density close to the average density, but with
some small amplitude noise. Under these circumstances, the final condition should be two separate phases of roughly
equal volume. The initial condition for the density is defined by
>>> numerix.random.seed(2011)
>>> density[:] = (liquidDensity + vaporDensity) / 2 * \
... (1 + 0.01 * (2 * numerix.random.random(mesh.numberOfCells) - 1))
The following section defines the required control parameters. The cfl parameter limits the size of the time step so
that dt = cfl * dx / max(velocity).
>>> cfl = 0.1
>>> tolerance = 1e-1
>>> dt = 1e-14
>>> timestep = 0
>>> relaxation = 0.5
>>> if __name__ == '__main__':
... totalSteps = 1e10
... else:
... totalSteps = 10
In the following time stepping scheme a time step is recalculated if the residual increases between sweeps or the
required tolerance is not attained within 20 sweeps. The major quirk in this scheme is the requirement of updat-
ing the matrixDiagonal using the entire coupled matrix. This could be achieved more elegantly by calling
cacheMatrix() only on the necessary part of the equation. This currently doesn’t work properly in FiPy.
17.1 examples.updating.update2_0to3_0
We generally use the first, but you may see us import specific functions if we feel it improves readability. You
should feel free to use whichever form you find most comfortable.
Note: the old behavior can be obtained, at least for now, by setting the FIPY_INCLUDE_NUMERIX_ALL
environment variable.
• If your equation contains a TransientTerm, then you must specify the timestep by passing a dt= argument
when calling solve() or sweep().
The remaining changes are not required, but they make scripts easier to read and we recommend them. FiPy may issue
a DeprecationWarning for some cases, to indicate that we may not maintain the old syntax indefinitely.
179
FiPy Manual, Release 3.1.3
• “getter” and “setter” methods have been replaced with properties, e.g., use
>>> x, y = mesh.cellCenters
instead of
>>> x, y = mesh.getCellCenters()
• Boundary conditions are better applied with the constrain() method than with the old FixedValue and
FixedFlux classes. See Boundary Conditions.
• Individual Mesh classes should be imported directly from fipy.meshes and not
fipy.meshes.numMesh.
• The Gmsh meshes now have simplified names: Gmsh2D instead of GmshImporter2D, Gmsh3D instead of
GmshImporter3D, and Gmsh2DIn3DSpace instead of GmshImporter2DIn3DSpace.
17.2 examples.updating.update1_0to2_0
instead of
>>> x = mesh.getCellCenters()[...,0]
This seemingly arbitrary change simplifies a great many things in FiPy, but the one most noticeable to the user
is that you can now write
>>> x, y = mesh.getCellCenters()
instead of
>>> x = mesh.getCellCenters()[...,0]
>>> y = mesh.getCellCenters()[...,1]
Unfortunately, we cannot reliably automate this conversion, but we find that searching for “...,” and “:,”
finds almost everything. Please don’t blindly “search & replace all” as that is almost bound to create more
problems than it’s worth.
Note: Any vector constants must be reoriented. For instance, in order to offset a Mesh, you must write
or
>>> mesh = Grid2D(...) + [[deltax], [deltay]]
instead of
>>> mesh = Grid2D(...) + (deltax, deltay)
instead of
>>> vectorField = VectorCellVariable(mesh=mesh)
Note: Because vector fields are properly supported, use vector operations to manipulate them, such as
>>> phase.getFaceGrad().dot((( 0, 1),
... (-1, 0)))
• For internal reasons, FiPy now supports CellVariable and FaceVariable objects that contain integers,
but it is not meaningful to solve a PDE for an integer field (FiPy should issue a warning if you try). As a result,
when given, initial values must be specified as floating-point values:
>>> var = CellVariable(mesh=mesh, value=1.)
If the value argument is not supplied, the CellVariable will contain floats, as before.
• The faces argument to BoundaryCondition now takes a mask, instead of a list of Face IDs. Now you
write
>>> X, Y = mesh.getFaaceCenters()
>>> FixedValue(faces=mesh.getExteriorFaces() & (X**2 < 1e-6), value=...)
instead of
>>> exteriorFaces = mesh.getExteriorFaces()
>>> X = exteriorFaces.getCenters()[...,0]
>>> FixedValue(faces=exteriorFaces.where(X**2 < 1e-6), value=...)
With the old syntax, a different call to getCenters() had to be made for each set of Face objects. It was also
extremely difficult to specify boundary conditions that depended both on position in space and on the current
values of any other Variable.
>>> FixedValue(faces=(mesh.getExteriorFaces()
... & (((X**2 < 1e-6)
although it probably could have been done with a rather convoluted (and slow!) filter function passed to
where. There no longer are any filter methods used in FiPy. You now would write
>>> x, y = mesh.cellCenters
>>> initialArray[(x < dx) | (x > (Lx - dx)) | (y < dy) | (y > (Ly - dy))] = 1.
Although they still exist, we find very little cause to ever call getCells() or
fipy.meshes.mesh.Mesh.getFaces().
• Some modules, such as fipy.solvers, have been significantly rearranged. For example, you need to change
>>> from fipy.solvers.linearPCGSolver import LinearPCGSolver
to either
>>> from fipy import LinearPCGSolver
or
>>> from fipy.solvers.pysparse.linearPCGSolver import LinearPCGSolver
• The numerix.max() and numerix.min() functions no longer exist. Either call max() and min() or
the max() and min() methods of a Variable.
• The Numeric module has not been supported for a long time. Be sure to use
>>> from fipy import numerix
instead of
>>> import Numeric
The remaining changes are not required, but they make scripts easier to read and we recommend them. FiPy may issue
a DeprecationWarning for some cases, to indicate that we may not maintain the old syntax indefinitely.
• All of the most commonly used classes and functions in FiPy are directly accessible in the fipy namespace.
For brevity, our examples now start with
>>> from fipy import *
imports that we used to use. Most of the explicit imports should continue to work, so you do not need to change
them if you don’t wish to, but we find our own scripts much easier to read without them.
All of the numerix module is now imported into the fipy namespace, so you can call numerix functions a
number of different ways, including:
>>> from fipy import *
>>> y = exp(x)
or
>>> from fipy import numerix
>>> y = numerix.exp(x)
or
>>> from fipy.tools.numerix import exp
>>> y = exp(x)
We generally use the first, but you may see us use the others, and should feel free to use whichever form you
find most comfortable.
Note: Internally, FiPy uses explicit imports, as is considered best Python practice, but we feel that clarity
trumps orthodoxy when it comes to the examples.
instead of
>>> from fipy import viewers
>>> viewer = viewers.make(vars=(alpha, beta, gamma),
... limits={'datamin': 0, 'datamax': 1})
which can be very hard to understand after the fact (make? make what?).
• A ConvectionTerm can now calculate its Peclet number automatically, so the diffusionTerm argument
is no longer required
>>> eq = (TransientTerm()
... == DiffusionTerm(coeff=diffCoeff)
... + PowerLawConvectionTerm(coeff=convCoeff))
instead of
>>> diffTerm = DiffusionTerm(coeff=diffCoeff)
>>> eq = (TransientTerm()
... == diffTerm
... + PowerLawConvectionTerm(coeff=convCoeff, diffusionTerm=diffTerm))
• An ImplicitSourceTerm now “knows” how to partion itself onto the solution matrix, so you can write
instead of
>>> S0 = mXi * phase * (1 - phase) - phase * S1 * (S1 < 0)
>>> source = S0 + ImplicitSourceTerm(coeff=S1 * (S1 < 0))
It is definitely still advantageous to hand-linearize your source terms, but it is no longer necessary to worry about
putting the “wrong” sign on the diagonal of the matrix.
• To make clearer the distinction between iterations, timesteps, and sweeps (see FAQ Iterations, timesteps, and
sweeps? Oh, my!) the steps argument to a Solver object has been renamed iterations.
• ImplicitDiffusionTerm has been renamed to DiffusionTerm.
17.3 examples.updating.update0_1to1_0
∇ · (𝐷∇𝜑 + ⃗𝑢𝜑) = 0
We define a 1D mesh
>>> L = 10.
>>> nx = 1000
>>> ny = 1
>>> from fipy.meshes.grid2D import Grid2D
>>> mesh = Grid2D(L / nx, L / ny, nx, ny)
or
>>> valueLeft = 0.
>>> valueRight = 1.
>>> from fipy.boundaryConditions.fixedValue import FixedValue
>>> from fipy.boundaryConditions.fixedFlux import FixedFlux
>>> boundaryConditions = (
... FixedValue(mesh.getFacesLeft(), valueLeft),
... FixedValue(mesh.getFacesRight(), valueRight),
... FixedFlux(mesh.getFacesTop(), 0.),
... FixedFlux(mesh.getFacesBottom(), 0.)
... )
More details of the benefits and drawbacks of each type of convection term can be found in the numerical section
of the manual. Essentially the ExponentialConvectionTerm and PowerLawConvectionTerm will both
handle most types of convection diffusion cases with the PowerLawConvectionTerm being more efficient.
We iterate to equilibrium
>>> from fipy.iterators.iterator import Iterator
>>> it = Iterator((eq,))
Traceback (most recent call last):
...
NameError: name 'eq' is not defined
>>> it.timestep()
Traceback (most recent call last):
...
NameError: name 'it' is not defined
or
>>> axis = 0
>>> x = mesh.getCellCenters()[:,axis]
>>> from fipy.tools import numerix
>>> CC = 1. - numerix.exp(-convCoeff[axis] * x / diffCoeff)
>>> DD = 1. - numerix.exp(-convCoeff[axis] * L / diffCoeff)
>>> analyticalArray = CC / DD
>>> numerix.allclose(analyticalArray, var, rtol = 1e-10, atol = 1e-10)
0
The Grid2D class with ny = 1 still works perfectly well for 1D problems, but the Grid1D class is slightly more
efficient, and it makes the code clearer when a 1D geometry is actually desired.
Because the mesh is now 1D, we must update the convection coefficient vector to be 1D as well
>>> diffCoeff = 1.
>>> convCoeff = (10.,)
The FixedValue boundary conditions at the left and right are unchanged, but a Grid1D mesh does not even have
top and bottom faces:
>>> valueLeft = 0.
>>> valueRight = 1.
>>> from fipy.boundaryConditions.fixedValue import FixedValue
>>> boundaryConditions = (
... FixedValue(mesh.getFacesLeft(), valueLeft),
... FixedValue(mesh.getFacesRight(), valueRight))
The biggest change between FiPy 0.1 and FiPy 1.0 is that Equation objects no longer exist at all. Instead, Term ob-
jects can be simply added, subtracted, and equated to assemble an equation. Where before the assembly of the equation
occurred in the black-box of SteadyConvectionDiffusionScEquation, we now assemble it directly:
>>> from fipy.terms.implicitDiffusionTerm import ImplicitDiffusionTerm
>>> diffTerm = ImplicitDiffusionTerm(coeff = diffCoeff)
Note: In version 0.1, the Equation object had to be told about the Variable, Solver, and
BoundaryCondition objects when it was created (and it, in turn, passed much of this information to the Term
objects in order to create them). In version 1.0, the Term objects (and the equation assembled from them) are abstract.
The Variable, Solver, and BoundaryCondition objects are only needed by the solve() method (and, in
fact, the same equation could be used to solve different variables, with different solvers, subject to different boundary
conditions, if desired).
The ImportError: No module named grid2DGistViewer results because the Viewer classes have
been moved and renamed. This error could be resolved by changing the import statement appropriately:
>>> if __name__ == '__main__':
... from fipy.viewers.gistViewer.gist1DViewer import Gist1DViewer
... viewer = Gist1DViewer(vars = var)
... viewer.plot()
Instead, rather than instantiating a particular Viewer (which you can still do, if you desire), a generic “factory”
method will return a Viewer appropriate for the supplied Variable object(s):
Please do not hesitate to contact us if this example does not help you convert your existing scripts to FiPy 1.0.
189
Chapter 18
How to Read the Modules Documentation
Each chapter describes one of the main sub-packages of the fipy package. The sub-package fipy.package
can be found in the directory fipy/package/. In a few cases, there will be packages within packages, e.g.
fipy.package.subpackage located in fipy/package/subpackage/. These sub-sub-packages will not
be given their own chapters; rather, their contents will be described in the chapter for their containing package.
18.1.1 Submodules
This module can be found in the file package/subpackage/base.py. You make it available to your script by
either:
import package.subpackage.base
and then refer simply to Object. For many classes, there is a shorthand notation:
from fipy import Object
Python is an object-oriented language and the FiPy framework is composed of objects or classes. Knowledge of
object-oriented programming (OOP) is not necessary to use either Python or FiPy, but a few concepts are useful.
OOP involves two main ideas:
encapsulation an object binds data with actions or “methods”. In most cases, you will not work with an
object’s data directly; instead, you will set, retrieve, or manipulate the data using the object’s methods.
Methods are functions that are attached to objects and that have direct access to the data of those objects.
Rather than passing the object data as an argument to a function:
191
FiPy Manual, Release 3.1.3
If you are unfamiliar with object-oriented practices, there probably seems little advantage in this reorder-
ing. You will have to trust us that the latter is a much more powerful way to do things.
inheritance specialized objects are derived or inherited from more general objects. Common behaviors or data
are defined in base objects and specific behaviors or data are either added or modified in derived objects.
Objects that declare the existence of certain methods, without actually defining what those methods do,
are called “abstract”. These objects exist to define the behavior of a family of objects, but rely on their
descendants to actually provide that behavior.
Unlike many object-oriented languages, Python does not prevent the creation of abstract objects, but we
will include a notice like
for abstract classes which should be used for documentation but never actually created in a FiPy script.
method1()
This is one thing that you can instruct any object that derives from Base to do, by calling
myObjectDerivedFromBase.method1()
Parameters
• self : this special argument refers to the object that is being created.
method2()
This is another thing that you can instruct any object that derives from Base to do.
Parameters
• arg1: this argument is required. Python supports named arguments, so you must either list
the value for arg1 first:
or you can specify the arguments in any order, as long as they are named:
• arg2: this argument may be omitted, in which case it will be assigned a default value of
None. If you do not use named arguments (and we recommend that you do), all required
arguments must be specified before any optional arguments.
• arg3: this argument may be omitted, in which case it will be assigned a default value of
’string’.
method2()
Object provides a new definition for the behavior of method2(), whereas the behavior of method1()
is defined by Base.
Each chapter describes one of the main sub-packages of the fipy package. The sub-package fipy.package
can be found in the directory fipy/package/. In a few cases, there will be packages within packages, e.g.
fipy.package.subpackage located in fipy/package/subpackage/. These sub-sub-packages will not
be given their own chapters; rather, their contents will be described in the chapter for their containing package.
19.1 Submodules
Parameters
• faces: A list or tuple of exterior Face objects to which this condition applies.
• value: The value to impose.
195
FiPy Manual, Release 3.1.3
class fipy.boundaryConditions.nthOrderBoundaryCondition.NthOrderBoundaryCondition(faces,
value,
or-
der)
Bases: fipy.boundaryConditions.boundaryCondition.BoundaryCondition
This boundary condition is generally used in conjunction with a ImplicitDiffusionTerm that has multiple coeffi-
cients. It does not have any direct effect on the solution matrices, but its derivatives do.
Creates an NthOrderBoundaryCondition.
Parameters
• faces: A list or tuple of Face objects to which this condition applies.
• value: The value to impose.
• order: The order of the boundary condition. An order of 0 corresponds to a FixedValue and
an order of 1 corresponds to a FixedFlux. Even and odd orders behave like FixedValue and
FixedFlux objects, respectively, but apply to higher order terms.
20.1 Submodules
fipy.matrices.offsetSparseMatrix.OffsetSparseMatrix(SparseMatrix, numberOfVari-
ables, numberOfEquations)
Used in binary terms. equationIndex and varIndex need to be set statically before instantiation.
199
FiPy Manual, Release 3.1.3
21.1 Subpackages
Submodules
fipy.meshes.builders.abstractGridBuilder module
fipy.meshes.builders.grid1DBuilder module
fipy.meshes.builders.grid2DBuilder module
fipy.meshes.builders.grid3DBuilder module
fipy.meshes.builders.periodicGrid1DBuilder module
fipy.meshes.builders.utilityClasses module
Module contents
Submodules
fipy.meshes.numMesh.cylindricalGrid1D module
fipy.meshes.numMesh.cylindricalGrid2D module
fipy.meshes.numMesh.cylindricalUniformGrid1D module
fipy.meshes.numMesh.cylindricalUniformGrid2D module
fipy.meshes.numMesh.deprecatedWarning module
fipy.meshes.numMesh.deprecatedWarning.numMeshDeprecated()
201
FiPy Manual, Release 3.1.3
fipy.meshes.numMesh.gmshImport module
fipy.meshes.numMesh.grid1D module
fipy.meshes.numMesh.grid2D module
fipy.meshes.numMesh.grid3D module
fipy.meshes.numMesh.periodicGrid1D module
fipy.meshes.numMesh.periodicGrid2D module
fipy.meshes.numMesh.skewedGrid2D module
fipy.meshes.numMesh.tri2D module
fipy.meshes.numMesh.uniformGrid1D module
fipy.meshes.numMesh.uniformGrid2D module
fipy.meshes.numMesh.uniformGrid3D module
Module contents
Submodules
fipy.meshes.representations.abstractRepresentation module
fipy.meshes.representations.gridRepresentation module
fipy.meshes.representations.meshRepresentation module
Module contents
Submodules
fipy.meshes.topologies.abstractTopology module
fipy.meshes.topologies.gridTopology module
fipy.meshes.topologies.meshTopology module
Module contents
21.2 Submodules
facesBottom
Return list of faces on bottom boundary of Grid3D with the y-axis running from bottom to top.
>>> from fipy import Grid2D, Grid3D, numerix
>>> mesh = Grid3D(nx = 3, ny = 2, nz = 1, dx = 0.5, dy = 2., dz = 4.)
>>> print numerix.allequal((12, 13, 14),
... numerix.nonzero(mesh.facesBottom)[0])
1
>>> x, y, z = mesh.faceCenters
>>> print numerix.allequal((12, 13),
... numerix.nonzero(mesh.facesBottom & (x < 1))[0])
1
facesDown
Return list of faces on bottom boundary of Grid3D with the y-axis running from bottom to top.
>>> from fipy import Grid2D, Grid3D, numerix
>>> mesh = Grid3D(nx = 3, ny = 2, nz = 1, dx = 0.5, dy = 2., dz = 4.)
>>> print numerix.allequal((12, 13, 14),
... numerix.nonzero(mesh.facesBottom)[0])
1
>>> x, y, z = mesh.faceCenters
>>> print numerix.allequal((12, 13),
... numerix.nonzero(mesh.facesBottom & (x < 1))[0])
1
facesFront
Return list of faces on front boundary of Grid3D with the z-axis running from front to back.
>>> from fipy import Grid3D, numerix
>>> mesh = Grid3D(nx = 3, ny = 2, nz = 1, dx = 0.5, dy = 2., dz = 4.)
>>> print numerix.allequal((0, 1, 2, 3, 4, 5),
... numerix.nonzero(mesh.facesFront)[0])
True
facesLeft
Return face on left boundary of Grid1D as list with the x-axis running from left to right.
>>> from fipy import Grid2D, Grid3D
>>> mesh = Grid3D(nx = 3, ny = 2, nz = 1, dx = 0.5, dy = 2., dz = 4.)
>>> print numerix.allequal((21, 25),
... numerix.nonzero(mesh.facesLeft)[0])
True
>>> mesh = Grid2D(nx = 3, ny = 2, dx = 0.5, dy = 2.)
>>> print numerix.allequal((9, 13),
... numerix.nonzero(mesh.facesLeft)[0])
True
facesRight
Return list of faces on right boundary of Grid3D with the x-axis running from left to right.
>>> from fipy import Grid2D, Grid3D, numerix
>>> mesh = Grid3D(nx = 3, ny = 2, nz = 1, dx = 0.5, dy = 2., dz = 4.)
>>> print numerix.allequal((24, 28),
... numerix.nonzero(mesh.facesRight)[0])
True
>>> mesh = Grid2D(nx = 3, ny = 2, dx = 0.5, dy = 2.)
>>> print numerix.allequal((12, 16),
... numerix.nonzero(mesh.facesRight)[0])
True
facesTop
Return list of faces on top boundary of Grid3D with the y-axis running from bottom to top.
>>> from fipy import Grid2D, Grid3D, numerix
>>> mesh = Grid3D(nx = 3, ny = 2, nz = 1, dx = 0.5, dy = 2., dz = 4.)
>>> print numerix.allequal((18, 19, 20),
... numerix.nonzero(mesh.facesTop)[0])
True
>>> mesh = Grid2D(nx = 3, ny = 2, dx = 0.5, dy = 2.)
>>> print numerix.allequal((6, 7, 8),
... numerix.nonzero(mesh.facesTop)[0])
True
facesUp
Return list of faces on top boundary of Grid3D with the y-axis running from bottom to top.
>>> from fipy import Grid2D, Grid3D, numerix
>>> mesh = Grid3D(nx = 3, ny = 2, nz = 1, dx = 0.5, dy = 2., dz = 4.)
>>> print numerix.allequal((18, 19, 20),
... numerix.nonzero(mesh.facesTop)[0])
True
>>> mesh = Grid2D(nx = 3, ny = 2, dx = 0.5, dy = 2.)
>>> print numerix.allequal((6, 7, 8),
... numerix.nonzero(mesh.facesTop)[0])
True
getCellCenters(*args, **kwds)
Deprecated since version 3.0: use the cellCenters property instead
getCellVolumes(*args, **kwds)
Deprecated since version 3.0: use the cellVolumes property instead
getDim(*args, **kwds)
Deprecated since version 3.0: use the dim property instead
getExteriorFaces(*args, **kwds)
Deprecated since version 3.0: use the exteriorFaces property instead
Return only the faces that have one neighboring cell.
getFaceCellIDs(*args, **kwds)
Deprecated since version 3.0: use the faceCellIDs property instead
getFaceCenters(*args, **kwds)
Deprecated since version 3.0: use the faceCenters property instead
getFacesBack(*args, **kwds)
Deprecated since version 3.0: use the facesBack property instead
getFacesBottom(*args, **kwds)
Deprecated since version 3.0: use the facesBottom property instead
getFacesDown(*args, **kwds)
Deprecated since version 3.0: use the facesBottom property instead
getFacesFront(*args, **kwds)
Deprecated since version 3.0: use the facesFront property instead
getFacesLeft(*args, **kwds)
Deprecated since version 3.0: use the facesLeft property instead
getFacesRight(*args, **kwds)
Deprecated since version 3.0: use the facesRight property instead
getFacesTop(*args, **kwds)
Deprecated since version 3.0: use the facesTop property instead
getFacesUp(*args, **kwds)
Deprecated since version 3.0: use the facesTop property instead
getInteriorFaceCellIDs(*args, **kwds)
Deprecated since version 3.0: use the interiorFaceCellIDs property instead
getInteriorFaceIDs(*args, **kwds)
Deprecated since version 3.0: use the interiorFaceIDs property instead
getInteriorFaces(*args, **kwds)
Deprecated since version 3.0: use the interiorFaces property instead
Return only the faces that have two neighboring cells.
getNearestCell(point)
getNumberOfCells(*args, **kwds)
Deprecated since version 3.0: use the numberOfCells property instead
getPhysicalShape(*args, **kwds)
Deprecated since version 3.0: use the physicalShape property instead
getScale(*args, **kwds)
Deprecated since version 3.0: use the scale property instead
getShape(*args, **kwds)
Deprecated since version 3.0: use the shape property instead
getVertexCoords(*args, **kwds)
Deprecated since version 3.0: use the vertexCoords property instead
interiorFaceCellIDs
interiorFaceIDs
interiorFaces
scale
scaledCellDistances
scaledCellToCellDistances
scaledCellVolumes
scaledFaceAreas
scaledFaceToCellDistances
setScale(*args, **kwds)
Deprecated since version 3.0: use the scale property instead
x
Equivalent to using cellCenters[0].
>>> from fipy import *
>>> print Grid1D(nx=2).x
[ 0.5 1.5]
y
Equivalent to using cellCenters[1].
>>> from fipy import *
>>> print Grid2D(nx=2, ny=2).y
[ 0.5 0.5 1.5 1.5]
>>> print Grid1D(nx=2).y
Traceback (most recent call last):
...
AttributeError: 1D meshes do not have a "y" attribute.
z
Equivalent to using cellCenters[2].
1D Mesh
class fipy.meshes.cylindricalNonUniformGrid1D.CylindricalNonUniformGrid1D(dx=1.0,
nx=None,
ori-
gin=(0,
),
over-
lap=2,
com-
mu-
ni-
ca-
tor=DummyComm(),
*args,
**kwargs)
Bases: fipy.meshes.nonUniformGrid1D.NonUniformGrid1D
Creates a 1D cylindrical grid mesh.
>>> mesh = CylindricalNonUniformGrid1D(nx = 3)
>>> print mesh.cellCenters
[[ 0.5 1.5 2.5]]
2D rectangular Mesh
class fipy.meshes.cylindricalNonUniformGrid2D.CylindricalNonUniformGrid2D(dx=1.0,
dy=1.0,
nx=None,
ny=None,
ori-
gin=((0.0,
),
(0.0,
)),
over-
lap=2,
com-
mu-
ni-
ca-
tor=DummyComm(),
*args,
**kwargs)
Bases: fipy.meshes.nonUniformGrid2D.NonUniformGrid2D
Creates a 2D cylindrical grid mesh with horizontal faces numbered first and then vertical faces.
1D Mesh
class fipy.meshes.cylindricalUniformGrid1D.CylindricalUniformGrid1D(dx=1.0,
nx=1, ori-
gin=(0, ),
overlap=2,
communica-
tor=DummyComm(),
*args,
**kwargs)
Bases: fipy.meshes.uniformGrid1D.UniformGrid1D
Creates a 1D cylindrical grid mesh.
>>> mesh = CylindricalUniformGrid1D(nx = 3)
>>> print mesh.cellCenters
[[ 0.5 1.5 2.5]]
cellVolumes
• overlap: the number of overlapping cells for parallel simulations. Generally 2 is adequate.
Higher order equations or discretizations require more.
• communicator: either fipy.tools.parallelComm or fipy.tools.serialComm. Select
fipy.tools.serialComm to create a serial mesh when running in parallel. Mostly used for
test purposes.
fipy.meshes.factoryMeshes.CylindricalGrid1D(dr=None, nr=None, Lr=None, dx=1.0,
nx=None, Lx=None, origin=(0, ), over-
lap=2, communicator=DummyComm())
Factory function to select between CylindricalUniformGrid1D and CylindricalNonUniformGrid1D. If Lx is
specified the length of the domain is always Lx regardless of dx.
Parameters
• dr or dx: grid spacing in the radial direction
• nr or nx: number of cells in the radial direction
• Lr or Lx: the domain length in the radial direction
• origin : position of the mesh’s origin in the form (x,)
• overlap: the number of overlapping cells for parallel simulations. Generally 2 is adequate.
Higher order equations or discretizations require more.
• communicator: either fipy.tools.parallelComm or fipy.tools.serialComm. Select
fipy.tools.serialComm to create a serial mesh when running in parallel. Mostly used for
test purposes.
>>> radius = 5.
>>> side = 4.
>>> squaredCircle = Gmsh2D('''
... // A mesh consisting of a square inside a circle inside a circle
...
... // define the basic dimensions of the mesh
...
... cellSize = 1;
... radius = %(radius)g;
... side = %(side)g;
...
... // define the compass points of the inner circle
...
... Point(1) = {0, 0, 0, cellSize};
... Point(2) = {-radius, 0, 0, cellSize};
... Point(3) = {0, radius, 0, cellSize};
... Point(4) = {radius, 0, 0, cellSize};
... Point(5) = {0, -radius, 0, cellSize};
...
... // define the compass points of the outer circle
...
... Point(6) = {-2*radius, 0, 0, cellSize};
... Point(7) = {0, 2*radius, 0, cellSize};
... Point(8) = {2*radius, 0, 0, cellSize};
... Point(9) = {0, -2*radius, 0, cellSize};
...
... // define the corners of the square
...
... Point(10) = {side/2, side/2, 0, cellSize/2};
... Point(11) = {-side/2, side/2, 0, cellSize/2};
... Point(12) = {-side/2, -side/2, 0, cellSize/2};
... Point(13) = {side/2, -side/2, 0, cellSize/2};
...
... // define the inner circle
...
... Circle(1) = {2, 1, 3};
... Circle(2) = {3, 1, 4};
... Circle(3) = {4, 1, 5};
... Circle(4) = {5, 1, 2};
...
... // define the outer circle
...
... Circle(5) = {6, 1, 7};
... Circle(6) = {7, 1, 8};
... Circle(7) = {8, 1, 9};
... Circle(8) = {9, 1, 6};
...
... // define the square
...
... Line(9) = {10, 13};
... Line(10) = {13, 12};
... Line(11) = {12, 11};
... Line(12) = {11, 10};
...
... // define the three boundaries
...
... Line Loop(1) = {1, 2, 3, 4};
... Line Loop(2) = {5, 6, 7, 8};
It can be easier to specify certain domains and boundaries within Gmsh than it is to define the same domains
and boundaries with FiPy expressions.
Here we compare obtaining the same Cells and Faces using FiPy’s parametric descriptions and Gmsh’s labels.
>>> x, y = squaredCircle.cellCenters
>>> X, Y = squaredCircle.faceCenters
It is possible to direct Gmsh to give the mesh different densities in different locations
>>> geo = '''
... // A mesh consisting of a square
...
>>> std = []
>>> bkg = None
>>> for refine in range(4):
... square = Gmsh2D(geo, background=bkg)
... x, y = square.cellCenters
... bkg = CellVariable(mesh=square, value=abs(x / 4) + 0.01)
... std.append(numerix.std(numerix.sqrt(2 * square.cellVolumes) / bkg))
and that the final density is close enough to the desired density
>>> print std[-1] < 0.2
True
Parameters
• arg: a string giving (i) the path to an MSH file, (ii) a path to a Gmsh geometry
(”.geo”) file, or (iii) a Gmsh geometry script
• coordDimensions: an integer indicating dimension of shapes
• order: ???
• background: a CellVariable that specifies the desired characteristic lengths of the mesh cells
exception fipy.meshes.mesh.MeshAdditionError
Bases: exceptions.Exception
class fipy.meshes.mesh.Mesh(vertexCoords, faceVertexIDs, cellFaceIDs, communi-
cator=DummyComm(), _RepresentationClass=<class
‘fipy.meshes.representations.meshRepresentation._MeshRepresentation’>,
_TopologyClass=<class ‘fipy.meshes.topologies.meshTopology._MeshTopology’>)
Bases: fipy.meshes.abstractMesh.AbstractMesh
Generic mesh class using numerix to do the calculations
Meshes contain cells, faces, and vertices.
This is built for a non-mixed element mesh.
Parameters
• extrudeFunc: function that takes the vertex coordinates and returns the displaced values
• layers: the number of layers in the extruded mesh (number of times extrudeFunc will be
called)
>>> from fipy.meshes.nonUniformGrid2D import NonUniformGrid2D
>>> print NonUniformGrid2D(nx=2,ny=2).extrude(layers=2).cellCenters
[[ 0.5 1.5 0.5 1.5 0.5 1.5 0.5 1.5]
[ 0.5 0.5 1.5 1.5 0.5 0.5 1.5 1.5]
[ 0.5 0.5 0.5 0.5 1.5 1.5 1.5 1.5]]
1D Mesh
class fipy.meshes.nonUniformGrid1D.NonUniformGrid1D(dx=1.0, nx=None, overlap=2,
communicator=DummyComm(),
_BuilderClass=<class
‘fipy.meshes.builders.grid1DBuilder._NonuniformGrid1DBui
_RepresentationClass=<class
‘fipy.meshes.representations.gridRepresentation._Grid1DRep
_TopologyClass=<class
‘fipy.meshes.topologies.gridTopology._Grid1DTopology’>)
Bases: fipy.meshes.mesh1D.Mesh1D
Creates a 1D grid mesh.
>>> mesh = NonUniformGrid1D(nx = 3)
>>> print mesh.cellCenters
[[ 0.5 1.5 2.5]]
2D rectangular Mesh
Peridoic 1D Mesh
class fipy.meshes.periodicGrid1D.PeriodicGrid1D(dx=1.0, nx=None, overlap=2, *args,
**kwargs)
Bases: fipy.meshes.nonUniformGrid1D.NonUniformGrid1D
Creates a Periodic grid mesh.
>>> mesh = PeriodicGrid1D(dx = (1, 2, 3))
cellCenters
Defined outside of a geometry class since we need the CellVariable version of cellCenters; that is, the
cellCenters defined in fipy.meshes.mesh and not in any geometry (since a CellVariable requires a reference
to a mesh).
1D Mesh
class fipy.meshes.uniformGrid1D.UniformGrid1D(dx=1.0, nx=1, origin=(0, ), over-
lap=2, communicator=DummyComm(),
_RepresentationClass=<class
‘fipy.meshes.representations.gridRepresentation._Grid1DRepresentati
_TopologyClass=<class
‘fipy.meshes.topologies.gridTopology._Grid1DTopology’>)
Bases: fipy.meshes.uniformGrid.UniformGrid
Creates a 1D grid mesh.
exteriorFaces
Geometry set and calc
faceCellIDs
faceNormals
vertexCoords
Faces: XY faces numbered first, then XZ faces, then YZ faces. Within each subcategory, it is numbered in the
usual way.
faceCellIDs
faceNormals
faceVertexIDs
vertexCoords
• overlap: the number of overlapping cells for parallel simulations. Generally 2 is adequate.
Higher order equations or discretizations require more.
• communicator: either fipy.tools.parallelComm or fipy.tools.serialComm. Select
fipy.tools.serialComm to create a serial mesh when running in parallel. Mostly used for
test purposes.
>>> print Grid2D(Lx=3., nx=2).dx
1.5
cellCenters
Defined outside of a geometry class since we need the CellVariable version of cellCenters; that is, the
cellCenters defined in fipy.meshes.mesh and not in any geometry (since a CellVariable requires a reference
to a mesh).
class fipy.meshes.PeriodicGrid2D(dx=1.0, dy=1.0, nx=None, ny=None, overlap=2, communica-
tor=DummyComm(), *args, **kwargs)
Bases: fipy.meshes.periodicGrid2D._BasePeriodicGrid2D
Creates a periodic2D grid mesh with horizontal faces numbered first and then vertical faces. Vertices and cells
are numbered in the usual way.
>>> from fipy import numerix
... cellSize = 1;
... radius = %(radius)g;
... side = %(side)g;
...
... // define the compass points of the inner circle
...
... Point(1) = {0, 0, 0, cellSize};
... Point(2) = {-radius, 0, 0, cellSize};
... Point(3) = {0, radius, 0, cellSize};
... Point(4) = {radius, 0, 0, cellSize};
... Point(5) = {0, -radius, 0, cellSize};
...
... // define the compass points of the outer circle
...
... Point(6) = {-2*radius, 0, 0, cellSize};
... Point(7) = {0, 2*radius, 0, cellSize};
... Point(8) = {2*radius, 0, 0, cellSize};
... Point(9) = {0, -2*radius, 0, cellSize};
...
... // define the corners of the square
...
... Point(10) = {side/2, side/2, 0, cellSize/2};
... Point(11) = {-side/2, side/2, 0, cellSize/2};
... Point(12) = {-side/2, -side/2, 0, cellSize/2};
... Point(13) = {side/2, -side/2, 0, cellSize/2};
...
... // define the inner circle
...
... Circle(1) = {2, 1, 3};
... Circle(2) = {3, 1, 4};
... Circle(3) = {4, 1, 5};
... Circle(4) = {5, 1, 2};
...
... // define the outer circle
...
... Circle(5) = {6, 1, 7};
... Circle(6) = {7, 1, 8};
... Circle(7) = {8, 1, 9};
... Circle(8) = {9, 1, 6};
...
... // define the square
...
... Line(9) = {10, 13};
... Line(10) = {13, 12};
... Line(11) = {12, 11};
... Line(12) = {11, 10};
...
... // define the three boundaries
...
... Line Loop(1) = {1, 2, 3, 4};
... Line Loop(2) = {5, 6, 7, 8};
... Line Loop(3) = {9, 10, 11, 12};
...
... // define the three domains
...
... Plane Surface(1) = {2, 1};
... Plane Surface(2) = {1, 3};
... Plane Surface(3) = {3};
...
... // label the three domains
...
... // attention: if you use any "Physical" labels, you *must* label
... // all elements that correspond to FiPy Cells (Physical Surace in 2D
... // and Physical Volume in 3D) or Gmsh will not include them and FiPy
... // will not be able to include them in the Mesh.
...
... // note: if you do not use any labels, all Cells will be included.
...
... Physical Surface("Outer") = {1};
... Physical Surface("Middle") = {2};
... Physical Surface("Inner") = {3};
...
... // label the "north-west" part of the exterior boundary
...
... // note: you only need to label the Face elements
... // (Physical Line in 2D and Physical Surface in 3D) that correspond
... // to boundaries you are interested in. FiPy does not need them to
... // construct the Mesh.
...
... Physical Line("NW") = {5};
... ''' % locals())
It can be easier to specify certain domains and boundaries within Gmsh than it is to define the same domains
and boundaries with FiPy expressions.
Here we compare obtaining the same Cells and Faces using FiPy’s parametric descriptions and Gmsh’s labels.
>>> x, y = squaredCircle.cellCenters
>>> X, Y = squaredCircle.faceCenters
It is possible to direct Gmsh to give the mesh different densities in different locations
>>> geo = '''
... // A mesh consisting of a square
...
... // define the corners of the square
...
... Point(1) = {1, 1, 0, 1};
... Point(2) = {0, 1, 0, 1};
... Point(3) = {0, 0, 0, 1};
... Point(4) = {1, 0, 0, 1};
...
... // define the square
...
... Line(1) = {1, 2};
... Line(2) = {2, 3};
... Line(3) = {3, 4};
... Line(4) = {4, 1};
...
... // define the boundary
...
... Line Loop(1) = {1, 2, 3, 4};
...
... // define the domain
...
... Plane Surface(1) = {1};
... '''
>>> std = []
>>> bkg = None
>>> for refine in range(4):
... square = Gmsh2D(geo, background=bkg)
... x, y = square.cellCenters
... bkg = CellVariable(mesh=square, value=abs(x / 4) + 0.01)
... std.append(numerix.std(numerix.sqrt(2 * square.cellVolumes) / bkg))
and that the final density is close enough to the desired density
>>> print std[-1] < 0.2
True
Parameters
• arg: a string giving (i) the path to an MSH file, (ii) a path to a Gmsh geometry
(”.geo”) file, or (iii) a Gmsh geometry script
• coordDimensions: an integer indicating dimension of shapes
• order: ???
• background: a CellVariable that specifies the desired characteristic lengths of the mesh cells
22.1 Subpackages
Subpackages
fipy.solvers.pyAMG.preconditioners package
Submodules
fipy.solvers.pyAMG.preconditioners.smoothedAggregationPreconditioner module
Module contents
Submodules
fipy.solvers.pyAMG.linearCGSSolver module
fipy.solvers.pyAMG.linearGMRESSolver module
fipy.solvers.pyAMG.linearGeneralSolver module
fipy.solvers.pyAMG.linearLUSolver module
fipy.solvers.pyAMG.linearPCGSolver module
fipy.solvers.pyAMG.smoothedAggregationSolver module
Module contents
Subpackages
fipy.solvers.pysparse.preconditioners package
Submodules
233
FiPy Manual, Release 3.1.3
fipy.solvers.pysparse.preconditioners.jacobiPreconditioner module
class fipy.solvers.pysparse.preconditioners.jacobiPreconditioner.JacobiPreconditioner
Bases: fipy.solvers.pysparse.preconditioners.preconditioner.Preconditioner
Jacobi preconditioner for PySparse. Really just a wrapper class for pysparse.precon.jacobi.
Create a Preconditioner object.
fipy.solvers.pysparse.preconditioners.preconditioner module
class fipy.solvers.pysparse.preconditioners.preconditioner.Preconditioner
Base preconditioner class
fipy.solvers.pysparse.preconditioners.ssorPreconditioner module
class fipy.solvers.pysparse.preconditioners.ssorPreconditioner.SsorPreconditioner
Bases: fipy.solvers.pysparse.preconditioners.preconditioner.Preconditioner
SSOR preconditioner for PySparse. Really just a wrapper class for pysparse.precon.jacobi.
Create a Preconditioner object.
Module contents
class fipy.solvers.pysparse.preconditioners.JacobiPreconditioner
Bases: fipy.solvers.pysparse.preconditioners.preconditioner.Preconditioner
Jacobi preconditioner for PySparse. Really just a wrapper class for pysparse.precon.jacobi.
Create a Preconditioner object.
class fipy.solvers.pysparse.preconditioners.SsorPreconditioner
Bases: fipy.solvers.pysparse.preconditioners.preconditioner.Preconditioner
SSOR preconditioner for PySparse. Really just a wrapper class for pysparse.precon.jacobi.
Create a Preconditioner object.
Submodules
fipy.solvers.pysparse.linearCGSSolver module
fipy.solvers.pysparse.linearGMRESSolver module
class fipy.solvers.pysparse.linearGMRESSolver.LinearGMRESSolver(precon=<fipy.solvers.pysparse.preconditio
instance>, *args,
**kwargs)
Bases: fipy.solvers.pysparse.pysparseSolver.PysparseSolver
The LinearGMRESSolver solves a linear system of equations using the generalised minimal residual method
(GMRES) with Jacobi preconditioning. GMRES solves systems with a general non-symmetric coefficient ma-
trix.
The LinearGMRESSolver is a wrapper class for the the PySparse itsolvers.gmres() and precon.jacobi() methods.
Parameters
• precon: Preconditioner to use
fipy.solvers.pysparse.linearJORSolver module
class fipy.solvers.pysparse.linearJORSolver.LinearJORSolver(tolerance=1e-10,
iterations=1000, relax-
ation=1.0)
Bases: fipy.solvers.pysparse.pysparseSolver.PysparseSolver
The LinearJORSolver solves a linear system of equations using Jacobi over-relaxation. This method solves
systems with a general non-symmetric coefficient matrix.
The Solver class should not be invoked directly.
Parameters
• tolerance: The required error tolerance.
• iterations: The maximum number of iterative steps to perform.
• relaxation: The relaxation.
fipy.solvers.pysparse.linearLUSolver module
fipy.solvers.pysparse.linearPCGSolver module
class fipy.solvers.pysparse.linearPCGSolver.LinearPCGSolver(precon=<fipy.solvers.pysparse.preconditioners.ss
instance>, *args,
**kwargs)
Bases: fipy.solvers.pysparse.pysparseSolver.PysparseSolver
The LinearPCGSolver solves a linear system of equations using the preconditioned conjugate gradient method
(PCG) with symmetric successive over-relaxation (SSOR) preconditioning by default. Alternatively, Jacobi
preconditioning can be specified through precon. The PCG method solves systems with a symmetric positive
definite coefficient matrix.
The LinearPCGSolver is a wrapper class for the the PySparse itsolvers.pcg() and precon.ssor() methods.
Parameters
• precon: Preconditioner to use
fipy.solvers.pysparse.pysparseSolver module
Module contents
fipy.solvers.pysparse.DefaultSolver
alias of LinearPCGSolver
fipy.solvers.pysparse.DummySolver
alias of LinearPCGSolver
fipy.solvers.pysparse.DefaultAsymmetricSolver
alias of LinearLUSolver
fipy.solvers.pysparse.GeneralSolver
alias of LinearLUSolver
class fipy.solvers.pysparse.LinearCGSSolver(precon=None, *args, **kwargs)
Bases: fipy.solvers.pysparse.pysparseSolver.PysparseSolver
The LinearCGSSolver solves a linear system of equations using the conjugate gradient squared method (CGS),
a variant of the biconjugate gradient method (BiCG). CGS solves linear systems with a general non-symmetric
coefficient matrix.
The LinearCGSSolver is a wrapper class for the the PySparse itsolvers.cgs() method.
Parameters
• precon: Preconditioner to use
class fipy.solvers.pysparse.LinearPCGSolver(precon=<fipy.solvers.pysparse.preconditioners.ssorPreconditioner.SsorP
instance>, *args, **kwargs)
Bases: fipy.solvers.pysparse.pysparseSolver.PysparseSolver
The LinearPCGSolver solves a linear system of equations using the preconditioned conjugate gradient method
(PCG) with symmetric successive over-relaxation (SSOR) preconditioning by default. Alternatively, Jacobi
preconditioning can be specified through precon. The PCG method solves systems with a symmetric positive
definite coefficient matrix.
The LinearPCGSolver is a wrapper class for the the PySparse itsolvers.pcg() and precon.ssor() methods.
Parameters
• precon: Preconditioner to use
class fipy.solvers.pysparse.LinearGMRESSolver(precon=<fipy.solvers.pysparse.preconditioners.jacobiPreconditioner.J
instance>, *args, **kwargs)
Bases: fipy.solvers.pysparse.pysparseSolver.PysparseSolver
The LinearGMRESSolver solves a linear system of equations using the generalised minimal residual method
(GMRES) with Jacobi preconditioning. GMRES solves systems with a general non-symmetric coefficient ma-
trix.
The LinearGMRESSolver is a wrapper class for the the PySparse itsolvers.gmres() and precon.jacobi() methods.
Parameters
• precon: Preconditioner to use
class fipy.solvers.pysparse.LinearLUSolver(tolerance=1e-10, iterations=10, maxItera-
tions=10, precon=None)
Bases: fipy.solvers.pysparse.pysparseSolver.PysparseSolver
The LinearLUSolver solves a linear system of equations using LU-factorisation. This method solves systems
with a general non-symmetric coefficient matrix using partial pivoting.
The LinearLUSolver is a wrapper class for the the PySparse superlu.factorize() method.
Creates a LinearLUSolver.
Parameters
• tolerance: The required error tolerance.
• iterations: The number of LU decompositions to perform. For large systems a number of
iterations is generally required.
• precon: not used but maintains a common interface.
class fipy.solvers.pysparse.LinearJORSolver(tolerance=1e-10, iterations=1000, relax-
ation=1.0)
Bases: fipy.solvers.pysparse.pysparseSolver.PysparseSolver
The LinearJORSolver solves a linear system of equations using Jacobi over-relaxation. This method solves
systems with a general non-symmetric coefficient matrix.
The Solver class should not be invoked directly.
Parameters
• tolerance: The required error tolerance.
• iterations: The maximum number of iterative steps to perform.
• relaxation: The relaxation.
class fipy.solvers.pysparse.JacobiPreconditioner
Bases: fipy.solvers.pysparse.preconditioners.preconditioner.Preconditioner
Jacobi preconditioner for PySparse. Really just a wrapper class for pysparse.precon.jacobi.
Create a Preconditioner object.
class fipy.solvers.pysparse.SsorPreconditioner
Bases: fipy.solvers.pysparse.preconditioners.preconditioner.Preconditioner
SSOR preconditioner for PySparse. Really just a wrapper class for pysparse.precon.jacobi.
Create a Preconditioner object.
Submodules
fipy.solvers.scipy.linearBicgstabSolver module
class fipy.solvers.scipy.linearBicgstabSolver.LinearBicgstabSolver
Bases: fipy.solvers.scipy.scipyKrylovSolver._ScipyKrylovSolver
The LinearBicgstabSolver is an interface to the Bicgstab solver in Scipy, with no preconditioning by default.
Parameters
• tolerance: The required error tolerance.
• iterations: The maximum number of iterative steps to perform.
• precon: Preconditioner to use.
fipy.solvers.scipy.linearCGSSolver module
fipy.solvers.scipy.linearGMRESSolver module
class fipy.solvers.scipy.linearGMRESSolver.LinearGMRESSolver(tolerance=1e-15,
iterations=2000,
precon=None)
Bases: fipy.solvers.scipy.scipyKrylovSolver._ScipyKrylovSolver
The LinearGMRESSolver is an interface to the GMRES solver in Scipy, with no preconditioning by default.
Parameters
• tolerance: The required error tolerance.
• iterations: The maximum number of iterative steps to perform.
• precon: Preconditioner to use.
fipy.solvers.scipy.linearLUSolver module
fipy.solvers.scipy.linearPCGSolver module
fipy.solvers.scipy.scipyKrylovSolver module
fipy.solvers.scipy.scipySolver module
Module contents
fipy.solvers.scipy.DefaultSolver
alias of LinearLUSolver
fipy.solvers.scipy.DummySolver
alias of LinearGMRESSolver
fipy.solvers.scipy.DefaultAsymmetricSolver
alias of LinearLUSolver
fipy.solvers.scipy.GeneralSolver
alias of LinearLUSolver
class fipy.solvers.scipy.LinearCGSSolver(tolerance=1e-15, iterations=2000, precon=None)
Bases: fipy.solvers.scipy.scipyKrylovSolver._ScipyKrylovSolver
The LinearCGSSolver is an interface to the CGS solver in Scipy, with no preconditioning by default.
Parameters
• tolerance: The required error tolerance.
Subpackages
fipy.solvers.trilinos.preconditioners package
Submodules
fipy.solvers.trilinos.preconditioners.domDecompPreconditioner module
fipy.solvers.trilinos.preconditioners.icPreconditioner module
fipy.solvers.trilinos.preconditioners.jacobiPreconditioner module
fipy.solvers.trilinos.preconditioners.multilevelDDMLPreconditioner module
fipy.solvers.trilinos.preconditioners.multilevelDDPreconditioner module
fipy.solvers.trilinos.preconditioners.multilevelNSSAPreconditioner module
fipy.solvers.trilinos.preconditioners.multilevelSAPreconditioner module
fipy.solvers.trilinos.preconditioners.multilevelSGSPreconditioner module
fipy.solvers.trilinos.preconditioners.multilevelSolverSmootherPreconditioner module
fipy.solvers.trilinos.preconditioners.preconditioner module
Module contents
Submodules
fipy.solvers.trilinos.linearBicgstabSolver module
fipy.solvers.trilinos.linearCGSSolver module
fipy.solvers.trilinos.linearGMRESSolver module
fipy.solvers.trilinos.linearLUSolver module
fipy.solvers.trilinos.linearPCGSolver module
fipy.solvers.trilinos.trilinosAztecOOSolver module
fipy.solvers.trilinos.trilinosMLTest module
fipy.solvers.trilinos.trilinosNonlinearSolver module
fipy.solvers.trilinos.trilinosSolver module
Module contents
22.2 Submodules
The iterative solvers may output warnings if the solution is considered unsatisfactory. If you are not interested in these
warnings, you can invoke python with a warning filter such as:
$ python -Wignore::fipy.SolverConvergenceWarning myscript.py
If you are extremely concerned about your preconditioner for some reason, you can abort whenever it has problems
with:
$ python -Werror::fipy.PreconditionerWarning myscript.py
• precon: Preconditioner to use. This parameter is only available for Trilinos solvers.
23.1 Submodules
class fipy.steppers.stepper.Stepper(vardata=())
247
FiPy Manual, Release 3.1.3
24.1 Submodules
class fipy.terms.advectionTerm.AdvectionTerm(coeff=None)
Bases: fipy.terms.firstOrderAdvectionTerm.FirstOrderAdvectionTerm
The AdvectionTerm object constructs the b vector contribution for the advection term given by
𝑢|∇𝜑|
where,
𝜑𝐴 − 𝜑𝑃 𝑑𝐴𝑃
𝐷𝐴𝑃 = − 𝑚 (𝐿𝐴 , 𝐿𝑃 )
𝑑𝐴𝑃 2
and
𝑚 (𝑥, 𝑦) = 𝑥 if |𝑥| ≤ |𝑦|∀𝑥𝑦 ≥ 0
𝑚 (𝑥, 𝑦) = 𝑦 if |𝑥| > |𝑦|∀𝑥𝑦 ≥ 0
𝑚 (𝑥, 𝑦) = 0 if 𝑥𝑦 < 0
251
FiPy Manual, Release 3.1.3
also,
𝜑𝐴𝐴 + 𝜑𝑃 − 2𝜑𝐴
𝐿𝐴 =
𝑑2𝐴𝑃
𝜑𝐴 + 𝜑𝑃 𝑃 − 2𝜑𝑃
𝐿𝑃 =
𝑑2𝐴𝑃
Trivial test:
>>> from fipy.variables.cellVariable import CellVariable
>>> coeff = CellVariable(mesh = mesh, value = numerix.zeros(3, 'd'))
>>> v, L, b = AdvectionTerm(0.)._buildMatrix(coeff, SparseMatrix)
>>> print numerix.allclose(b, numerix.zeros(3, 'd'), atol = 1e-10)
True
Another trivial test case (more trivial than a trivial test case standing on a harpsichord singing ‘trivial test cases
are here again’)
>>> vel = numerix.array((-1, 2, -3))
>>> coeff = CellVariable(mesh = mesh, value = numerix.array((4,6,1)))
>>> v, L, b = AdvectionTerm(vel)._buildMatrix(coeff, SparseMatrix)
>>> print numerix.allclose(b, -vel * numerix.array((2, numerix.sqrt(5**2 + 2**2), 5)), atol = 1e
True
For the above test cases the AdvectionTerm gives the same result as the AdvectionTerm. The following test
imposes a quadratic field. The higher order term can resolve this field correctly.
𝜑 = 𝑥2
The first order term is not accurate. The first and last element are ignored because they don’t have any neighbors
for higher order evaluation
>>> print numerix.allclose(CellVariable(mesh=mesh,
... value=b).globalValue[1:-1], -2 * mesh.cellCenters.globalValue[0][1:-1])
False
The AdvectionTerm will also resolve a circular field with more accuracy,
)︀1/2
𝜑 = 𝑥2 + 𝑦 2
(︀
The maximum error is large (about 12 %) for the first order advection.
>>> v, L, b = AdvectionTerm(1.)._buildMatrix(coeff, SparseMatrix)
>>> error = CellVariable(mesh=mesh, value=b + 1)
>>> ans = CellVariable(mesh=mesh, value=b + 1)
>>> ans[(x > 2) & (x < 8) & (y > 2) & (y < 8)] = 0.0201715476598
>>> print (error <= ans).all()
True
class fipy.terms.centralDiffConvectionTerm.CentralDifferenceConvectionTerm(coeff=1.0,
var=None)
Bases: fipy.terms.abstractConvectionTerm._AbstractConvectionTerm
This Term represents
∫︁ ∑︁
∇ · (⃗𝑢𝜑) 𝑑𝑉 ≃ (⃗𝑛 · ⃗𝑢)𝑓 𝜑𝑓 𝐴𝑓
𝑉 𝑓
where 𝜑𝑓 = 𝛼𝑓 𝜑𝑃 + (1 − 𝛼𝑓 )𝜑𝐴 and 𝛼𝑓 is calculated using the central differencing scheme. For further details
see Numerical Schemes.
Create a _AbstractConvectionTerm object.
>>> from fipy import *
>>> m = Grid1D(nx = 2)
>>> cv = CellVariable(mesh = m)
>>> fv = FaceVariable(mesh = m)
>>> vcv = CellVariable(mesh=m, rank=1)
>>> vfv = FaceVariable(mesh=m, rank=1)
>>> __ConvectionTerm(coeff = cv)
Traceback (most recent call last):
...
VectorCoeffError: The coefficient must be a vector value.
>>> __ConvectionTerm(coeff = fv)
Traceback (most recent call last):
...
VectorCoeffError: The coefficient must be a vector value.
>>> __ConvectionTerm(coeff = vcv)
__ConvectionTerm(coeff=_ArithmeticCellToFaceVariable(value=array([[ 0., 0., 0.]]), mesh=Unifor
>>> __ConvectionTerm(coeff = vfv)
__ConvectionTerm(coeff=FaceVariable(value=array([[ 0., 0., 0.]]), mesh=UniformGrid1D(dx=1.0, n
>>> __ConvectionTerm(coeff = (1,))
__ConvectionTerm(coeff=(1,))
>>> ExplicitUpwindConvectionTerm(coeff = (0,)).solve(var=cv, solver=DummySolver())
Traceback (most recent call last):
...
TransientTermError: The equation requires a TransientTerm with explicit convection.
>>> (TransientTerm(0.) - ExplicitUpwindConvectionTerm(coeff = (0,))).solve(var=cv, solver=DummyS
Parameters
• coeff : The Term‘s coefficient value.
∇ · (𝐷1 ∇𝜑)
and:
DiffusionTerm((D1,D2))
and so on.
class fipy.terms.diffusionTerm.DiffusionTermCorrection(coeff=(1.0, ), var=None)
Bases: fipy.terms.abstractDiffusionTerm._AbstractDiffusionTerm
class fipy.terms.diffusionTerm.DiffusionTermNoCorrection(coeff=(1.0, ), var=None)
Bases: fipy.terms.abstractDiffusionTerm._AbstractDiffusionTerm
class fipy.terms.diffusionTermCorrection.DiffusionTermCorrection(coeff=(1.0, ),
var=None)
Bases: fipy.terms.abstractDiffusionTerm._AbstractDiffusionTerm
class fipy.terms.diffusionTermNoCorrection.DiffusionTermNoCorrection(coeff=(1.0,
),
var=None)
Bases: fipy.terms.abstractDiffusionTerm._AbstractDiffusionTerm
class fipy.terms.explicitDiffusionTerm.ExplicitDiffusionTerm(coeff=(1.0, ),
var=None)
Bases: fipy.terms.abstractDiffusionTerm._AbstractDiffusionTerm
The discretization for the ExplicitDiffusionTerm is given by
∫︁ ∑︁ 𝜑old − 𝜑old
∇ · (Γ∇𝜑)𝑑𝑉 ≃ Γ𝑓 𝐴 𝑃
𝐴𝑓
𝑉 𝑑𝐴𝑃
𝑓
class fipy.terms.explicitUpwindConvectionTerm.ExplicitUpwindConvectionTerm(coeff=1.0,
var=None)
Bases: fipy.terms.abstractUpwindConvectionTerm._AbstractUpwindConvectionTerm
The discretization for this Term is given by
∫︁ ∑︁
∇ · (⃗𝑢𝜑) 𝑑𝑉 ≃ (⃗𝑛 · ⃗𝑢)𝑓 𝜑𝑓 𝐴𝑓
𝑉 𝑓
Parameters
• coeff : The Term‘s coefficient value.
class fipy.terms.exponentialConvectionTerm.ExponentialConvectionTerm(coeff=1.0,
var=None)
Bases: fipy.terms.asymmetricConvectionTerm._AsymmetricConvectionTerm
The discretization for this Term is given by
∫︁ ∑︁
∇ · (⃗𝑢𝜑) 𝑑𝑉 ≃ (⃗𝑛 · ⃗𝑢)𝑓 𝜑𝑓 𝐴𝑓
𝑉 𝑓
where 𝜑𝑓 = 𝛼𝑓 𝜑𝑃 + (1 − 𝛼𝑓 )𝜑𝐴 and 𝛼𝑓 is calculated using the exponential scheme. For further details see
Numerical Schemes.
Create a _AbstractConvectionTerm object.
Parameters
• coeff : The Term‘s coefficient value.
class fipy.terms.firstOrderAdvectionTerm.FirstOrderAdvectionTerm(coeff=None)
Bases: fipy.terms.nonDiffusionTerm._NonDiffusionTerm
The FirstOrderAdvectionTerm object constructs the b vector contribution for the advection term given by
𝑢|∇𝜑|
𝜕𝜑
+ 𝑢|∇𝜑| = 0
𝜕𝑡
The construction of the gradient magnitude term requires upwinding. The formula used here is given by:
[︃ (︂ )︂2 ]︃1/2 [︃ (︂ )︂2 ]︃1/2
∑︁ 𝜑𝐴 − 𝜑𝑃 ∑︁ 𝜑𝐴 − 𝜑𝑃
𝑢𝑃 |∇𝜑|𝑃 = max (𝑢𝑃 , 0) min ,0 + min (𝑢𝑃 , 0) max ,0
𝑑𝐴𝑃 𝑑𝐴𝑃
𝐴 𝐴
Trivial test:
>>> var = CellVariable(value = numerix.zeros(3, 'd'), mesh = mesh)
>>> v, L, b = FirstOrderAdvectionTerm(0.)._buildMatrix(var, SparseMatrix)
>>> print numerix.allclose(b, numerix.zeros(3, 'd'), atol = 1e-10)
True
Another trivial test case (more trivial than a trivial test case standing on a harpsichord singing ‘trivial test cases
are here again’)
>>> vel = numerix.array((-1, 2, -3))
>>> var = CellVariable(value = numerix.array((4,6,1)), mesh = mesh)
>>> v, L, b = FirstOrderAdvectionTerm(vel)._buildMatrix(var, SparseMatrix)
>>> print numerix.allclose(b, -vel * numerix.array((2, numerix.sqrt(5**2 + 2**2), 5)), atol = 1e
True
where 𝜑𝑓 = 𝛼𝑓 𝜑𝑃 +(1−𝛼𝑓 )𝜑𝐴 and 𝛼𝑓 is calculated using the hybrid scheme. For further details see Numerical
Schemes.
Create a _AbstractConvectionTerm object.
>>> from fipy import *
>>> m = Grid1D(nx = 2)
>>> cv = CellVariable(mesh = m)
>>> fv = FaceVariable(mesh = m)
>>> vcv = CellVariable(mesh=m, rank=1)
>>> vfv = FaceVariable(mesh=m, rank=1)
>>> __ConvectionTerm(coeff = cv)
Traceback (most recent call last):
...
VectorCoeffError: The coefficient must be a vector value.
>>> __ConvectionTerm(coeff = fv)
Traceback (most recent call last):
...
VectorCoeffError: The coefficient must be a vector value.
>>> __ConvectionTerm(coeff = vcv)
__ConvectionTerm(coeff=_ArithmeticCellToFaceVariable(value=array([[ 0., 0., 0.]]), mesh=Unifor
>>> __ConvectionTerm(coeff = vfv)
__ConvectionTerm(coeff=FaceVariable(value=array([[ 0., 0., 0.]]), mesh=UniformGrid1D(dx=1.0, n
>>> __ConvectionTerm(coeff = (1,))
__ConvectionTerm(coeff=(1,))
>>> ExplicitUpwindConvectionTerm(coeff = (0,)).solve(var=cv, solver=DummySolver())
Traceback (most recent call last):
...
TransientTermError: The equation requires a TransientTerm with explicit convection.
>>> (TransientTerm(0.) - ExplicitUpwindConvectionTerm(coeff = (0,))).solve(var=cv, solver=DummyS
Parameters
• coeff : The Term‘s coefficient value.
fipy.terms.implicitDiffusionTerm.ImplicitDiffusionTerm
alias of DiffusionTerm
class fipy.terms.powerLawConvectionTerm.PowerLawConvectionTerm(coeff=1.0,
var=None)
Bases: fipy.terms.asymmetricConvectionTerm._AsymmetricConvectionTerm
The discretization for this Term is given by
∫︁ ∑︁
∇ · (⃗𝑢𝜑) 𝑑𝑉 ≃ (⃗𝑛 · ⃗𝑢)𝑓 𝜑𝑓 𝐴𝑓
𝑉 𝑓
where 𝜑𝑓 = 𝛼𝑓 𝜑𝑃 + (1 − 𝛼𝑓 )𝜑𝐴 and 𝛼𝑓 is calculated using the power law scheme. For further details see
Numerical Schemes.
Create a _AbstractConvectionTerm object.
Parameters
• coeff : The Term‘s coefficient value.
Create a Term.
Parameters
• coeff : The coefficient for the term. A CellVariable or number. FaceVariable objects are also
acceptable for diffusion or convection terms.
RHSvector
Return the RHS vector caculated in solve() or sweep(). The cacheRHSvector() method should be called
before solve() or sweep() to cache the vector.
cacheMatrix()
Informs solve() and sweep() to cache their matrix so that getMatrix() can return the matrix.
cacheRHSvector()
Informs solve() and sweep() to cache their right hand side vector so that getRHSvector() can return it.
copy()
getDefaultSolver(var=None, solver=None, *args, **kwargs)
getMatrix(*args, **kwds)
Deprecated since version 3.0: use the matrix property instead
getRHSvector(*args, **kwds)
Deprecated since version 3.0: use the rHSvector property instead
justErrorVector(var=None, solver=None, boundaryConditions=(), dt=1.0, underRelax-
ation=None, residualFn=None)
Builds the Term‘s linear system once. This method also recalculates and returns the error as well as
applying under-relaxation.
Parameters
• var: The variable to be solved for. Provides the initial condition, the old value and holds
the solution on completion.
• solver: The iterative solver to be used to solve the linear system of equations. Defaults to
LinearPCGSolver for Pysparse and LinearLUSolver for Trilinos.
• boundaryConditions: A tuple of boundaryConditions.
• dt: The time step size.
matrix
Return the matrix caculated in solve() or sweep(). The cacheMatrix() method should be called before
solve() or sweep() to cache the matrix.
residualVectorAndNorm(var=None, solver=None, boundaryConditions=(), dt=None, underRe-
laxation=None, residualFn=None)
Builds the Term‘s linear system once. This method also recalculates and returns the residual as well as
applying under-relaxation.
Parameters
• var: The variable to be solved for. Provides the initial condition, the old value and holds
the solution on completion.
• solver: The iterative solver to be used to solve the linear system of equations. Defaults to
LinearPCGSolver for Pysparse and LinearLUSolver for Trilinos.
• boundaryConditions: A tuple of boundaryConditions.
𝜕𝜑2
= 𝑘.
𝜕𝑡
The analytic solution is given by
√︁
𝜑= 𝜑20 + 𝑘𝑡,
A number of sweeps at each time step are required to let the relaxation take effect.
>>> for step in range(steps):
... var.updateOld()
... for sweep in range(sweeps):
... eq.solve(var, dt = dt)
where 𝜑𝑓 = 𝛼𝑓 𝜑𝑃 + (1 − 𝛼𝑓 )𝜑𝐴 and 𝛼𝑓 is calculated using the upwind convection scheme. For further details
see Numerical Schemes.
Create a _AbstractConvectionTerm object.
>>> from fipy import *
>>> m = Grid1D(nx = 2)
>>> cv = CellVariable(mesh = m)
>>> fv = FaceVariable(mesh = m)
>>> vcv = CellVariable(mesh=m, rank=1)
>>> vfv = FaceVariable(mesh=m, rank=1)
>>> __ConvectionTerm(coeff = cv)
Traceback (most recent call last):
...
VectorCoeffError: The coefficient must be a vector value.
>>> __ConvectionTerm(coeff = fv)
Traceback (most recent call last):
...
VectorCoeffError: The coefficient must be a vector value.
>>> __ConvectionTerm(coeff = vcv)
__ConvectionTerm(coeff=_ArithmeticCellToFaceVariable(value=array([[ 0., 0., 0.]]), mesh=Unifor
>>> __ConvectionTerm(coeff = vfv)
__ConvectionTerm(coeff=FaceVariable(value=array([[ 0., 0., 0.]]), mesh=UniformGrid1D(dx=1.0, n
>>> __ConvectionTerm(coeff = (1,))
__ConvectionTerm(coeff=(1,))
>>> ExplicitUpwindConvectionTerm(coeff = (0,)).solve(var=cv, solver=DummySolver())
Traceback (most recent call last):
...
TransientTermError: The equation requires a TransientTerm with explicit convection.
>>> (TransientTerm(0.) - ExplicitUpwindConvectionTerm(coeff = (0,))).solve(var=cv, solver=DummyS
Parameters
• coeff : The Term‘s coefficient value.
Parameters
• coeff : The Term‘s coefficient value.
𝑢|∇𝜑|
𝜕𝜑
+ 𝑢|∇𝜑| = 0
𝜕𝑡
The construction of the gradient magnitude term requires upwinding. The formula used here is given by:
[︃ (︂ )︂2 ]︃1/2 [︃ (︂ )︂2 ]︃1/2
∑︁ 𝜑𝐴 − 𝜑𝑃 ∑︁ 𝜑𝐴 − 𝜑𝑃
𝑢𝑃 |∇𝜑|𝑃 = max (𝑢𝑃 , 0) min ,0 + min (𝑢𝑃 , 0) max ,0
𝑑𝐴𝑃 𝑑𝐴𝑃
𝐴 𝐴
Trivial test:
>>> var = CellVariable(value = numerix.zeros(3, 'd'), mesh = mesh)
>>> v, L, b = FirstOrderAdvectionTerm(0.)._buildMatrix(var, SparseMatrix)
>>> print numerix.allclose(b, numerix.zeros(3, 'd'), atol = 1e-10)
True
Another trivial test case (more trivial than a trivial test case standing on a harpsichord singing ‘trivial test cases
are here again’)
>>> vel = numerix.array((-1, 2, -3))
>>> var = CellVariable(value = numerix.array((4,6,1)), mesh = mesh)
>>> v, L, b = FirstOrderAdvectionTerm(vel)._buildMatrix(var, SparseMatrix)
>>> print numerix.allclose(b, -vel * numerix.array((2, numerix.sqrt(5**2 + 2**2), 5)), atol = 1e
True
class fipy.terms.AdvectionTerm(coeff=None)
Bases: fipy.terms.firstOrderAdvectionTerm.FirstOrderAdvectionTerm
The AdvectionTerm object constructs the b vector contribution for the advection term given by
𝑢|∇𝜑|
from the advection equation given by:
𝜕𝜑
+ 𝑢|∇𝜑| = 0
𝜕𝑡
The construction of the gradient magnitude term requires upwinding as in the standard FirstOrderAdvection-
Term. The higher order terms are incorperated as follows. The formula used here is given by:
[︃ ]︃1/2 [︃ ]︃1/2
∑︁ 2
∑︁ 2
𝑢𝑃 |∇𝜑|𝑃 = max (𝑢𝑃 , 0) min (𝐷𝐴𝑃 , 0) + min (𝑢𝑃 , 0) max (𝐷𝐴𝑃 , 0)
𝐴 𝐴
where,
𝜑𝐴 − 𝜑𝑃 𝑑𝐴𝑃
𝐷𝐴𝑃 = − 𝑚 (𝐿𝐴 , 𝐿𝑃 )
𝑑𝐴𝑃 2
and
𝑚 (𝑥, 𝑦) = 𝑥 if |𝑥| ≤ |𝑦|∀𝑥𝑦 ≥ 0
𝑚 (𝑥, 𝑦) = 𝑦 if |𝑥| > |𝑦|∀𝑥𝑦 ≥ 0
𝑚 (𝑥, 𝑦) = 0 if 𝑥𝑦 < 0
also,
𝜑𝐴𝐴 + 𝜑𝑃 − 2𝜑𝐴
𝐿𝐴 =
𝑑2𝐴𝑃
𝜑𝐴 + 𝜑𝑃 𝑃 − 2𝜑𝑃
𝐿𝑃 =
𝑑2𝐴𝑃
Trivial test:
>>> from fipy.variables.cellVariable import CellVariable
>>> coeff = CellVariable(mesh = mesh, value = numerix.zeros(3, 'd'))
>>> v, L, b = AdvectionTerm(0.)._buildMatrix(coeff, SparseMatrix)
>>> print numerix.allclose(b, numerix.zeros(3, 'd'), atol = 1e-10)
True
Another trivial test case (more trivial than a trivial test case standing on a harpsichord singing ‘trivial test cases
are here again’)
>>> vel = numerix.array((-1, 2, -3))
>>> coeff = CellVariable(mesh = mesh, value = numerix.array((4,6,1)))
>>> v, L, b = AdvectionTerm(vel)._buildMatrix(coeff, SparseMatrix)
>>> print numerix.allclose(b, -vel * numerix.array((2, numerix.sqrt(5**2 + 2**2), 5)), atol = 1e
True
For the above test cases the AdvectionTerm gives the same result as the AdvectionTerm. The following test
imposes a quadratic field. The higher order term can resolve this field correctly.
𝜑 = 𝑥2
The first order term is not accurate. The first and last element are ignored because they don’t have any neighbors
for higher order evaluation
>>> print numerix.allclose(CellVariable(mesh=mesh,
... value=b).globalValue[1:-1], -2 * mesh.cellCenters.globalValue[0][1:-1])
False
The AdvectionTerm will also resolve a circular field with more accuracy,
)︀1/2
𝜑 = 𝑥2 + 𝑦 2
(︀
The maximum error is large (about 12 %) for the first order advection.
>>> v, L, b = AdvectionTerm(1.)._buildMatrix(coeff, SparseMatrix)
>>> error = CellVariable(mesh=mesh, value=b + 1)
>>> ans = CellVariable(mesh=mesh, value=b + 1)
>>> ans[(x > 2) & (x < 8) & (y > 2) & (y < 8)] = 0.0201715476598
>>> print (error <= ans).all()
True
𝜕𝜑2
= 𝑘.
𝜕𝑡
The analytic solution is given by
√︁
𝜑= 𝜑20 + 𝑘𝑡,
A number of sweeps at each time step are required to let the relaxation take effect.
>>> for step in range(steps):
... var.updateOld()
... for sweep in range(sweeps):
... eq.solve(var, dt = dt)
∇ · (𝐷1 ∇𝜑)
and:
DiffusionTerm((D1,D2))
and so on.
class fipy.terms.DiffusionTermCorrection(coeff=(1.0, ), var=None)
Bases: fipy.terms.abstractDiffusionTerm._AbstractDiffusionTerm
class fipy.terms.DiffusionTermNoCorrection(coeff=(1.0, ), var=None)
Bases: fipy.terms.abstractDiffusionTerm._AbstractDiffusionTerm
class fipy.terms.DiffusionTermCorrection(coeff=(1.0, ), var=None)
Bases: fipy.terms.abstractDiffusionTerm._AbstractDiffusionTerm
class fipy.terms.DiffusionTermNoCorrection(coeff=(1.0, ), var=None)
Bases: fipy.terms.abstractDiffusionTerm._AbstractDiffusionTerm
class fipy.terms.ExplicitDiffusionTerm(coeff=(1.0, ), var=None)
Bases: fipy.terms.abstractDiffusionTerm._AbstractDiffusionTerm
The discretization for the ExplicitDiffusionTerm is given by
∫︁ ∑︁ 𝜑old − 𝜑old
∇ · (Γ∇𝜑)𝑑𝑉 ≃ Γ𝑓 𝐴 𝑃
𝐴𝑓
𝑉 𝑑𝐴𝑃
𝑓
where 𝜑𝑓 = 𝛼𝑓 𝜑𝑃 + (1 − 𝛼𝑓 )𝜑𝐴 and 𝛼𝑓 is calculated using the central differencing scheme. For further details
see Numerical Schemes.
Create a _AbstractConvectionTerm object.
Parameters
• coeff : The Term‘s coefficient value.
Parameters
• coeff : The Term‘s coefficient value.
where 𝜑𝑓 = 𝛼𝑓 𝜑𝑃 + (1 − 𝛼𝑓 )𝜑𝐴 and 𝛼𝑓 is calculated using the exponential scheme. For further details see
Numerical Schemes.
Create a _AbstractConvectionTerm object.
Parameters
• coeff : The Term‘s coefficient value.
where 𝜑𝑓 = 𝛼𝑓 𝜑𝑃 +(1−𝛼𝑓 )𝜑𝐴 and 𝛼𝑓 is calculated using the hybrid scheme. For further details see Numerical
Schemes.
Create a _AbstractConvectionTerm object.
Parameters
• coeff : The Term‘s coefficient value.
where 𝜑𝑓 = 𝛼𝑓 𝜑𝑃 + (1 − 𝛼𝑓 )𝜑𝐴 and 𝛼𝑓 is calculated using the power law scheme. For further details see
Numerical Schemes.
Create a _AbstractConvectionTerm object.
Parameters
• coeff : The Term‘s coefficient value.
where 𝜑𝑓 = 𝛼𝑓 𝜑𝑃 + (1 − 𝛼𝑓 )𝜑𝐴 and 𝛼𝑓 is calculated using the upwind convection scheme. For further details
see Numerical Schemes.
Create a _AbstractConvectionTerm object.
Parameters
• coeff : The Term‘s coefficient value.
Parameters
• coeff : The Term‘s coefficient value.
class fipy.terms.FirstOrderAdvectionTerm(coeff=None)
Bases: fipy.terms.nonDiffusionTerm._NonDiffusionTerm
The FirstOrderAdvectionTerm object constructs the b vector contribution for the advection term given by
𝑢|∇𝜑|
from the advection equation given by:
𝜕𝜑
+ 𝑢|∇𝜑| = 0
𝜕𝑡
The construction of the gradient magnitude term requires upwinding. The formula used here is given by:
[︃ (︂ )︂2 ]︃1/2 [︃ (︂ )︂2 ]︃1/2
∑︁ 𝜑𝐴 − 𝜑𝑃 ∑︁ 𝜑𝐴 − 𝜑𝑃
𝑢𝑃 |∇𝜑|𝑃 = max (𝑢𝑃 , 0) min ,0 + min (𝑢𝑃 , 0) max ,0
𝑑𝐴𝑃 𝑑𝐴𝑃
𝐴 𝐴
Trivial test:
>>> var = CellVariable(value = numerix.zeros(3, 'd'), mesh = mesh)
>>> v, L, b = FirstOrderAdvectionTerm(0.)._buildMatrix(var, SparseMatrix)
>>> print numerix.allclose(b, numerix.zeros(3, 'd'), atol = 1e-10)
True
Another trivial test case (more trivial than a trivial test case standing on a harpsichord singing ‘trivial test cases
are here again’)
>>> vel = numerix.array((-1, 2, -3))
>>> var = CellVariable(value = numerix.array((4,6,1)), mesh = mesh)
>>> v, L, b = FirstOrderAdvectionTerm(vel)._buildMatrix(var, SparseMatrix)
>>> print numerix.allclose(b, -vel * numerix.array((2, numerix.sqrt(5**2 + 2**2), 5)), atol = 1e
True
class fipy.terms.AdvectionTerm(coeff=None)
Bases: fipy.terms.firstOrderAdvectionTerm.FirstOrderAdvectionTerm
The AdvectionTerm object constructs the b vector contribution for the advection term given by
𝑢|∇𝜑|
𝜕𝜑
+ 𝑢|∇𝜑| = 0
𝜕𝑡
The construction of the gradient magnitude term requires upwinding as in the standard FirstOrderAdvection-
Term. The higher order terms are incorperated as follows. The formula used here is given by:
[︃ ]︃1/2 [︃ ]︃1/2
∑︁ 2
∑︁ 2
𝑢𝑃 |∇𝜑|𝑃 = max (𝑢𝑃 , 0) min (𝐷𝐴𝑃 , 0) + min (𝑢𝑃 , 0) max (𝐷𝐴𝑃 , 0)
𝐴 𝐴
where,
𝜑𝐴 − 𝜑𝑃 𝑑𝐴𝑃
𝐷𝐴𝑃 = − 𝑚 (𝐿𝐴 , 𝐿𝑃 )
𝑑𝐴𝑃 2
and
𝑚 (𝑥, 𝑦) = 𝑥 if |𝑥| ≤ |𝑦|∀𝑥𝑦 ≥ 0
𝑚 (𝑥, 𝑦) = 𝑦 if |𝑥| > |𝑦|∀𝑥𝑦 ≥ 0
𝑚 (𝑥, 𝑦) = 0 if 𝑥𝑦 < 0
also,
𝜑𝐴𝐴 + 𝜑𝑃 − 2𝜑𝐴
𝐿𝐴 =
𝑑2𝐴𝑃
𝜑𝐴 + 𝜑𝑃 𝑃 − 2𝜑𝑃
𝐿𝑃 =
𝑑2𝐴𝑃
Here are some simple test cases for this problem:
>>> from fipy.meshes import Grid1D
>>> from fipy.solvers import *
>>> SparseMatrix = LinearPCGSolver()._matrixClass
>>> mesh = Grid1D(dx = 1., nx = 3)
Trivial test:
>>> from fipy.variables.cellVariable import CellVariable
>>> coeff = CellVariable(mesh = mesh, value = numerix.zeros(3, 'd'))
>>> v, L, b = AdvectionTerm(0.)._buildMatrix(coeff, SparseMatrix)
>>> print numerix.allclose(b, numerix.zeros(3, 'd'), atol = 1e-10)
True
Another trivial test case (more trivial than a trivial test case standing on a harpsichord singing ‘trivial test cases
are here again’)
>>> vel = numerix.array((-1, 2, -3))
>>> coeff = CellVariable(mesh = mesh, value = numerix.array((4,6,1)))
>>> v, L, b = AdvectionTerm(vel)._buildMatrix(coeff, SparseMatrix)
>>> print numerix.allclose(b, -vel * numerix.array((2, numerix.sqrt(5**2 + 2**2), 5)), atol = 1e
True
For the above test cases the AdvectionTerm gives the same result as the AdvectionTerm. The following test
imposes a quadratic field. The higher order term can resolve this field correctly.
𝜑 = 𝑥2
The first order term is not accurate. The first and last element are ignored because they don’t have any neighbors
for higher order evaluation
>>> print numerix.allclose(CellVariable(mesh=mesh,
... value=b).globalValue[1:-1], -2 * mesh.cellCenters.globalValue[0][1:-1])
False
The AdvectionTerm will also resolve a circular field with more accuracy,
)︀1/2
𝜑 = 𝑥2 + 𝑦 2
(︀
The maximum error is large (about 12 %) for the first order advection.
25.1 Submodules
fipy.tests.doctestPlus.execButNoTest(name=’__main__’)
fipy.tests.doctestPlus.register_skipper(flag, test, why, skipWarning=True)
Create a new doctest option flag for skipping tests
Parameters flag : str
Name of the option flag
test : function
A function which should return True if the test should be run
why : str
Explanation for why the test was skipped (to be used in a string “Skipped
%%(count)d doctest examples because %%(why)s”)
skipWarning : bool
Whether or not to report on tests skipped by this flag (default True)
fipy.tests.doctestPlus.report_skips()
Print out how many doctest examples were skipped due to flags
fipy.tests.doctestPlus.testmod(m=None, name=None, globs=None, verbose=None,
report=True, optionflags=0, extraglobs=None,
raise_on_error=False, exclude_empty=False)
Test examples in the given module. Return (#failures, #tests).
Largely duplicated from doctest.testmod(), but using _SelectiveDocTestParser.
Test examples in docstrings in functions and classes reachable from module m (or the current module if m is not
supplied), starting with m.__doc__.
Also test examples reachable from dict m.__test__ if it exists and is not None. m.__test__ maps names to
functions, classes and strings; function and class docstrings are tested even if the name is private; strings are
tested directly, as if they were docstrings.
Return (#failures, #tests).
See help(doctest) for an overview.
Optional keyword arg “name” gives the name of the module; by default use m.__name__.
287
FiPy Manual, Release 3.1.3
Optional keyword arg “globs” gives a dict to be used as the globals when executing examples; by default, use
m.__dict__. A copy of this dict is actually used for each docstring, so that each docstring’s examples start with
a clean slate.
Optional keyword arg “extraglobs” gives a dictionary that should be merged into the globals that are used to
execute examples. By default, no extra globals are used. This is new in 2.4.
Optional keyword arg “verbose” prints lots of stuff if true, prints only failures if false; by default, it’s true iff
“-v” is in sys.argv.
Optional keyword arg “report” prints a summary at the end when true, else prints nothing at the end. In verbose
mode, the summary is detailed, else very brief (in fact, empty if all tests passed).
Optional keyword arg “optionflags” or’s together module constants, and defaults to 0. This is new in 2.3.
Possible values (see the docs for details):
DONT_ACCEPT_TRUE_FOR_1 DONT_ACCEPT_BLANKLINE NORMALIZE_WHITESPACE
ELLIPSIS SKIP IGNORE_EXCEPTION_DETAIL REPORT_UDIFF REPORT_CDIFF RE-
PORT_NDIFF REPORT_ONLY_FIRST_FAILURE
as well as FiPy’s flags
GMSH SCIPY TVTK SERIAL PARALLEL PROCESSOR_0 PROCESSOR_0_OF_2 PROCES-
SOR_1_OF_2 PROCESSOR_0_OF_3 PROCESSOR_1_OF_3 PROCESSOR_2_OF_3
Optional keyword arg “raise_on_error” raises an exception on the first unexpected exception or failure. This
allows failures to be post-mortem debugged.
26.1 Subpackages
Submodules
fipy.tools.comms.commWrapper module
class fipy.tools.comms.commWrapper.CommWrapper(Epetra=None)
Bases: object
MPI Communicator wrapper
Encapsulates capabilities needed for Epetra. Some capabilities are not parallel.
Barrier()
MaxAll(vec)
MinAll(vec)
Norm2(vec)
Nproc
all(a, axis=None)
allclose(a, b, rtol=1e-05, atol=1e-08)
allequal(a, b)
allgather(sendobj=None, recvobj=None)
any(a, axis=None)
bcast(obj=None, root=0)
procID
sum(a, axis=None)
class fipy.tools.comms.commWrapper.ParallelCommWrapper(Epetra=None)
Bases: fipy.tools.comms.commWrapper.CommWrapper
MPI Communicator wrapper for parallel processes
289
FiPy Manual, Release 3.1.3
fipy.tools.comms.dummyComm module
class fipy.tools.comms.dummyComm.DummyComm
Bases: fipy.tools.comms.serialCommWrapper.SerialCommWrapper
Barrier()
MaxAll(vec)
MinAll(vec)
sum(a, axis=None)
fipy.tools.comms.mpi4pyCommWrapper module
fipy.tools.comms.serialCommWrapper module
class fipy.tools.comms.serialCommWrapper.SerialCommWrapper(Epetra=None)
Bases: fipy.tools.comms.commWrapper.CommWrapper
Norm2(vec)
Nproc
procID
Module contents
Submodules
fipy.tools.dimensions.DictWithDefault module
fipy.tools.dimensions.NumberDict module
fipy.tools.dimensions.physicalField module
Warning: We can’t guarantee for the correctness of all entries in the unit table, so use this at your own risk!
Base SI units:
m, kg, s, A, K, mol, cd, rad, sr
SI prefixes:
Y = 1e+24
Z = 1e+21
E = 1e+18
P = 1e+15
T = 1e+12
G = 1e+09
M = 1e+06
k = 1000
h = 100
da = 10
d = 0.1
c = 0.01
m = 0.001
mu = 1e-06
n = 1e-09
p = 1e-12
f = 1e-15
a = 1e-18
z = 1e-21
y = 1e-24
1 V = 1 kg*m**2/A/s**3
1 W = 1 m**2*kg/s**3
1 Wb = 1 kg*m**2/A/s**2
1 mp = 1.67262171e-27 kg
1 mu0 = 1.25663706144e-06 kg*m/A**2/s**2
1 Nav = 6.0221415e+23 1/mol
1 nmi = 1852.0 m
1 oz = 0.028349523125 kg
1 psi = 6894.75729317 kg/s**2/m
1 pt = 0.000473176512 m**3
1 qt = 0.000946353024 m**3
1 tbsp = 1.4786766e-05 m**3
1 ton = 907.18474 kg
1 Torr = 133.322368421 kg/s**2/m
1 tsp = 4.928922e-06 m**3
1 wk = 604800.0 s
1 yd = 0.9144 m
1 yr = 31536000.0 s
1 yrJul = 31557600.0 s
1 yrSid = 31558152.96 s
•PhysicalField(*string*), where *string* contains both the value and the unit. This form is provided to
make interactive use more convenient
>>> print PhysicalField(value = "10. m")
10.0 m
Physical arrays are also possible (and are the reason this code was adapted from Konrad Hinsen‘s original
PhysicalQuantity). The value can be a Numeric array:
>>> a = numerix.array(((3.,4.),(5.,6.)))
>>> print PhysicalField(value = a, unit = "m")
[[ 3. 4.]
[ 5. 6.]] m
or a tuple:
>>> print PhysicalField(value = ((3.,4.),(5.,6.)), unit = "m")
[[ 3. 4.]
[ 5. 6.]] m
Every element in an array has the same unit, which is stored only once for the whole array.
add(other)
Add two physical quantities, so long as their units are compatible. The unit of the result is the unit of the
first operand.
>>> print PhysicalField(10., 'km') + PhysicalField(10., 'm')
10.01 km
>>> print PhysicalField(10., 'km') + PhysicalField(10., 'J')
Traceback (most recent call last):
...
TypeError: Incompatible units
This means essentially that both elements are small compared to atol or their difference divided by other‘s
value is small compared to rtol.
allequal(other)
This function tests whether or not self and other are exactly equal.
arccos()
Return the inverse cosine of the PhysicalField in radians
>>> print PhysicalField(0).arccos().allclose("1.57079632679 rad")
1
arccosh()
Return the inverse hyperbolic cosine of the PhysicalField
>>> print numerix.allclose(PhysicalField(2).arccosh(),
... 1.31695789692)
1
arcsin()
Return the inverse sine of the PhysicalField in radians
arctan()
Return the arctangent of the PhysicalField in radians
>>> print numerix.round_(PhysicalField(1).arctan(), 6)
0.785398
arctan2(other)
Return the arctangent of self divided by other in radians
>>> print numerix.round_(PhysicalField(2.).arctan2(PhysicalField(5.)), 6)
0.380506
arctanh()
Return the inverse hyperbolic tangent of the PhysicalField
>>> print PhysicalField(0.5).arctanh()
0.549306144334
ceil()
Return the smallest integer greater than or equal to the PhysicalField.
>>> print PhysicalField(2.2,"m").ceil()
3.0 m
conjugate()
Return the complex conjugate of the PhysicalField.
convertToUnit(unit)
Changes the unit to unit and adjusts the value such that the combination is equivalent. The new unit is by
a string containing its name. The new unit must be compatible with the previous unit of the object.
>>> e = PhysicalField('2.7 Hartree*Nav')
>>> e.convertToUnit('kcal/mol')
>>> print e
1694.27557621 kcal/mol
copy()
Make a duplicate.
>>> a = PhysicalField(1, unit = 'inch')
>>> b = a.copy()
cos()
Return the cosine of the PhysicalField
>>> print numerix.round_(PhysicalField(2*numerix.pi/6,"rad").cos(), 6)
0.5
>>> print numerix.round_(PhysicalField(60.,"deg").cos(), 6)
0.5
cosh()
Return the hyperbolic cosine of the PhysicalField
>>> PhysicalField(0.).cosh()
1.0
>>> PhysicalField(60.,"m").cosh()
Traceback (most recent call last):
...
TypeError: Incompatible units
divide(other)
Divide two physical quantities. The unit of the result is the unit of the first operand divided by the unit of
the second.
>>> print PhysicalField(10., 'm') / PhysicalField(2., 's')
5.0 m/s
As a special case, if the result is dimensionless, the value is returned without units, rather than with a
dimensionless unit of 1. This facilitates passing physical quantities to packages such as Numeric that
cannot use units, while ensuring the quantities have the desired units
>>> print (PhysicalField(1., 'inch')
... / PhysicalField(1., 'mm'))
25.4
dot(other)
Return the dot product of self with other. The resulting unit is the product of the units of self and other.
>>> v = PhysicalField(((5.,6.),(7.,8.)), "m")
>>> print PhysicalField(((1.,2.),(3.,4.)), "m").dot(v)
[ 26. 44.] m**2
floor()
Return the largest integer less than or equal to the PhysicalField.
>>> print PhysicalField(2.2,"m").floor()
2.0 m
getNumericValue(*args, **kwds)
Deprecated since version 3.0: use the numericValue property instead
getShape(*args, **kwds)
Deprecated since version 3.0: use the shape property instead
getUnit(*args, **kwds)
Deprecated since version 3.0: use the unit property instead
getsctype(default=None)
Returns the Numpy sctype of the underlying array.
>>> PhysicalField(1, 'm').getsctype() == numerix.NUMERIX.obj2sctype(numerix.array(1))
True
>>> PhysicalField(1., 'm').getsctype() == numerix.NUMERIX.obj2sctype(numerix.array(1.))
True
>>> PhysicalField((1,1.), 'm').getsctype() == numerix.NUMERIX.obj2sctype(numerix.array((1.,
True
inBaseUnits()
Return the quantity with all units reduced to their base SI elements.
>>> e = PhysicalField('2.7 Hartree*Nav')
>>> print e.inBaseUnits().allclose("7088849.01085 kg*m**2/s**2/mol")
1
inDimensionless()
Returns the numerical value of a dimensionless quantity.
inRadians()
Converts an angular quantity to radians and returns the numerical value.
inSIUnits()
Return the quantity with all units reduced to SI-compatible elements.
>>> e = PhysicalField('2.7 Hartree*Nav')
>>> print e.inSIUnits().allclose("7088849.01085 kg*m**2/s**2/mol")
1
inUnitsOf(*units)
Returns one or more PhysicalField objects that express the same physical quantity in different units. The
units are specified by strings containing their names. The units must be compatible with the unit of the
object. If one unit is specified, the return value is a single PhysicalField.
>>> freeze = PhysicalField('0 degC')
>>> print freeze.inUnitsOf('degF').allclose("32.0 degF")
1
If several units are specified, the return value is a tuple of PhysicalField instances with with one element per
unit such that the sum of all quantities in the tuple equals the the original quantity and all the values except
for the last one are integers. This is used to convert to irregular unit systems like hour/minute/second. The
original object will not be changed.
isCompatible(unit)
itemset(value)
Assign the value of a scalar array, performing appropriate conversions.
>>> a = PhysicalField(4.,"m")
>>> a.itemset(PhysicalField("6 ft"))
>>> print a.allclose("1.8288 m")
1
>>> a = PhysicalField(((3.,4.),(5.,6.)),"m")
>>> try:
... a.itemset(PhysicalField("6 ft"))
... except IndexError:
... # NumPy 1.7 has changed the exception type
... raise ValueError("can only place a scalar for an array of size 1")
Traceback (most recent call last):
...
ValueError: can only place a scalar for an array of size 1
>>> a.itemset(PhysicalField("2 min"))
Traceback (most recent call last):
...
TypeError: Incompatible units
itemsize
log()
Return the natural logarithm of the PhysicalField
>>> print numerix.round_(PhysicalField(10).log(), 6)
2.302585
log10()
Return the base-10 logarithm of the PhysicalField
>>> print numerix.round_(PhysicalField(10.).log10(), 6)
1.0
multiply(other)
Multiply two physical quantities. The unit of the result is the product of the units of the operands.
As a special case, if the result is dimensionless, the value is returned without units, rather than with a
dimensionless unit of 1. This facilitates passing physical quantities to packages such as Numeric that
cannot use units, while ensuring the quantities have the desired units.
>>> print (PhysicalField(10., 's') * PhysicalField(2., 'Hz'))
20.0
numericValue
Return the PhysicalField without units, after conversion to base SI units.
>>> print numerix.round_(PhysicalField("1 inch").numericValue, 6)
0.0254
put(indices, values)
put is the opposite of take. The values of self at the locations specified in indices are set to the correspond-
ing value of values.
The indices can be any integer sequence object with values suitable for indexing into the flat form of self.
The values must be any sequence of values that can be converted to the typecode of self.
>>> f = PhysicalField((1.,2.,3.),"m")
>>> f.put((2,0), PhysicalField((2.,3.),"inch"))
>>> print f
[ 0.0762 2. 0.0508] m
ravel()
reshape(shape)
Changes the shape of self to that specified in shape
>>> print PhysicalField((1.,2.,3.,4.),"m").reshape((2,2))
[[ 1. 2.]
[ 3. 4.]] m
The new shape must have the same size as the existing one.
>>> print PhysicalField((1.,2.,3.,4.),"m").reshape((2,3))
Traceback (most recent call last):
...
ValueError: total size of new array must be unchanged
setUnit(*args, **kwds)
Deprecated since version 3.0: use the unit property instead
shape
Tuple of array dimensions.
sign()
Return the sign of the quantity. The unit is unchanged.
sin()
Return the sine of the PhysicalField
>>> print PhysicalField(numerix.pi/6,"rad").sin()
0.5
>>> print PhysicalField(30.,"deg").sin()
0.5
sinh()
Return the hyperbolic sine of the PhysicalField
>>> PhysicalField(0.).sinh()
0.0
sqrt()
Return the square root of the PhysicalField
>>> print PhysicalField("100. m**2").sqrt()
10.0 m
subtract(other)
Subtract two physical quantities, so long as their units are compatible. The unit of the result is the unit of
the first operand.
>>> print PhysicalField(10., 'km') - PhysicalField(10., 'm')
9.99 km
>>> print PhysicalField(10., 'km') - PhysicalField(10., 'J')
Traceback (most recent call last):
...
TypeError: Incompatible units
sum(index=0)
Returns the sum of all of the elements in self along the specified axis (first axis by default).
take(indices, axis=0)
Return the elements of self specified by the elements of indices. The resulting PhysicalField array has the
same units as the original.
>>> print PhysicalField((1.,2.,3.),"m").take((2,0))
[ 3. 1.] m
The optional third argument specifies the axis along which the selection occurs, and the default value (as
in the example above) is 0, the first axis.
>>> print PhysicalField(((1.,2.,3.),(4.,5.,6.)),"m").take((2,0), axis = 1)
[[ 3. 1.]
[ 6. 4.]] m
tan()
Return the tangent of the PhysicalField
>>> numerix.round_(PhysicalField(numerix.pi/4,"rad").tan(), 6)
1.0
>>> numerix.round_(PhysicalField(45,"deg").tan(), 6)
1.0
tanh()
Return the hyperbolic tangent of the PhysicalField
>>> print numerix.allclose(PhysicalField(1.).tanh(), 0.761594155956)
True
unit
Return the unit object of self.
>>> PhysicalField("1 m").unit
<PhysicalUnit m>
If units have different offsets, they must have the same factor
>>> d = PhysicalField("1. degC")
>>> c.unit.conversionFactorTo(d.unit)
1.0
>>> e = PhysicalField("1. degF")
>>> c.unit.conversionFactorTo(e.unit)
Traceback (most recent call last):
...
TypeError: Unit conversion (K to degF) cannot be expressed as a simple multiplicative factor
conversionTupleTo(other)
Return a tuple of the multiplication factor and offset between two physical units
>>> a = PhysicalField("1. K").unit
>>> b = PhysicalField("1. degF").unit
>>> [str(numerix.round_(element,6)) for element in b.conversionTupleTo(a)]
['0.555556', '459.67']
isAngle()
Returns True if the unit is an angle
>>> PhysicalField("1. deg").unit.isAngle()
1
>>> PhysicalField("1. rad").unit.isAngle()
1
isCompatible(other)
Returns a list of which fundamental SI units are compatible between self and other
>>> a = PhysicalField("1. mm")
>>> b = PhysicalField("1. inch")
>>> print numerix.allclose(a.unit.isCompatible(b.unit),
... [True, True, True, True, True, True, True, True, True])
True
>>> c = PhysicalField("1. K")
>>> print numerix.allclose(a.unit.isCompatible(c.unit),
... [False, True, True, True, False, True, True, True, True])
True
isDimensionless()
Returns True if the unit is dimensionless
>>> PhysicalField("1. m/m").unit.isDimensionless()
1
>>> PhysicalField("1. inch").unit.isDimensionless()
0
isDimensionlessOrAngle()
Returns True if the unit is dimensionless or an angle
>>> PhysicalField("1. m/m").unit.isDimensionlessOrAngle()
1
>>> PhysicalField("1. deg").unit.isDimensionlessOrAngle()
1
>>> PhysicalField("1. rad").unit.isDimensionlessOrAngle()
1
>>> PhysicalField("1. inch").unit.isDimensionlessOrAngle()
0
isInverseAngle()
Returns True if the 1 divided by the unit is an angle
>>> PhysicalField("1. deg**-1").unit.isInverseAngle()
1
>>> PhysicalField("1. 1/rad").unit.isInverseAngle()
1
>>> PhysicalField("1. inch").unit.isInverseAngle()
0
name()
Return the name of the unit
>>> PhysicalField("1. m").unit.name()
'm'
>>> (PhysicalField("1. m") / PhysicalField("1. s")
... / PhysicalField("1. s")).unit.name()
'm/s**2'
setName(name)
Set the name of the unit to name
>>> a = PhysicalField("1. m/s").unit
>>> a
<PhysicalUnit m/s>
>>> a.setName('meterpersecond')
>>> a
<PhysicalUnit meterpersecond>
Module contents
Submodules
fipy.tools.performance.efficiencyTestGenerator module
fipy.tools.performance.efficiencyTestHistory module
fipy.tools.performance.efficiency_test module
class fipy.tools.performance.efficiency_test.Efficiency_test(dist)
Bases: distutils.cmd.Command
Create and initialize a new Command object. Most importantly, invokes the ‘initialize_options()’ method, which
is the real initializer and depends on the actual command being instantiated.
description = ‘run FiPy efficiency tests’
finalize_options()
initialize_options()
run()
user_options = [(‘minimumelements=’, None, ‘minimum number of elements’), (‘factor=’, None, ‘factor by which the
fipy.tools.performance.memoryLeak module
fipy.tools.performance.memoryLogger module
start()
stop()
fipy.tools.performance.memoryUsage module
Module contents
26.2 Submodules
class fipy.tools.copy_script.Copy_script(dist)
Bases: distutils.cmd.Command
Create and initialize a new Command object. Most importantly, invokes the ‘initialize_options()’ method, which
is the real initializer and depends on the actual command being instantiated.
description = ‘copy an example script into a new editable file’
finalize_options()
initialize_options()
run()
user_options = [(‘From=’, None, ‘path and file name containing script to copy’), (‘To=’, None, ‘path and file name to s
fipy.tools.decorators.getsetDeprecated(*args, **kwargs)
Issues a DeprecationWarning to use the appropriate property, rather than the get/set method of the same name
This function may also be used as a decorator.
Parameters func : function
The function to be deprecated.
old_name : str, optional
The name of the function to be deprecated. Default is None, in which case the name of
func is used.
new_name : str, optional
The new name for the function. Default is None, in which case the deprecation message
is that old_name is deprecated. If given, the deprecation message is that old_name is
deprecated and new_name should be used instead.
message : str, optional
Additional explanation of the deprecation. Displayed in the docstring after the warning.
Attention: This module should be the only place in the code where numpy is explicitly imported and you should
always import this module and not numpy in your own code. The documentation for numpy remains canonical
for all functions and classes not explicitly documented here.
The functions provided in ths module replace and augment the NumPy module. The functions work with Variables,
arrays or numbers. For example, create a Variable.
>>> from fipy.variables.variable import Variable
>>> var = Variable(value=0)
Take the tangent of such a variable. The returned value is itself a Variable.
>>> v = tan(var)
>>> v
tan(Variable(value=array(0)))
>>> print float(v)
0.0
fipy.tools.numerix.indices(dimensions, typecode=None)
indices(dimensions,typecode=None) returns an array representing a grid of indices with row-only, and column-
only variation.
>>> NUMERIX.allclose(NUMERIX.array(indices((4, 6))), NUMERIX.indices((4,6)))
1
>>> NUMERIX.allclose(NUMERIX.array(indices((4, 6, 2))), NUMERIX.indices((4, 6, 2)))
1
>>> NUMERIX.allclose(NUMERIX.array(indices((1,))), NUMERIX.indices((1,)))
1
>>> NUMERIX.allclose(NUMERIX.array(indices((5,))), NUMERIX.indices((5,)))
1
This means essentially that both elements are small compared to atol or their difference divided by second‘s
value is small compared to rtol.
fipy.tools.numerix.allclose(first, second, rtol=1e-05, atol=1e-08)
Tests whether or not first and second are equal, subect to the given relative and absolute tolerances, such that:
|first - second| < atol + rtol * |second|
This means essentially that both elements are small compared to atol or their difference divided by second‘s
value is small compared to rtol.
fipy.tools.numerix.all(a, axis=None, out=None)
Test whether all array elements along a given axis evaluate to True.
Parameters a : array_like
Input array or object that can be converted to an array.
axis : int, optional
Axis along which an logical AND is performed. The default (axis = None) is to perform
a logical AND over a flattened input array. axis may be negative, in which case it counts
from the last to the first axis.
out : ndarray, optional
Alternative output array in which to place the result. It must have the same shape as the
expected output and the type is preserved.
fipy.tools.numerix.rank(a)
Get the rank of sequence a (the number of dimensions, not a matrix rank) The rank of a scalar is zero.
Note: The rank of a MeshVariable is for any single element. E.g., A CellVariable containing scalars at each
cell, and defined on a 9 element Grid1D, has rank 0. If it is defined on a 3x3 Grid2D, it is still rank 0.
fipy.tools.numerix.reshape(arr, shape)
Change the shape of arr to shape, as long as the product of all the lenghts of all the axes is constant (the total
number of elements does not change).
fipy.tools.numerix.sum(arr, axis=0)
The sum of all the elements of arr along the specified axis.
fipy.tools.numerix.take(a, indices, axis=0, fill_value=None)
Selects the elements of a corresponding to indices.
fipy.tools.numerix.all(a, axis=None, out=None)
Test whether all array elements along a given axis evaluate to True.
Parameters a : array_like
Input array or object that can be converted to an array.
axis : int, optional
Axis along which an logical AND is performed. The default (axis = None) is to perform
a logical AND over a flattened input array. axis may be negative, in which case it counts
from the last to the first axis.
out : ndarray, optional
Alternative output array in which to place the result. It must have the same shape as the
expected output and the type is preserved.
fipy.tools.numerix.put(arr, ids, values)
The opposite of take. The values of arr at the locations specified by ids are set to the corresponding value of
values.
The following is to test improvments to puts with masked arrays. Places in the code were assuming incorrect
put behavior.
>>> maskValue = 999999
fipy.tools.numerix.rank(a)
Get the rank of sequence a (the number of dimensions, not a matrix rank) The rank of a scalar is zero.
Note: The rank of a MeshVariable is for any single element. E.g., A CellVariable containing scalars at each
cell, and defined on a 9 element Grid1D, has rank 0. If it is defined on a 3x3 Grid2D, it is still rank 0.
fipy.tools.numerix.reshape(arr, shape)
Change the shape of arr to shape, as long as the product of all the lenghts of all the axes is constant (the total
number of elements does not change).
fipy.tools.numerix.sum(arr, axis=0)
The sum of all the elements of arr along the specified axis.
fipy.tools.numerix.take(a, indices, axis=0, fill_value=None)
Selects the elements of a corresponding to indices.
fipy.tools.numerix.all(a, axis=None, out=None)
Test whether all array elements along a given axis evaluate to True.
Parameters a : array_like
Input array or object that can be converted to an array.
axis : int, optional
Axis along which an logical AND is performed. The default (axis = None) is to perform
a logical AND over a flattened input array. axis may be negative, in which case it counts
from the last to the first axis.
This means essentially that both elements are small compared to atol or their difference divided by second‘s
value is small compared to rtol.
fipy.tools.numerix.allequal(first, second)
Returns true if every element of first is equal to the corresponding element of second.
fipy.tools.numerix.dot(a1, a2, axis=0)
return array of vector dot-products of v1 and v2 for arrays a1 and a2 of vectors v1 and v2
We can’t use numpy.dot() on an array of vectors
Test that Variables are returned as Variables.
>>> from fipy.meshes import Grid2D
>>> mesh = Grid2D(nx=2, ny=1)
>>> from fipy.variables.cellVariable import CellVariable
>>> v1 = CellVariable(mesh=mesh, value=((0,1),(2,3)), rank=1)
>>> v2 = CellVariable(mesh=mesh, value=((0,1),(2,3)), rank=1)
>>> dot(v1, v2)._variableClass
<class 'fipy.variables.cellVariable.CellVariable'>
>>> dot(v2, v1)._variableClass
<class 'fipy.variables.cellVariable.CellVariable'>
>>> print rank(dot(v2, v1))
0
>>> print dot(v1, v2)
[ 4 10]
>>> dot(v1, v1)._variableClass
<class 'fipy.variables.cellVariable.CellVariable'>
>>> print dot(v1, v1)
[ 4 10]
>>> v3 = array(((0,1),(2,3)))
>>> print type(dot(v3, v3)) is type(array(1))
1
>>> print dot(v3, v3)
[ 4 10]
fipy.tools.numerix.getShape(arr)
Return the shape of arr
>>> getShape(1)
()
>>> getShape(1.)
()
>>> from fipy.variables.variable import Variable
>>> getShape(Variable(1))
()
>>> getShape(Variable(1.))
()
>>> getShape(Variable(1., unit="m"))
()
fipy.tools.numerix.getUnit(arr)
fipy.tools.numerix.indices(dimensions, typecode=None)
indices(dimensions,typecode=None) returns an array representing a grid of indices with row-only, and column-
only variation.
>>> NUMERIX.allclose(NUMERIX.array(indices((4, 6))), NUMERIX.indices((4,6)))
1
>>> NUMERIX.allclose(NUMERIX.array(indices((4, 6, 2))), NUMERIX.indices((4, 6, 2)))
1
>>> NUMERIX.allclose(NUMERIX.array(indices((1,))), NUMERIX.indices((1,)))
1
>>> NUMERIX.allclose(NUMERIX.array(indices((5,))), NUMERIX.indices((5,)))
1
This means essentially that both elements are small compared to atol or their difference divided by second‘s
value is small compared to rtol.
fipy.tools.numerix.isFloat(arr)
fipy.tools.numerix.isInt(arr)
fipy.tools.numerix.L1norm(arr)
Parameters
• arr: The array to evaluate.
∑︀𝑛
Returns ‖arr‖1 = 𝑗=1 |arr𝑗 | is the 𝐿1 -norm of arr.
fipy.tools.numerix.L2norm(arr)
Parameters
• arr: The array to evaluate.
√︁∑︀
𝑛
𝑗=1 |arr𝑗 | is the 𝐿 -norm of arr.
2 2
Returns ‖arr‖2 =
fipy.tools.numerix.LINFnorm(arr)
Parameters
• arr: The array to evaluate.
∞ ∞
‖arr‖∞ =[ 𝑛
∑︀
𝑗=1 |arr𝑗 | ] =
Returns max𝑗|arr𝑗 | is the 𝐿∞ -norm of arr.
fipy.tools.numerix.nearest(data, points, max_mem=100000000.0)
find the indices of data that are closest to points
>>> from fipy import *
>>> m0 = Grid2D(dx=(.1, 1., 10.), dy=(.1, 1., 10.))
>>> m1 = Grid2D(nx=2, ny=2, dx=5., dy=5.)
>>> print nearest(m0.cellCenters.globalValue, m1.cellCenters.globalValue)
[4 5 7 8]
fipy.tools.numerix.rank(a)
Get the rank of sequence a (the number of dimensions, not a matrix rank) The rank of a scalar is zero.
Note: The rank of a MeshVariable is for any single element. E.g., A CellVariable containing scalars at each
cell, and defined on a 9 element Grid1D, has rank 0. If it is defined on a 3x3 Grid2D, it is still rank 0.
fipy.tools.numerix.reshape(arr, shape)
Change the shape of arr to shape, as long as the product of all the lenghts of all the axes is constant (the total
number of elements does not change).
fipy.tools.numerix.sqrtDot(a1, a2)
Return array of square roots of vector dot-products for arrays a1 and a2 of vectors v1 and v2
Usually used with v1==v2 to return magnitude of v1.
fipy.tools.numerix.sum(arr, axis=0)
The sum of all the elements of arr along the specified axis.
fipy.tools.numerix.take(a, indices, axis=0, fill_value=None)
Selects the elements of a corresponding to indices.
fipy.tools.numerix.tostring(arr, max_line_width=75, precision=8, suppress_small=False, sepa-
rator=’ ‘, array_output=0)
Returns a textual representation of a number or field of numbers. Each dimension is indicated by a pair of match-
ing square brackets ([]), within which each subset of the field is output. The orientation of the dimensions is as
follows: the last (rightmost) dimension is always horizontal, so that the frequent rank-1 fields use a minimum
of screen real-estate. The next-to-last dimesnion is displayed vertically if present and any earlier dimension is
displayed with additional bracket divisions.
Parameters
• max_line_width: the maximum number of characters used in a single line. Default is
sys.output_line_width or 77.
• precision: the number of digits after the decimal point. Default is sys.float_output_precision
or 8.
• suppress_small: whether small values should be suppressed (and output as 0). Default is
sys.float_output_suppress_small or false.
• separator: what character string to place between two numbers.
• array_output: Format output for an eval. Only used if arr is a Numeric array.
class fipy.tools.vitals.Vitals
Bases: xml.dom.minidom.Document
Returns XML formatted information about current FiPy environment
appendChild(child)
appendInfo(name, svnpath=None, **kwargs)
append some additional information, possibly about a project under a separate svn repository
dictToXML(d, name)
save(fname)
svn(*args)
svncmd(cmd, *args)
tupleToXML(t, name, keys=None)
•PhysicalField(*string*), where *string* contains both the value and the unit. This form is provided to
make interactive use more convenient
>>> print PhysicalField(value = "10. m")
10.0 m
Physical arrays are also possible (and are the reason this code was adapted from Konrad Hinsen‘s original
PhysicalQuantity). The value can be a Numeric array:
>>> a = numerix.array(((3.,4.),(5.,6.)))
>>> print PhysicalField(value = a, unit = "m")
[[ 3. 4.]
[ 5. 6.]] m
or a tuple:
>>> print PhysicalField(value = ((3.,4.),(5.,6.)), unit = "m")
[[ 3. 4.]
[ 5. 6.]] m
Every element in an array has the same unit, which is stored only once for the whole array.
add(other)
Add two physical quantities, so long as their units are compatible. The unit of the result is the unit of the
first operand.
>>> print PhysicalField(10., 'km') + PhysicalField(10., 'm')
10.01 km
>>> print PhysicalField(10., 'km') + PhysicalField(10., 'J')
Traceback (most recent call last):
...
TypeError: Incompatible units
This means essentially that both elements are small compared to atol or their difference divided by other‘s
value is small compared to rtol.
allequal(other)
This function tests whether or not self and other are exactly equal.
arccos()
Return the inverse cosine of the PhysicalField in radians
>>> print PhysicalField(0).arccos().allclose("1.57079632679 rad")
1
arccosh()
Return the inverse hyperbolic cosine of the PhysicalField
>>> print numerix.allclose(PhysicalField(2).arccosh(),
... 1.31695789692)
1
arcsin()
Return the inverse sine of the PhysicalField in radians
>>> print PhysicalField(1).arcsin().allclose("1.57079632679 rad")
1
arctan()
Return the arctangent of the PhysicalField in radians
>>> print numerix.round_(PhysicalField(1).arctan(), 6)
0.785398
arctan2(other)
Return the arctangent of self divided by other in radians
>>> print numerix.round_(PhysicalField(2.).arctan2(PhysicalField(5.)), 6)
0.380506
arctanh()
Return the inverse hyperbolic tangent of the PhysicalField
>>> print PhysicalField(0.5).arctanh()
0.549306144334
ceil()
Return the smallest integer greater than or equal to the PhysicalField.
>>> print PhysicalField(2.2,"m").ceil()
3.0 m
conjugate()
Return the complex conjugate of the PhysicalField.
>>> print PhysicalField(2.2 - 3j,"ohm").conjugate() == PhysicalField(2.2 + 3j,"ohm")
True
convertToUnit(unit)
Changes the unit to unit and adjusts the value such that the combination is equivalent. The new unit is by
a string containing its name. The new unit must be compatible with the previous unit of the object.
>>> e = PhysicalField('2.7 Hartree*Nav')
>>> e.convertToUnit('kcal/mol')
>>> print e
1694.27557621 kcal/mol
copy()
Make a duplicate.
>>> a = PhysicalField(1, unit = 'inch')
>>> b = a.copy()
cos()
Return the cosine of the PhysicalField
>>> print numerix.round_(PhysicalField(2*numerix.pi/6,"rad").cos(), 6)
0.5
>>> print numerix.round_(PhysicalField(60.,"deg").cos(), 6)
0.5
cosh()
Return the hyperbolic cosine of the PhysicalField
>>> PhysicalField(0.).cosh()
1.0
divide(other)
Divide two physical quantities. The unit of the result is the unit of the first operand divided by the unit of
the second.
>>> print PhysicalField(10., 'm') / PhysicalField(2., 's')
5.0 m/s
As a special case, if the result is dimensionless, the value is returned without units, rather than with a
dimensionless unit of 1. This facilitates passing physical quantities to packages such as Numeric that
cannot use units, while ensuring the quantities have the desired units
>>> print (PhysicalField(1., 'inch')
... / PhysicalField(1., 'mm'))
25.4
dot(other)
Return the dot product of self with other. The resulting unit is the product of the units of self and other.
>>> v = PhysicalField(((5.,6.),(7.,8.)), "m")
>>> print PhysicalField(((1.,2.),(3.,4.)), "m").dot(v)
[ 26. 44.] m**2
floor()
Return the largest integer less than or equal to the PhysicalField.
getNumericValue(*args, **kwds)
Deprecated since version 3.0: use the numericValue property instead
getShape(*args, **kwds)
Deprecated since version 3.0: use the shape property instead
getUnit(*args, **kwds)
Deprecated since version 3.0: use the unit property instead
getsctype(default=None)
Returns the Numpy sctype of the underlying array.
>>> PhysicalField(1, 'm').getsctype() == numerix.NUMERIX.obj2sctype(numerix.array(1))
True
>>> PhysicalField(1., 'm').getsctype() == numerix.NUMERIX.obj2sctype(numerix.array(1.))
True
>>> PhysicalField((1,1.), 'm').getsctype() == numerix.NUMERIX.obj2sctype(numerix.array((1.,
True
inBaseUnits()
Return the quantity with all units reduced to their base SI elements.
>>> e = PhysicalField('2.7 Hartree*Nav')
>>> print e.inBaseUnits().allclose("7088849.01085 kg*m**2/s**2/mol")
1
inDimensionless()
Returns the numerical value of a dimensionless quantity.
inRadians()
Converts an angular quantity to radians and returns the numerical value.
inSIUnits()
Return the quantity with all units reduced to SI-compatible elements.
>>> e = PhysicalField('2.7 Hartree*Nav')
>>> print e.inSIUnits().allclose("7088849.01085 kg*m**2/s**2/mol")
1
inUnitsOf(*units)
Returns one or more PhysicalField objects that express the same physical quantity in different units. The
units are specified by strings containing their names. The units must be compatible with the unit of the
object. If one unit is specified, the return value is a single PhysicalField.
>>> freeze = PhysicalField('0 degC')
>>> print freeze.inUnitsOf('degF').allclose("32.0 degF")
1
If several units are specified, the return value is a tuple of PhysicalField instances with with one element per
unit such that the sum of all quantities in the tuple equals the the original quantity and all the values except
for the last one are integers. This is used to convert to irregular unit systems like hour/minute/second. The
original object will not be changed.
>>> t = PhysicalField(314159., 's')
>>> print numerix.allclose([e.allclose(v) for (e, v) in zip(t.inUnitsOf('d','h','min','s'),
... ['3.0 d', '15.0 h', '15.0 min',
... True)
1
isCompatible(unit)
itemset(value)
Assign the value of a scalar array, performing appropriate conversions.
>>> a = PhysicalField(4.,"m")
>>> a.itemset(PhysicalField("6 ft"))
>>> print a.allclose("1.8288 m")
1
>>> a = PhysicalField(((3.,4.),(5.,6.)),"m")
>>> try:
... a.itemset(PhysicalField("6 ft"))
... except IndexError:
... # NumPy 1.7 has changed the exception type
... raise ValueError("can only place a scalar for an array of size 1")
Traceback (most recent call last):
...
ValueError: can only place a scalar for an array of size 1
>>> a.itemset(PhysicalField("2 min"))
Traceback (most recent call last):
...
TypeError: Incompatible units
itemsize
log()
Return the natural logarithm of the PhysicalField
>>> print numerix.round_(PhysicalField(10).log(), 6)
2.302585
log10()
Return the base-10 logarithm of the PhysicalField
>>> print numerix.round_(PhysicalField(10.).log10(), 6)
1.0
multiply(other)
Multiply two physical quantities. The unit of the result is the product of the units of the operands.
>>> print PhysicalField(10., 'N') * PhysicalField(10., 'm')
100.0 m*N
As a special case, if the result is dimensionless, the value is returned without units, rather than with a
dimensionless unit of 1. This facilitates passing physical quantities to packages such as Numeric that
cannot use units, while ensuring the quantities have the desired units.
>>> print (PhysicalField(10., 's') * PhysicalField(2., 'Hz'))
20.0
numericValue
Return the PhysicalField without units, after conversion to base SI units.
>>> print numerix.round_(PhysicalField("1 inch").numericValue, 6)
0.0254
put(indices, values)
put is the opposite of take. The values of self at the locations specified in indices are set to the correspond-
ing value of values.
The indices can be any integer sequence object with values suitable for indexing into the flat form of self.
The values must be any sequence of values that can be converted to the typecode of self.
>>> f = PhysicalField((1.,2.,3.),"m")
>>> f.put((2,0), PhysicalField((2.,3.),"inch"))
>>> print f
[ 0.0762 2. 0.0508] m
...
TypeError: Incompatible units
ravel()
reshape(shape)
Changes the shape of self to that specified in shape
>>> print PhysicalField((1.,2.,3.,4.),"m").reshape((2,2))
[[ 1. 2.]
[ 3. 4.]] m
The new shape must have the same size as the existing one.
>>> print PhysicalField((1.,2.,3.,4.),"m").reshape((2,3))
Traceback (most recent call last):
...
ValueError: total size of new array must be unchanged
setUnit(*args, **kwds)
Deprecated since version 3.0: use the unit property instead
shape
Tuple of array dimensions.
sign()
Return the sign of the quantity. The unit is unchanged.
>>> from fipy.tools.numerix import sign
>>> print sign(PhysicalField(((3.,-2.),(-1.,4.)), 'm'))
[[ 1. -1.]
[-1. 1.]]
sin()
Return the sine of the PhysicalField
>>> print PhysicalField(numerix.pi/6,"rad").sin()
0.5
>>> print PhysicalField(30.,"deg").sin()
0.5
sinh()
Return the hyperbolic sine of the PhysicalField
>>> PhysicalField(0.).sinh()
0.0
sqrt()
Return the square root of the PhysicalField
>>> print PhysicalField("100. m**2").sqrt()
10.0 m
subtract(other)
Subtract two physical quantities, so long as their units are compatible. The unit of the result is the unit of
the first operand.
>>> print PhysicalField(10., 'km') - PhysicalField(10., 'm')
9.99 km
>>> print PhysicalField(10., 'km') - PhysicalField(10., 'J')
Traceback (most recent call last):
...
TypeError: Incompatible units
sum(index=0)
Returns the sum of all of the elements in self along the specified axis (first axis by default).
>>> print PhysicalField(((1.,2.),(3.,4.)), "m").sum()
[ 4. 6.] m
>>> print PhysicalField(((1.,2.),(3.,4.)), "m").sum(1)
[ 3. 7.] m
take(indices, axis=0)
Return the elements of self specified by the elements of indices. The resulting PhysicalField array has the
same units as the original.
>>> print PhysicalField((1.,2.,3.),"m").take((2,0))
[ 3. 1.] m
The optional third argument specifies the axis along which the selection occurs, and the default value (as
in the example above) is 0, the first axis.
>>> print PhysicalField(((1.,2.,3.),(4.,5.,6.)),"m").take((2,0), axis = 1)
[[ 3. 1.]
[ 6. 4.]] m
tan()
Return the tangent of the PhysicalField
>>> numerix.round_(PhysicalField(numerix.pi/4,"rad").tan(), 6)
1.0
>>> numerix.round_(PhysicalField(45,"deg").tan(), 6)
1.0
tanh()
Return the hyperbolic tangent of the PhysicalField
>>> print numerix.allclose(PhysicalField(1.).tanh(), 0.761594155956)
True
unit
Return the unit object of self.
>>> PhysicalField("1 m").unit
<PhysicalUnit m>
class fipy.tools.Vitals
Bases: xml.dom.minidom.Document
Returns XML formatted information about current FiPy environment
appendChild(child)
appendInfo(name, svnpath=None, **kwargs)
append some additional information, possibly about a project under a separate svn repository
dictToXML(d, name)
save(fname)
svn(*args)
svncmd(cmd, *args)
tupleToXML(t, name, keys=None)
27.1 Submodules
𝛽 𝛼 𝑒−𝛽𝑥
𝑥𝛼−1
Γ(𝛼)
∫︀ ∞
with a shape parameter 𝛼, a rate parameter 𝛽, and Γ(𝑧) = 0 𝑡𝑧−1 𝑒−𝑡 𝑑𝑡.
Seed the random module for the sake of deterministic test results.
>>> from fipy import numerix
>>> numerix.random.seed(1)
327
FiPy Manual, Release 3.1.3
2 a = b = 0.5
Parameters
• mesh: The mesh on which to define the noise.
• alpha: The parameter 𝛼.
• beta: The parameter 𝛽.
random()
arithmeticFaceValue
Returns a FaceVariable whose value corresponds to the arithmetic interpolation of the adjacent cells:
𝑑𝑓 2
𝜑𝑓 = (𝜑1 − 𝜑2 ) + 𝜑2
𝑑12
cellVolumeAverage
Return the cell-volume-weighted average of the CellVariable:
∑︀
cells 𝜑cell 𝑉cell
< 𝜑 >vol = ∑︀
cells 𝑉cell
constrain(value, where=None)
Constrains the CellVariable to value at a location specified by where.
>>> from fipy import *
>>> m = Grid1D(nx=3)
>>> v = CellVariable(mesh=m, value=m.cellCenters[0])
>>> v.constrain(0., where=m.facesLeft)
>>> v.faceGrad.constrain([1.], where=m.facesRight)
>>> print v.faceGrad
[[ 1. 1. 1. 1.]]
>>> print v.faceValue
[ 0. 1. 2. 2.5]
[ 1. 0. 0. 0.]
>>> mask[:] = mask | m.facesRight
>>> print v.faceValue
[ 1. 0. 0. 1.]
copy()
faceGrad
Return ∇𝜑 as a rank-1 FaceVariable using differencing for the normal direction(second-order gradient).
faceGradAverage
Return ∇𝜑 as a rank-1 FaceVariable using averaging for the normal direction(second-order gradient)
faceValue
Returns a FaceVariable whose value corresponds to the arithmetic interpolation of the adjacent cells:
𝑑𝑓 2
𝜑𝑓 = (𝜑1 − 𝜑2 ) + 𝜑2
𝑑12
gaussGrad ∑︀
Return 𝑉1𝑃 𝑓 ⃗𝑛𝜑𝑓 𝐴𝑓 as a rank-1 CellVariable (first-order gradient).
getArithmeticFaceValue(*args, **kwds)
Deprecated since version 3.0: use the arithmeticFaceValue property instead
getCellVolumeAverage(*args, **kwds)
Deprecated since version 3.0: use the cellVolumeAverage property instead
getFaceGrad(*args, **kwds)
Deprecated since version 3.0: use the faceGrad property instead
getFaceGradAverage(*args, **kwds)
Deprecated since version 3.0: use the faceGradAverage property instead
getFaceValue(*args, **kwds)
Deprecated since version 3.0: use the arithmeticFaceValue property instead
getGaussGrad(*args, **kwds)
Deprecated since version 3.0: use the gaussGrad property instead
getGrad(*args, **kwds)
Deprecated since version 3.0: use the grad property instead
getHarmonicFaceValue(*args, **kwds)
Deprecated since version 3.0: use the harmonicFaceValue property instead
getLeastSquaresGrad(*args, **kwds)
Deprecated since version 3.0: use the leastSquaresGrad property instead
getMinmodFaceValue(*args, **kwds)
Deprecated since version 3.0: use the minmodFaceValue property instead
getOld(*args, **kwds)
Deprecated since version 3.0: use the old property instead
globalValue
Concatenate and return values from all processors
When running on a single processor, the result is identical to value.
grad
Return ∇𝜑 as a rank-1 CellVariable (first-order gradient).
harmonicFaceValue
Returns a FaceVariable whose value corresponds to the harmonic interpolation of the adjacent cells:
𝜑1 𝜑2
𝜑𝑓 = 𝑑
(𝜑2 − 𝜑1 ) 𝑑𝑓122 + 𝜑1
leastSquaresGrad
Return ∇𝜑, which is determined by solving for ∇𝜑 in the following matrix equation,
∑︁ ∑︁
∇𝜑 · 𝑑2𝐴𝑃 ⃗𝑛𝐴𝑃 ⊗ ⃗𝑛𝐴𝑃 = 𝑑2𝐴𝑃 (⃗𝑛 · ∇𝜑)𝐴𝑃
𝑓 𝑓
The matrix equation is derived by minimizing the following least squares sum,
√︃∑︁
2
𝐹 (𝜑𝑥 , 𝜑𝑦 ) = (𝑑𝐴𝑃 ⃗𝑛𝐴𝑃 · ∇𝜑 − 𝑑𝐴𝑃 (⃗𝑛𝐴𝑃 · ∇𝜑)𝐴𝑃 )
𝑓
Tests
>>> from fipy import Grid2D
>>> m = Grid2D(nx=2, ny=2, dx=0.1, dy=2.0)
>>> print numerix.allclose(CellVariable(mesh=m, value=(0,1,3,6)).leastSquaresGrad.globalValu
... [[8.0, 8.0, 24.0, 24.0],
... [1.2, 2.0, 1.2, 2.0]])
True
minmodFaceValue
Returns a FaceVariable with a value that is the minimum of the absolute values of the adjacent cells. If the
values are of opposite sign then the result is zero:
⎧
⎨𝜑1 when |𝜑1 | ≤ |𝜑2 |,
⎪
𝜑𝑓 = 𝜑2 when |𝜑2 | < |𝜑1 |,
when 𝜑1𝜑2 < 0
⎪
0
⎩
old
Return the values of the CellVariable from the previous solution sweep.
Combinations of CellVariable’s should also return old values.
>>> from fipy.meshes import Grid1D
>>> mesh = Grid1D(nx = 2)
>>> from fipy.variables.cellVariable import CellVariable
>>> var1 = CellVariable(mesh = mesh, value = (2, 3), hasOld = 1)
>>> var2 = CellVariable(mesh = mesh, value = (3, 4))
>>> v = var1 * var2
>>> print v
[ 6 12]
>>> var1.value = ((3,2))
>>> print v
[9 8]
>>> print v.old
[ 6 12]
The following small test is to correct for a bug when the operator does not just use variables.
>>> v1 = var1 * 3
>>> print v1
[9 6]
>>> print v1.old
[6 9]
release(constraint)
Remove constraint from self
>>> from fipy import *
>>> m = Grid1D(nx=3)
>>> v = CellVariable(mesh=m, value=m.cellCenters[0])
>>> c = Constraint(0., where=m.facesLeft)
>>> v.constrain(c)
>>> print v.faceValue
[ 0. 1. 2. 2.5]
>>> v.release(constraint=c)
>>> print v.faceValue
[ 0.5 1. 2. 2.5]
|∇𝜑| = 1
using the fast marching method with an initial condition defined by the zero level set. The solution can either be
first or second order.
>>> var.calcDistanceFunction()
>>> vbl = -dx * dy / numerix.sqrt(dx**2 + dy**2) / 2.
>>> vbr = dx / 2
>>> vml = dy / 2.
>>> crossProd = dx * dy
>>> dsq = dx**2 + dy**2
>>> top = vbr * dx**2 + vml * dy**2
>>> sqrt = crossProd**2 *(dsq - (vbr - vml)**2)
>>> sqrt = numerix.sqrt(max(sqrt, 0))
>>> vmr = (top + sqrt) / dsq
>>> answer = (vbl, vbr, vml, vmr, vbl, vbr)
>>> print var.allclose(answer)
1
The extendVariable method solves the following equation for a given extensionVariable.
∇𝑢 · ∇𝜑 = 0
using the fast marching method with an initial condition defined at the zero level set.
>>> from fipy.variables.cellVariable import CellVariable
>>> mesh = Grid2D(dx = 1., dy = 1., nx = 2, ny = 2, communicator=serialComm)
>>> var = DistanceVariable(mesh = mesh, value = (-1., 1., 1., 1.))
>>> var.calcDistanceFunction()
>>> extensionVar = CellVariable(mesh = mesh, value = (-1, .5, 2, -1))
>>> tmp = 1 / numerix.sqrt(2)
>>> print var.allclose((-tmp / 2, 0.5, 0.5, 0.5 + tmp))
1
>>> var.extendVariable(extensionVar, order=1)
>>> print extensionVar.allclose((1.25, .5, 2, 1.25))
1
>>> mesh = Grid2D(dx = 1., dy = 1., nx = 3, ny = 3, communicator=serialComm)
>>> var = DistanceVariable(mesh = mesh, value = (-1., 1., 1.,
... 1., 1., 1.,
... 1., 1., 1.))
>>> var.calcDistanceFunction(order=1)
>>> extensionVar = CellVariable(mesh = mesh, value = (-1., .5, -1.,
... 2., -1., -1.,
... -1., -1., -1.))
Test case for a bug that occurs when initializing the distance variable at the interface. Currently it is assumed
that adjacent cells that are opposite sign neighbors have perpendicular normal vectors. In fact the two closest
cells could have opposite normals.
>>> mesh = Grid1D(dx = 1., nx = 3)
>>> var = DistanceVariable(mesh = mesh, value = (-1., 1., -1.))
>>> var.calcDistanceFunction()
>>> print var.allclose((-0.5, 0.5, -0.5))
1
The 3rd and 7th element are different for LSMLIB. This is because the 15th element is not “known” when the
“trial” value for the 7th element is calculated. Scikit-fmm calculates the values in a slightly different order so
gets a seemingly better answer, but this is just chance.
>>> print numerix.allclose(var, answer, rtol=1e-9)
True
A 2D test case:
>>> from fipy.meshes import Grid2D
>>> from fipy.variables.cellVariable import CellVariable
>>> mesh = Grid2D(dx = 1., dy = 1., nx = 3, ny = 3)
>>> distanceVariable = DistanceVariable(mesh = mesh,
... value = (1.5, 0.5, 1.5,
... 0.5,-0.5, 0.5,
... 1.5, 0.5, 1.5))
>>> answer = CellVariable(mesh=mesh,
... value=(0, 1, 0, 1, 0, 1, 0, 1, 0))
extendVariable(extensionVariable, order=2)
Calculates the extension of extensionVariable from the zero level set.
Parameters
• extensionVariable: The variable to extend from the zero level set.
getCellInterfaceAreas(*args, **kwds)
Deprecated since version 3.0: use the cellInterfaceAreas property instead
getLSMshape()
class fipy.variables.exponentialNoiseVariable.ExponentialNoiseVariable(mesh,
mean=0.0,
name=’‘,
ha-
sOld=0)
Bases: fipy.variables.noiseVariable.NoiseVariable
Represents an exponential distribution of random numbers with the probability distribution
𝑥
𝜇−1 𝑒− 𝜇
0.6
0.5
0.4
m = 1.5
0.3
0.2
0.1
0.0
2 4 6 8
Parameters
• mesh: The mesh on which to define the noise.
• mean: The mean of the distribution 𝜇.
random()
getDivergence(*args, **kwds)
Deprecated since version 3.0: use the divergence property instead
globalValue
setValue(value, unit=None, where=None)
𝛽 𝛼 𝑒−𝛽𝑥
𝑥𝛼−1
Γ(𝛼)
∫︀ ∞
with a shape parameter 𝛼, a rate parameter 𝛽, and Γ(𝑧) = 0 𝑡𝑧−1 𝑒−𝑡 𝑑𝑡.
Seed the random module for the sake of deterministic test results.
>>> from fipy import numerix
>>> numerix.random.seed(1)
0.30
0.25
0.20
a=4
0.15
b=1
0.10
0.05
0.00
0 5 10 15 20
Parameters
• mesh: The mesh on which to define the noise.
• shape: The shape parameter, 𝛼.
• rate: The rate or inverse scale parameter, 𝛽.
random()
1 (𝑥 − 𝜇)2
√ exp −
𝜎 2𝜋 2𝜎 2
For example, the variance of thermal noise that is uncorrelated in space and time is often expressed as
Note: If the time step will change as the simulation progresses, either through use of an adaptive iterator or by
making manual changes at different stages, remember to declare timeStep as a Variable and to change its value
with its setValue() method.
>>> mean = 0.
>>> variance = 4.
Seed the random module for the sake of deterministic test results.
>>> from fipy import numerix
>>> numerix.random.seed(3)
Note that the noise exhibits larger amplitude in the small cells than in the large ones
0.20
0.15
s=2
0.10
0.05
0.00
-10 -5 0 5 10
Parameters
• mesh: The mesh on which to define the noise.
arithmeticFaceValue
Returns a FaceVariable whose value corresponds to the arithmetic interpolation of the adjacent cells:
𝑑𝑓 2
𝜑𝑓 = (𝜑1 − 𝜑2 ) + 𝜑2
𝑑12
Adjusted for a ModularVariable
faceGrad
Return ∇𝜑 as a rank-1 FaceVariable (second-order gradient). Adjusted for a ModularVariable
faceGradNoMod
Return ∇𝜑 as a rank-1 FaceVariable (second-order gradient). Not adjusted for a ModularVariable
getFaceGradNoMod(*args, **kwds)
Deprecated since version 3.0: use the faceGradNoMod property instead
grad
Return ∇𝜑 as a rank-1 CellVariable (first-order gradient). Adjusted for a ModularVariable
updateOld()
Set the values of the previous solution sweep to the current values. Test case due to bug.
>>> from fipy.meshes import Grid1D
>>> mesh = Grid1D(nx = 1)
>>> var = ModularVariable(mesh=mesh, value=1., hasOld=1)
>>> var.updateOld()
>>> var[:] = 2
>>> answer = CellVariable(mesh=mesh, value=1.)
>>> print var.old.allclose(answer)
True
A generic base class for sources of noise distributed over the cells of a mesh.
In the event that the noise should be conserved, use:
<Specific>NoiseVariable(...).faceGrad.divergence
The seed() and get_seed() functions of the fipy.tools.numerix.random module can be set and query the random
number generated used by all NoiseVariable objects.
copy()
Copy the value of the NoiseVariable to a static CellVariable.
parallelRandom()
random()
scramble()
Generate a new random distribution.
class fipy.variables.scharfetterGummelFaceVariable.ScharfetterGummelFaceVariable(var,
bound-
aryCon-
di-
tions=())
Bases: fipy.variables.cellToFaceVariable._CellToFaceVariable
class fipy.variables.surfactantConvectionVariable.SurfactantConvectionVariable(distanceVar)
Bases: fipy.variables.faceVariable.FaceVariable
Convection coefficient for the ConservativeSurfactantEquation. The coeff only has a value for a negative dis-
tanceVar.
Simple one dimensional test:
>>> from fipy.variables.cellVariable import CellVariable
>>> from fipy.meshes import Grid2D
>>> mesh = Grid2D(nx = 3, ny = 1, dx = 1., dy = 1.)
>>> from fipy.variables.distanceVariable import DistanceVariable
>>> distanceVar = DistanceVariable(mesh, value = (-.5, .5, 1.5))
>>> ## answer = numerix.zeros((2, mesh.numberOfFaces),'d')
>>> answer = FaceVariable(mesh=mesh, rank=1, value=0.).globalValue
>>> answer[0,7] = -1
>>> print numerix.allclose(SurfactantConvectionVariable(distanceVar).globalValue, answer)
True
Larger grid:
>>> mesh = Grid2D(nx = 3, ny = 3, dx = 1., dy = 1.)
>>> distanceVar = DistanceVariable(mesh, value = (1.5, .5 , 1.5,
... .5 , -.5, .5 ,
... 1.5, .5 , 1.5))
>>> answer = FaceVariable(mesh=mesh, rank=1, value=0.).globalValue
>>> answer[1,4] = .25
>>> answer[1,7] = -.25
>>> answer[0,17] = .25
>>> answer[0,18] = -.25
>>> print numerix.allclose(SurfactantConvectionVariable(distanceVar).globalValue, answer)
True
A 2D test case:
>>> from fipy.meshes import Grid2D
>>> mesh = Grid2D(dx = 1., dy = 1., nx = 3, ny = 3)
>>> distanceVariable = DistanceVariable(mesh = mesh,
... value = (1.5, 0.5, 1.5,
... 0.5,-0.5, 0.5,
... 1.5, 0.5, 1.5))
Parameters
• value: The initial value.
• distanceVar: A DistanceVariable object.
• name: The name of the variable.
copy()
getInterfaceVar(*args, **kwds)
Deprecated since version 3.0: use the interfaceVar property instead
interfaceVar
Returns the SurfactantVariable rendered as an _InterfaceSurfactantVariable which evaluates the surfactant
concentration as an area concentration the interface rather than a volumetric concentration.
1.2
1.0
0.8
0.6
0.4
0.2
0.0
Parameters
• mesh: The mesh on which to define the noise.
• minimum: The minimum (not-inclusive) value of the distribution.
• maximum: The maximum (not-inclusive) value of the distribution.
random()
Changes to the value of a Variable will automatically trigger changes in any dependent Variable objects
>>> a.setValue(5)
>>> b
(Variable(value=array(5)) * 4)
>>> print b()
20
Create a Variable.
>>> Variable(value=3)
Variable(value=array(3))
>>> Variable(value=3, unit="m")
Variable(value=PhysicalField(3,'m'))
>>> Variable(value=3, unit="m", array=numerix.zeros((3,2), 'l'))
Variable(value=PhysicalField(array([[3, 3],
[3, 3],
[3, 3]]),'m'))
Parameters
• value: the initial value
• unit: the physical units of the Variable
• array: the storage array for the Variable
• name: the user-readable name of the Variable
• cached: whether to cache or always recalculate the value
all(axis=None)
The following test is to check that the system does not run out of memory.
>>> from fipy.tools import numerix
>>> var = Variable(numerix.ones(10000))
>>> print var.allclose(numerix.zeros(10000, 'l'))
False
allequal(other)
any(axis=None)
arccos(*args, **kwds)
Deprecated since version 3.0: use numerix.arccos() instead
arccosh(*args, **kwds)
Deprecated since version 3.0: use numerix.arccosh() instead
arcsin(*args, **kwds)
Deprecated since version 3.0: use numerix.arcsin() instead
arcsinh(*args, **kwds)
Deprecated since version 3.0: use numerix.arcsinh() instead
arctan(*args, **kwds)
Deprecated since version 3.0: use numerix.arctan() instead
arctan2(*args, **kwds)
Deprecated since version 3.0: use numerix.arctan2() instead
arctanh(*args, **kwds)
Deprecated since version 3.0: use numerix.arctanh() instead
cacheMe(recursive=False)
ceil(*args, **kwds)
Deprecated since version 3.0: use numerix.ceil() instead
conjugate(*args, **kwds)
Deprecated since version 3.0: use numerix.conjugate() instead
constrain(value, where=None)
Constrain the Variable to have a value at an index or mask location specified by where.
>>> v = Variable((0,1,2,3))
>>> v.constrain(2, numerix.array((True, False, False, False)))
>>> print v
[2 1 2 3]
>>> v[:] = 10
>>> print v
[ 2 10 10 10]
>>> v.constrain(5, numerix.array((False, False, True, False)))
>>> print v
[ 2 10 5 10]
>>> v[:] = 6
>>> print v
[2 6 5 6]
>>> v.constrain(8)
>>> print v
[8 8 8 8]
>>> v[:] = 10
>>> print v
[8 8 8 8]
>>> del v.constraints[2]
>>> print v
[ 2 10 5 10]
Parameters
• value: the value of the constraint
• where: the constraint mask or index specifying the location of the constraint
constraints
copy()
Make an duplicate of the Variable
>>> a = Variable(value=3)
>>> b = a.copy()
>>> b
Variable(value=array(3))
cos(*args, **kwds)
Deprecated since version 3.0: use numerix.cos() instead
cosh(*args, **kwds)
Deprecated since version 3.0: use numerix.cosh() instead
dontCacheMe(recursive=False)
dot(other, opShape=None, operatorClass=None, axis=0)
exp(*args, **kwds)
Deprecated since version 3.0: use numerix.exp() instead
floor(*args, **kwds)
Deprecated since version 3.0: use numerix.floor() instead
getMag(*args, **kwds)
Deprecated since version 3.0: use the mag property instead
getName(*args, **kwds)
Deprecated since version 3.0: use the name property instead
getNumericValue(*args, **kwds)
Deprecated since version 3.0: use the numericValue property instead
getShape(*args, **kwds)
Deprecated since version 3.0: use the shape property instead
getSubscribedVariables(*args, **kwds)
Deprecated since version 3.0: use the subscribedVariables property instead
getUnit(*args, **kwds)
Deprecated since version 3.0: use the unit property instead
getValue(*args, **kwds)
Deprecated since version 3.0: use the value property instead
getsctype(default=None)
Returns the Numpy sctype of the underlying array.
>>> Variable(1).getsctype() == numerix.NUMERIX.obj2sctype(numerix.array(1))
True
>>> Variable(1.).getsctype() == numerix.NUMERIX.obj2sctype(numerix.array(1.))
True
>>> Variable((1,1.)).getsctype() == numerix.NUMERIX.obj2sctype(numerix.array((1., 1.)))
True
inBaseUnits()
Return the value of the Variable with all units reduced to their base SI elements.
>>> e = Variable(value="2.7 Hartree*Nav")
>>> print e.inBaseUnits().allclose("7088849.01085 kg*m**2/s**2/mol")
1
inUnitsOf(*units)
Returns one or more Variable objects that express the same physical quantity in different units. The units
are specified by strings containing their names. The units must be compatible with the unit of the object.
If one unit is specified, the return value is a single Variable.
>>> freeze = Variable('0 degC')
>>> print freeze.inUnitsOf('degF').allclose("32.0 degF")
1
If several units are specified, the return value is a tuple of Variable instances with with one element per
unit such that the sum of all quantities in the tuple equals the the original quantity and all the values except
for the last one are integers. This is used to convert to irregular unit systems like hour/minute/second. The
original object will not be changed.
>>> t = Variable(value=314159., unit='s')
>>> print numerix.allclose([e.allclose(v) for (e, v) in zip(t.inUnitsOf('d','h','min','s'),
... ['3.0 d', '15.0 h', '15.0 min',
... True)
1
itemset(value)
itemsize
log(*args, **kwds)
Deprecated since version 3.0: use numerix.log() instead
log10(*args, **kwds)
Deprecated since version 3.0: use numerix.log10() instead
mag
max(axis=None)
min(axis=None)
name
numericValue
put(indices, value)
ravel()
release(constraint)
Remove constraint from self
>>> v = Variable((0,1,2,3))
>>> v.constrain(2, numerix.array((True, False, False, False)))
>>> v[:] = 10
>>> from fipy.boundaryConditions.constraint import Constraint
>>> c1 = Constraint(5, numerix.array((False, False, True, False)))
>>> v.constrain(c1)
>>> v[:] = 6
>>> v.constrain(8)
>>> v[:] = 10
>>> del v.constraints[2]
>>> v.release(constraint=c1)
>>> print v
[ 2 10 10 10]
reshape(*args, **kwds)
Deprecated since version 3.0: use numerix.reshape() instead
setName(*args, **kwds)
Deprecated since version 3.0: use the name property instead
setUnit(*args, **kwds)
Deprecated since version 3.0: use the unit property instead
setValue(value, unit=None, where=None)
Set the value of the Variable. Can take a masked array.
>>> a = Variable((1,2,3))
>>> a.setValue(5, where=(1, 0, 1))
>>> print a
[5 2 5]
>>> b = Variable((4,5,6))
>>> a.setValue(b, where=(1, 0, 1))
>>> print a
[4 2 6]
>>> print b
[4 5 6]
>>> a.value = 3
>>> print a
[3 3 3]
>>> b = numerix.array((3,4,5))
>>> a.value = b
>>> a[:] = 1
>>> print b
[3 4 5]
shape
Tuple of array dimensions.
>>> Variable(value=3).shape
()
>>> Variable(value=(3,)).shape
(1,)
>>> Variable(value=(3,4)).shape
(2,)
sign(*args, **kwds)
Deprecated since version 3.0: use numerix.sign() instead
sin(*args, **kwds)
Deprecated since version 3.0: use numerix.sin() instead
sinh(*args, **kwds)
Deprecated since version 3.0: use numerix.sinh() instead
sqrt(*args, **kwds)
Deprecated since version 3.0: use numerix.sqrt() instead
>>> from fipy.meshes import Grid1D
>>> mesh= Grid1D(nx=3)
subscribedVariables
sum(axis=None)
take(ids, axis=0)
tan(*args, **kwds)
Deprecated since version 3.0: use numerix.tan() instead
tanh(*args, **kwds)
Deprecated since version 3.0: use numerix.tanh() instead
tostring(max_line_width=75, precision=8, suppress_small=False, separator=’ ‘)
unit
Return the unit object of self.
>>> Variable(value="1 m").unit
<PhysicalUnit m>
value
“Evaluate” the Variable and return its value (longhand)
>>> a = Variable(value=3)
>>> print a.value
3
>>> b = a + 4
>>> b
(Variable(value=array(3)) + 4)
>>> b.value
7
Changes to the value of a Variable will automatically trigger changes in any dependent Variable objects
>>> a.setValue(5)
>>> b
(Variable(value=array(5)) * 4)
>>> print b()
20
Create a Variable.
>>> Variable(value=3)
Variable(value=array(3))
>>> Variable(value=3, unit="m")
Variable(value=PhysicalField(3,'m'))
>>> Variable(value=3, unit="m", array=numerix.zeros((3,2), 'l'))
Variable(value=PhysicalField(array([[3, 3],
[3, 3],
[3, 3]]),'m'))
Parameters
• value: the initial value
• unit: the physical units of the Variable
all(axis=None)
The following test is to check that the system does not run out of memory.
>>> from fipy.tools import numerix
>>> var = Variable(numerix.ones(10000))
>>> print var.allclose(numerix.zeros(10000, 'l'))
False
allequal(other)
any(axis=None)
arccos(*args, **kwds)
Deprecated since version 3.0: use numerix.arccos() instead
arccosh(*args, **kwds)
Deprecated since version 3.0: use numerix.arccosh() instead
arcsin(*args, **kwds)
Deprecated since version 3.0: use numerix.arcsin() instead
arcsinh(*args, **kwds)
Deprecated since version 3.0: use numerix.arcsinh() instead
arctan(*args, **kwds)
Deprecated since version 3.0: use numerix.arctan() instead
arctan2(*args, **kwds)
Deprecated since version 3.0: use numerix.arctan2() instead
arctanh(*args, **kwds)
Deprecated since version 3.0: use numerix.arctanh() instead
cacheMe(recursive=False)
ceil(*args, **kwds)
Deprecated since version 3.0: use numerix.ceil() instead
conjugate(*args, **kwds)
Deprecated since version 3.0: use numerix.conjugate() instead
constrain(value, where=None)
Constrain the Variable to have a value at an index or mask location specified by where.
>>> v = Variable((0,1,2,3))
>>> v.constrain(2, numerix.array((True, False, False, False)))
>>> print v
[2 1 2 3]
>>> v[:] = 10
>>> print v
[ 2 10 10 10]
>>> v.constrain(5, numerix.array((False, False, True, False)))
>>> print v
[ 2 10 5 10]
>>> v[:] = 6
>>> print v
[2 6 5 6]
>>> v.constrain(8)
>>> print v
[8 8 8 8]
>>> v[:] = 10
>>> print v
[8 8 8 8]
>>> del v.constraints[2]
>>> print v
[ 2 10 5 10]
Parameters
• value: the value of the constraint
• where: the constraint mask or index specifying the location of the constraint
constraints
copy()
Make an duplicate of the Variable
>>> a = Variable(value=3)
>>> b = a.copy()
>>> b
Variable(value=array(3))
>>> a.setValue(5)
>>> b
Variable(value=array(3))
cos(*args, **kwds)
Deprecated since version 3.0: use numerix.cos() instead
cosh(*args, **kwds)
Deprecated since version 3.0: use numerix.cosh() instead
dontCacheMe(recursive=False)
dot(other, opShape=None, operatorClass=None, axis=0)
exp(*args, **kwds)
Deprecated since version 3.0: use numerix.exp() instead
floor(*args, **kwds)
Deprecated since version 3.0: use numerix.floor() instead
getMag(*args, **kwds)
Deprecated since version 3.0: use the mag property instead
getName(*args, **kwds)
Deprecated since version 3.0: use the name property instead
getNumericValue(*args, **kwds)
Deprecated since version 3.0: use the numericValue property instead
getShape(*args, **kwds)
Deprecated since version 3.0: use the shape property instead
getSubscribedVariables(*args, **kwds)
Deprecated since version 3.0: use the subscribedVariables property instead
getUnit(*args, **kwds)
Deprecated since version 3.0: use the unit property instead
getValue(*args, **kwds)
Deprecated since version 3.0: use the value property instead
getsctype(default=None)
Returns the Numpy sctype of the underlying array.
>>> Variable(1).getsctype() == numerix.NUMERIX.obj2sctype(numerix.array(1))
True
>>> Variable(1.).getsctype() == numerix.NUMERIX.obj2sctype(numerix.array(1.))
True
>>> Variable((1,1.)).getsctype() == numerix.NUMERIX.obj2sctype(numerix.array((1., 1.)))
True
inBaseUnits()
Return the value of the Variable with all units reduced to their base SI elements.
inUnitsOf(*units)
Returns one or more Variable objects that express the same physical quantity in different units. The units
are specified by strings containing their names. The units must be compatible with the unit of the object.
If one unit is specified, the return value is a single Variable.
>>> freeze = Variable('0 degC')
>>> print freeze.inUnitsOf('degF').allclose("32.0 degF")
1
If several units are specified, the return value is a tuple of Variable instances with with one element per
unit such that the sum of all quantities in the tuple equals the the original quantity and all the values except
for the last one are integers. This is used to convert to irregular unit systems like hour/minute/second. The
original object will not be changed.
>>> t = Variable(value=314159., unit='s')
>>> print numerix.allclose([e.allclose(v) for (e, v) in zip(t.inUnitsOf('d','h','min','s'),
... ['3.0 d', '15.0 h', '15.0 min',
... True)
1
itemset(value)
itemsize
log(*args, **kwds)
Deprecated since version 3.0: use numerix.log() instead
log10(*args, **kwds)
Deprecated since version 3.0: use numerix.log10() instead
mag
max(axis=None)
min(axis=None)
name
numericValue
put(indices, value)
ravel()
release(constraint)
Remove constraint from self
>>> v = Variable((0,1,2,3))
>>> v.constrain(2, numerix.array((True, False, False, False)))
>>> v[:] = 10
>>> from fipy.boundaryConditions.constraint import Constraint
>>> c1 = Constraint(5, numerix.array((False, False, True, False)))
>>> v.constrain(c1)
>>> v[:] = 6
>>> v.constrain(8)
>>> v[:] = 10
>>> del v.constraints[2]
>>> v.release(constraint=c1)
>>> print v
[ 2 10 10 10]
reshape(*args, **kwds)
Deprecated since version 3.0: use numerix.reshape() instead
setName(*args, **kwds)
Deprecated since version 3.0: use the name property instead
setUnit(*args, **kwds)
Deprecated since version 3.0: use the unit property instead
setValue(value, unit=None, where=None)
Set the value of the Variable. Can take a masked array.
>>> a = Variable((1,2,3))
>>> a.setValue(5, where=(1, 0, 1))
>>> print a
[5 2 5]
>>> b = Variable((4,5,6))
>>> a.setValue(b, where=(1, 0, 1))
>>> print a
[4 2 6]
>>> print b
[4 5 6]
>>> a.value = 3
>>> print a
[3 3 3]
>>> b = numerix.array((3,4,5))
>>> a.value = b
>>> a[:] = 1
>>> print b
[3 4 5]
shape
Tuple of array dimensions.
>>> Variable(value=3).shape
()
>>> Variable(value=(3,)).shape
(1,)
>>> Variable(value=(3,4)).shape
(2,)
sign(*args, **kwds)
Deprecated since version 3.0: use numerix.sign() instead
sin(*args, **kwds)
Deprecated since version 3.0: use numerix.sin() instead
sinh(*args, **kwds)
Deprecated since version 3.0: use numerix.sinh() instead
sqrt(*args, **kwds)
Deprecated since version 3.0: use numerix.sqrt() instead
>>> from fipy.meshes import Grid1D
>>> mesh= Grid1D(nx=3)
subscribedVariables
sum(axis=None)
take(ids, axis=0)
tan(*args, **kwds)
Deprecated since version 3.0: use numerix.tan() instead
tanh(*args, **kwds)
Deprecated since version 3.0: use numerix.tanh() instead
tostring(max_line_width=75, precision=8, suppress_small=False, separator=’ ‘)
unit
Return the unit object of self.
>>> Variable(value="1 m").unit
<PhysicalUnit m>
value
“Evaluate” the Variable and return its value (longhand)
>>> a = Variable(value=3)
>>> print a.value
3
>>> b = a + 4
>>> b
(Variable(value=array(3)) + 4)
>>> b.value
7
arithmeticFaceValue
Returns a FaceVariable whose value corresponds to the arithmetic interpolation of the adjacent cells:
𝑑𝑓 2
𝜑𝑓 = (𝜑1 − 𝜑2 ) + 𝜑2
𝑑12
cellVolumeAverage
Return the cell-volume-weighted average of the CellVariable:
∑︀
cells 𝜑cell 𝑉cell
< 𝜑 >vol = ∑︀
cells 𝑉cell
constrain(value, where=None)
Constrains the CellVariable to value at a location specified by where.
>>> from fipy import *
>>> m = Grid1D(nx=3)
copy()
faceGrad
Return ∇𝜑 as a rank-1 FaceVariable using differencing for the normal direction(second-order gradient).
faceGradAverage
Return ∇𝜑 as a rank-1 FaceVariable using averaging for the normal direction(second-order gradient)
faceValue
Returns a FaceVariable whose value corresponds to the arithmetic interpolation of the adjacent cells:
𝑑𝑓 2
𝜑𝑓 = (𝜑1 − 𝜑2 ) + 𝜑2
𝑑12
>>> R = 2
>>> var = CellVariable(mesh = mesh, value = (L, R))
>>> faceValue = var.arithmeticFaceValue[mesh.interiorFaces.value]
>>> answer = (R - L) * (0.5 / 1.) + L
>>> print numerix.allclose(faceValue, answer, atol = 1e-10, rtol = 1e-10)
True
gaussGrad ∑︀
Return 𝑉1𝑃 𝑓 ⃗𝑛𝜑𝑓 𝐴𝑓 as a rank-1 CellVariable (first-order gradient).
getArithmeticFaceValue(*args, **kwds)
Deprecated since version 3.0: use the arithmeticFaceValue property instead
getCellVolumeAverage(*args, **kwds)
Deprecated since version 3.0: use the cellVolumeAverage property instead
getFaceGrad(*args, **kwds)
Deprecated since version 3.0: use the faceGrad property instead
getFaceGradAverage(*args, **kwds)
Deprecated since version 3.0: use the faceGradAverage property instead
getFaceValue(*args, **kwds)
Deprecated since version 3.0: use the arithmeticFaceValue property instead
getGaussGrad(*args, **kwds)
Deprecated since version 3.0: use the gaussGrad property instead
getGrad(*args, **kwds)
Deprecated since version 3.0: use the grad property instead
getHarmonicFaceValue(*args, **kwds)
Deprecated since version 3.0: use the harmonicFaceValue property instead
getLeastSquaresGrad(*args, **kwds)
Deprecated since version 3.0: use the leastSquaresGrad property instead
getMinmodFaceValue(*args, **kwds)
Deprecated since version 3.0: use the minmodFaceValue property instead
getOld(*args, **kwds)
Deprecated since version 3.0: use the old property instead
globalValue
Concatenate and return values from all processors
When running on a single processor, the result is identical to value.
grad
Return ∇𝜑 as a rank-1 CellVariable (first-order gradient).
harmonicFaceValue
Returns a FaceVariable whose value corresponds to the harmonic interpolation of the adjacent cells:
𝜑1 𝜑2
𝜑𝑓 = 𝑑
(𝜑2 − 𝜑1 ) 𝑑𝑓122 + 𝜑1
leastSquaresGrad
Return ∇𝜑, which is determined by solving for ∇𝜑 in the following matrix equation,
∑︁ ∑︁
∇𝜑 · 𝑑2𝐴𝑃 ⃗𝑛𝐴𝑃 ⊗ ⃗𝑛𝐴𝑃 = 𝑑2𝐴𝑃 (⃗𝑛 · ∇𝜑)𝐴𝑃
𝑓 𝑓
The matrix equation is derived by minimizing the following least squares sum,
√︃∑︁
2
𝐹 (𝜑𝑥 , 𝜑𝑦 ) = (𝑑𝐴𝑃 ⃗𝑛𝐴𝑃 · ∇𝜑 − 𝑑𝐴𝑃 (⃗𝑛𝐴𝑃 · ∇𝜑)𝐴𝑃 )
𝑓
Tests
>>> from fipy import Grid2D
>>> m = Grid2D(nx=2, ny=2, dx=0.1, dy=2.0)
>>> print numerix.allclose(CellVariable(mesh=m, value=(0,1,3,6)).leastSquaresGrad.globalValu
... [[8.0, 8.0, 24.0, 24.0],
... [1.2, 2.0, 1.2, 2.0]])
True
minmodFaceValue
Returns a FaceVariable with a value that is the minimum of the absolute values of the adjacent cells. If the
values are of opposite sign then the result is zero:
⎧
⎨𝜑1 when |𝜑1 | ≤ |𝜑2 |,
⎪
𝜑𝑓 = 𝜑2 when |𝜑2 | < |𝜑1 |,
when 𝜑1𝜑2 < 0
⎪
0
⎩
old
Return the values of the CellVariable from the previous solution sweep.
Combinations of CellVariable’s should also return old values.
>>> from fipy.meshes import Grid1D
>>> mesh = Grid1D(nx = 2)
>>> from fipy.variables.cellVariable import CellVariable
>>> var1 = CellVariable(mesh = mesh, value = (2, 3), hasOld = 1)
>>> var2 = CellVariable(mesh = mesh, value = (3, 4))
>>> v = var1 * var2
>>> print v
[ 6 12]
>>> var1.value = ((3,2))
>>> print v
[9 8]
>>> print v.old
[ 6 12]
The following small test is to correct for a bug when the operator does not just use variables.
>>> v1 = var1 * 3
>>> print v1
[9 6]
>>> print v1.old
[6 9]
release(constraint)
Remove constraint from self
>>> from fipy import *
>>> m = Grid1D(nx=3)
>>> v = CellVariable(mesh=m, value=m.cellCenters[0])
>>> c = Constraint(0., where=m.facesLeft)
>>> v.constrain(c)
>>> print v.faceValue
[ 0. 1. 2. 2.5]
>>> v.release(constraint=c)
>>> print v.faceValue
[ 0.5 1. 2. 2.5]
updateOld()
Set the values of the previous solution sweep to the current values.
>>> from fipy import *
>>> v = CellVariable(mesh=Grid1D(), hasOld=False)
>>> v.updateOld()
Traceback (most recent call last):
...
AssertionError: The updateOld method requires the CellVariable to have an old value. Set has
getDivergence(*args, **kwds)
Deprecated since version 3.0: use the divergence property instead
globalValue
setValue(value, unit=None, where=None)
class fipy.variables.ScharfetterGummelFaceVariable(var, boundaryConditions=())
Bases: fipy.variables.cellToFaceVariable._CellToFaceVariable
class fipy.variables.ModularVariable(mesh, name=’‘, value=0.0, rank=None, ele-
mentshape=None, unit=None, hasOld=0)
Bases: fipy.variables.cellVariable.CellVariable
The ModularVariable defines a variable that exisits on the circle between −𝜋 and 𝜋
The following examples show how ModularVariable works. When subtracting the answer wraps back around
the circle.
>>> from fipy.meshes import Grid1D
>>> mesh = Grid1D(nx = 2)
>>> from fipy.tools import numerix
>>> pi = numerix.pi
>>> v1 = ModularVariable(mesh = mesh, value = (2*pi/3, -2*pi/3))
arithmeticFaceValue
Returns a FaceVariable whose value corresponds to the arithmetic interpolation of the adjacent cells:
𝑑𝑓 2
𝜑𝑓 = (𝜑1 − 𝜑2 ) + 𝜑2
𝑑12
Adjusted for a ModularVariable
faceGrad
Return ∇𝜑 as a rank-1 FaceVariable (second-order gradient). Adjusted for a ModularVariable
faceGradNoMod
Return ∇𝜑 as a rank-1 FaceVariable (second-order gradient). Not adjusted for a ModularVariable
getFaceGradNoMod(*args, **kwds)
Deprecated since version 3.0: use the faceGradNoMod property instead
grad
Return ∇𝜑 as a rank-1 CellVariable (first-order gradient). Adjusted for a ModularVariable
updateOld()
Set the values of the previous solution sweep to the current values. Test case due to bug.
>>> from fipy.meshes import Grid1D
>>> mesh = Grid1D(nx = 1)
>>> var = ModularVariable(mesh=mesh, value=1., hasOld=1)
>>> var.updateOld()
>>> var[:] = 2
>>> answer = CellVariable(mesh=mesh, value=1.)
>>> print var.old.allclose(answer)
True
∫︀ ∞
with a shape parameter 𝛼, a rate parameter 𝛽, and Γ(𝑧) = 0
𝑡𝑧−1 𝑒−𝑡 𝑑𝑡.
Seed the random module for the sake of deterministic test results.
>>> from fipy import numerix
>>> numerix.random.seed(1)
2 a = b = 0.5
Parameters
• mesh: The mesh on which to define the noise.
• alpha: The parameter 𝛼.
• beta: The parameter 𝛽.
random()
class fipy.variables.ExponentialNoiseVariable(mesh, mean=0.0, name=’‘, hasOld=0)
Bases: fipy.variables.noiseVariable.NoiseVariable
Represents an exponential distribution of random numbers with the probability distribution
𝑥
𝜇−1 𝑒− 𝜇
0.6
0.5
0.4
m = 1.5
0.3
0.2
0.1
0.0
2 4 6 8
Parameters
• mesh: The mesh on which to define the noise.
• mean: The mean of the distribution 𝜇.
random()
class fipy.variables.GammaNoiseVariable(mesh, shape, rate, name=’‘, hasOld=0)
Bases: fipy.variables.noiseVariable.NoiseVariable
Represents a gamma distribution of random numbers with the probability distribution
𝛽 𝛼 𝑒−𝛽𝑥
𝑥𝛼−1
Γ(𝛼)
∫︀ ∞
with a shape parameter 𝛼, a rate parameter 𝛽, and Γ(𝑧) = 0 𝑡𝑧−1 𝑒−𝑡 𝑑𝑡.
Seed the random module for the sake of deterministic test results.
>>> from fipy import numerix
>>> numerix.random.seed(1)
0.30
0.25
0.20
a=4
0.15
b=1
0.10
0.05
0.00
0 5 10 15 20
Parameters
• mesh: The mesh on which to define the noise.
• shape: The shape parameter, 𝛼.
• rate: The rate or inverse scale parameter, 𝛽.
random()
class fipy.variables.GaussianNoiseVariable(mesh, name=’‘, mean=0.0, variance=1.0, ha-
sOld=0)
Bases: fipy.variables.noiseVariable.NoiseVariable
Represents a normal (Gaussian) distribution of random numbers with mean 𝜇 and variance ⟨𝜂(⃗𝑟)𝜂(⃗𝑟 ′ )⟩ = 𝜎 2 ,
which has the probability distribution
1 (𝑥 − 𝜇)2
√ exp −
𝜎 2𝜋 2𝜎 2
For example, the variance of thermal noise that is uncorrelated in space and time is often expressed as
Note: If the time step will change as the simulation progresses, either through use of an adaptive iterator or by
making manual changes at different stages, remember to declare timeStep as a Variable and to change its value
with its setValue() method.
>>> mean = 0.
>>> variance = 4.
Seed the random module for the sake of deterministic test results.
>>> from fipy import numerix
>>> numerix.random.seed(3)
Note that the noise exhibits larger amplitude in the small cells than in the large ones
0.20
0.15
s=2
0.10
0.05
0.00
-10 -5 0 5 10
Parameters
• mesh: The mesh on which to define the noise.
• mean: The mean of the noise distrubution, 𝜇.
• variance: The variance of the noise distribution, 𝜎 2 .
parallelRandom()
class fipy.variables.UniformNoiseVariable(mesh, name=’‘, minimum=0.0, maximum=1.0, ha-
sOld=0)
Bases: fipy.variables.noiseVariable.NoiseVariable
Represents a uniform distribution of random numbers.
We generate noise on a uniform cartesian mesh
>>> from fipy.meshes import Grid2D
>>> noise = UniformNoiseVariable(mesh=Grid2D(nx=100, ny=100))
1.2
1.0
0.8
0.6
0.4
0.2
0.0
Parameters
• mesh: The mesh on which to define the noise.
• minimum: The minimum (not-inclusive) value of the distribution.
• maximum: The maximum (not-inclusive) value of the distribution.
random()
class fipy.variables.HistogramVariable(distribution, dx=1.0, nx=None, offset=0.0)
Bases: fipy.variables.cellVariable.CellVariable
Produces a histogram of the values of the supplied distribution.
Parameters
• distribution: The collection of values to sample.
• dx: the bin size
• nx: the number of bins
• offset: the position of the first bin
class fipy.variables.SurfactantVariable(value=0.0, distanceVar=None, name=’surfactant
variable’, hasOld=False)
Bases: fipy.variables.cellVariable.CellVariable
The SurfactantVariable maintains a conserved volumetric concentration on cells adjacent to, but in front of,
the interface. The value argument corresponds to the initial concentration of surfactant on the interface (moles
divided by area). The value held by the SurfactantVariable is actually a volume density (moles divided by
volume).
A simple 1D test:
>>> from fipy.meshes import Grid1D
>>> mesh = Grid1D(dx = 1., nx = 4)
>>> from fipy.variables.distanceVariable import DistanceVariable
A 2D test case:
>>> from fipy.meshes import Grid2D
>>> mesh = Grid2D(dx = 1., dy = 1., nx = 3, ny = 3)
>>> distanceVariable = DistanceVariable(mesh = mesh,
... value = (1.5, 0.5, 1.5,
... 0.5,-0.5, 0.5,
... 1.5, 0.5, 1.5))
>>> surfactantVariable = SurfactantVariable(value = 1,
... distanceVar = distanceVariable)
>>> print numerix.allclose(surfactantVariable, (0, 1, 0, 1, 0, 1, 0, 1, 0))
1
Parameters
• value: The initial value.
• distanceVar: A DistanceVariable object.
• name: The name of the variable.
copy()
getInterfaceVar(*args, **kwds)
Deprecated since version 3.0: use the interfaceVar property instead
interfaceVar
Returns the SurfactantVariable rendered as an _InterfaceSurfactantVariable which evaluates the surfactant
concentration as an area concentration the interface rather than a volumetric concentration.
class fipy.variables.SurfactantConvectionVariable(distanceVar)
Bases: fipy.variables.faceVariable.FaceVariable
Convection coefficient for the ConservativeSurfactantEquation. The coeff only has a value for a negative dis-
tanceVar.
Simple one dimensional test:
>>> from fipy.variables.cellVariable import CellVariable
>>> from fipy.meshes import Grid2D
>>> mesh = Grid2D(nx = 3, ny = 1, dx = 1., dy = 1.)
>>> from fipy.variables.distanceVariable import DistanceVariable
>>> distanceVar = DistanceVariable(mesh, value = (-.5, .5, 1.5))
Larger grid:
>>> mesh = Grid2D(nx = 3, ny = 3, dx = 1., dy = 1.)
>>> distanceVar = DistanceVariable(mesh, value = (1.5, .5 , 1.5,
... .5 , -.5, .5 ,
... 1.5, .5 , 1.5))
>>> answer = FaceVariable(mesh=mesh, rank=1, value=0.).globalValue
>>> answer[1,4] = .25
>>> answer[1,7] = -.25
>>> answer[0,17] = .25
>>> answer[0,18] = -.25
>>> print numerix.allclose(SurfactantConvectionVariable(distanceVar).globalValue, answer)
True
|∇𝜑| = 1
using the fast marching method with an initial condition defined by the zero level set. The solution can either be
first or second order.
Here we will define a few test cases. Firstly a 1D test case
>>> from fipy.meshes import Grid1D
>>> from fipy.tools import serialComm
>>> mesh = Grid1D(dx = .5, nx = 8, communicator=serialComm)
>>> from distanceVariable import DistanceVariable
>>> var = DistanceVariable(mesh = mesh, value = (-1., -1., -1., -1., 1., 1., 1., 1.))
>>> var.calcDistanceFunction()
>>> answer = (-1.75, -1.25, -.75, -0.25, 0.25, 0.75, 1.25, 1.75)
>>> print var.allclose(answer)
1
>>> var.calcDistanceFunction()
>>> vbl = -dx * dy / numerix.sqrt(dx**2 + dy**2) / 2.
>>> vbr = dx / 2
>>> vml = dy / 2.
>>> crossProd = dx * dy
>>> dsq = dx**2 + dy**2
>>> top = vbr * dx**2 + vml * dy**2
>>> sqrt = crossProd**2 *(dsq - (vbr - vml)**2)
>>> sqrt = numerix.sqrt(max(sqrt, 0))
>>> vmr = (top + sqrt) / dsq
>>> answer = (vbl, vbr, vml, vmr, vbl, vbr)
>>> print var.allclose(answer)
1
The extendVariable method solves the following equation for a given extensionVariable.
∇𝑢 · ∇𝜑 = 0
using the fast marching method with an initial condition defined at the zero level set.
>>> from fipy.variables.cellVariable import CellVariable
>>> mesh = Grid2D(dx = 1., dy = 1., nx = 2, ny = 2, communicator=serialComm)
>>> var = DistanceVariable(mesh = mesh, value = (-1., 1., 1., 1.))
>>> var.calcDistanceFunction()
>>> extensionVar = CellVariable(mesh = mesh, value = (-1, .5, 2, -1))
>>> tmp = 1 / numerix.sqrt(2)
>>> print var.allclose((-tmp / 2, 0.5, 0.5, 0.5 + tmp))
1
>>> var.extendVariable(extensionVar, order=1)
>>> print extensionVar.allclose((1.25, .5, 2, 1.25))
1
>>> mesh = Grid2D(dx = 1., dy = 1., nx = 3, ny = 3, communicator=serialComm)
>>> var = DistanceVariable(mesh = mesh, value = (-1., 1., 1.,
... 1., 1., 1.,
... 1., 1., 1.))
>>> var.calcDistanceFunction(order=1)
>>> extensionVar = CellVariable(mesh = mesh, value = (-1., .5, -1.,
... 2., -1., -1.,
... -1., -1., -1.))
Test case for a bug that occurs when initializing the distance variable at the interface. Currently it is assumed
that adjacent cells that are opposite sign neighbors have perpendicular normal vectors. In fact the two closest
cells could have opposite normals.
>>> mesh = Grid1D(dx = 1., nx = 3)
>>> var = DistanceVariable(mesh = mesh, value = (-1., 1., -1.))
>>> var.calcDistanceFunction()
>>> print var.allclose((-0.5, 0.5, -0.5))
1
The 3rd and 7th element are different for LSMLIB. This is because the 15th element is not “known” when the
“trial” value for the 7th element is calculated. Scikit-fmm calculates the values in a slightly different order so
gets a seemingly better answer, but this is just chance.
>>> print numerix.allclose(var, answer, rtol=1e-9)
True
A 2D test case:
>>> from fipy.meshes import Grid2D
>>> from fipy.variables.cellVariable import CellVariable
>>> mesh = Grid2D(dx = 1., dy = 1., nx = 3, ny = 3)
>>> distanceVariable = DistanceVariable(mesh = mesh,
... value = (1.5, 0.5, 1.5,
... 0.5,-0.5, 0.5,
... 1.5, 0.5, 1.5))
>>> answer = CellVariable(mesh=mesh,
... value=(0, 1, 0, 1, 0, 1, 0, 1, 0))
>>> print numerix.allclose(distanceVariable.cellInterfaceAreas, answer)
True
... answer)
True
extendVariable(extensionVariable, order=2)
Calculates the extension of extensionVariable from the zero level set.
Parameters
• extensionVariable: The variable to extend from the zero level set.
getCellInterfaceAreas(*args, **kwds)
Deprecated since version 3.0: use the cellInterfaceAreas property instead
getLSMshape()
28.1 Subpackages
Submodules
fipy.viewers.gistViewer.colorbar module
fipy.viewers.gistViewer.gist1DViewer module
fipy.viewers.gistViewer.gist2DViewer module
fipy.viewers.gistViewer.gistVectorViewer module
fipy.viewers.gistViewer.gistViewer module
fipy.viewers.gistViewer.test module
Module contents
385
FiPy Manual, Release 3.1.3
Submodules
fipy.viewers.gnuplotViewer.gnuplot1DViewer module
fipy.viewers.gnuplotViewer.gnuplot2DViewer module
fipy.viewers.gnuplotViewer.gnuplotViewer module
fipy.viewers.gnuplotViewer.test module
Module contents
Submodules
fipy.viewers.matplotlibViewer.matplotlib1DViewer module
class fipy.viewers.matplotlibViewer.matplotlib1DViewer.Matplotlib1DViewer(vars,
ti-
tle=None,
xlog=False,
ylog=False,
lim-
its={},
leg-
end=’upper
left’,
axes=None,
**kwlim-
its)
Bases: fipy.viewers.matplotlibViewer.matplotlibViewer.AbstractMatplotlibViewer
Displays a y vs. x plot of one or more 1D CellVariable objects using Matplotlib.
Parameters
vars a CellVariable or tuple of CellVariable objects to plot
title displayed at the top of the Viewer window
xlog log scaling of x axis if True
ylog log scaling of y axis if True
limits [dict] a (deprecated) alternative to limit keyword arguments
xmin, xmax, datamin, datamax displayed range of data. Any limit set to a (default) value of
None will autoscale. (ymin and ymax are synonyms for datamin and datamax).
legend place a legend at the specified position, if not None
axes if not None, vars will be plotted into this Matplotlib Axes object
log
logarithmic data scaling
fipy.viewers.matplotlibViewer.matplotlib2DContourViewer module
fipy.viewers.matplotlibViewer.matplotlib2DGridContourViewer module
class fipy.viewers.matplotlibViewer.matplotlib2DGridContourViewer.Matplotlib2DGridContourView
Bases: fipy.viewers.matplotlibViewer.matplotlib2DViewer.AbstractMatplotlib2DViewer
Displays a contour plot of a 2D CellVariable object.
The Matplotlib2DGridContourViewer plots a 2D CellVariable using Matplotlib.
Creates a Matplotlib2DViewer.
Parameters
vars a CellVariable object.
title displayed at the top of the Viewer window
limits [dict] a (deprecated) alternative to limit keyword arguments
xmin, xmax, ymin, ymax, datamin, datamax displayed range of data. Any limit set to a (de-
fault) value of None will autoscale.
cmap the colormap. Defaults to matplotlib.cm.jet
colorbar plot a colorbar in specified orientation if not None
axes if not None, vars will be plotted into this Matplotlib Axes object
figaspect desired aspect ratio of figure. If arg is a number, use that aspect ratio. If arg is ‘auto’,
the aspect ratio will be determined from the Variable’s mesh.
fipy.viewers.matplotlibViewer.matplotlib2DGridViewer module
class fipy.viewers.matplotlibViewer.matplotlib2DGridViewer.Matplotlib2DGridViewer(vars,
ti-
tle=None,
lim-
its={},
cmap=None,
col-
or-
bar=’vertical’,
axes=None,
fi-
gaspect=’auto’,
**kwlim-
its)
Bases: fipy.viewers.matplotlibViewer.matplotlib2DViewer.AbstractMatplotlib2DViewer
Displays an image plot of a 2D CellVariable object using Matplotlib.
>>> from fipy import *
>>> mesh = Grid2D(nx=50, ny=100, dx=0.1, dy=0.01)
>>> x, y = mesh.cellCenters
>>> xyVar = CellVariable(mesh=mesh, name="x y", value=x * y)
Creates a Matplotlib2DGridViewer.
Parameters
vars A CellVariable object.
title displayed at the top of the Viewer window
limits [dict] a (deprecated) alternative to limit keyword arguments
cmap The colormap. Defaults to matplotlib.cm.jet
xmin, xmax, ymin, ymax, datamin, datamax displayed range of data. Any limit set to a (de-
fault) value of None will autoscale.
colorbar plot a colorbar in specified orientation if not None
axes if not None, vars will be plotted into this Matplotlib Axes object
figaspect desired aspect ratio of figure. If arg is a number, use that aspect ratio. If arg is ‘auto’,
the aspect ratio will be determined from the Variable’s mesh.
fipy.viewers.matplotlibViewer.matplotlib2DViewer module
class fipy.viewers.matplotlibViewer.matplotlib2DViewer.Matplotlib2DViewer(vars,
ti-
tle=None,
lim-
its={},
cmap=None,
col-
or-
bar=’vertical’,
axes=None,
fi-
gaspect=’auto’,
**kwlim-
its)
Bases: fipy.viewers.matplotlibViewer.matplotlib2DViewer.AbstractMatplotlib2DViewer
Displays a contour plot of a 2D CellVariable object.
The Matplotlib2DViewer plots a 2D CellVariable using Matplotlib.
>>> from fipy import *
>>> mesh = (Grid2D(nx=5, ny=10, dx=0.1, dy=0.1)
... + (Tri2D(nx=5, ny=5, dx=0.1, dy=0.1)
... + ((0.5,), (0.2,))))
>>> x, y = mesh.cellCenters
>>> xyVar = CellVariable(mesh=mesh, name="x y", value=x * y)
Creates a Matplotlib2DViewer.
Parameters
vars a CellVariable object.
title displayed at the top of the Viewer window
limits [dict] a (deprecated) alternative to limit keyword arguments
cmap the colormap. Defaults to matplotlib.cm.jet
xmin, xmax, ymin, ymax, datamin, datamax displayed range of data. Any limit set to a (de-
fault) value of None will autoscale.
colorbar plot a colorbar in specified orientation if not None
axes if not None, vars will be plotted into this Matplotlib Axes object
figaspect desired aspect ratio of figure. If arg is a number, use that aspect ratio. If arg is ‘auto’,
the aspect ratio will be determined from the Variable’s mesh.
fipy.viewers.matplotlibViewer.matplotlibSparseMatrixViewer module
class fipy.viewers.matplotlibViewer.matplotlibSparseMatrixViewer.MatplotlibSparseMatrixViewer
fipy.viewers.matplotlibViewer.matplotlibStreamViewer module
class fipy.viewers.matplotlibViewer.matplotlibStreamViewer.MatplotlibStreamViewer(vars,
ti-
tle=None,
log=False,
lim-
its={},
axes=None,
fi-
gaspect=’auto’,
den-
sity=1,
linewidth=None
color=None,
cmap=None,
norm=None,
ar-
row-
size=1,
arrowstyle=’-
|>’,
min-
length=0.1,
**kwlim-
its)
Bases: fipy.viewers.matplotlibViewer.matplotlib2DViewer.AbstractMatplotlib2DViewer
Displays a stream plot of a 2D rank-1 CellVariable or FaceVariable object using Matplotlib
One issue is that this Viewer relies on scipy.interpolate.griddata, which interpolates on the convex hull of the
data. The results is that streams are plotted across any concavities in the mesh.
Another issue is that it does not seem possible to remove the streams without calling cla(), which means that
different set of streams cannot be overlaid.
>>> from fipy import *
>>> mesh = Grid2D(nx=50, ny=100, dx=0.1, dy=0.01)
>>> x, y = mesh.cellCenters
>>> xyVar = CellVariable(mesh=mesh, name="x y", value=x * y)
>>> k = Variable(name="k", value=1.)
>>> viewer = MatplotlibStreamViewer(vars=numerix.sin(k * xyVar).grad,
... title="MatplotlibStreamViewer test")
>>> for kval in numerix.arange(1, 10):
... k.setValue(kval)
... viewer.plot()
>>> viewer._promptForOpinion()
Creates a MatplotlibStreamViewer.
Parameters
vars a rank-1 CellVariable or FaceVariable object.
title displayed at the top of the Viewer window
log if True, arrow length goes at the base-10 logarithm of the magnitude
limits [dict] a (deprecated) alternative to limit keyword arguments
xmin, xmax, ymin, ymax, datamin, datamax displayed range of data. Any limit set to a (de-
fault) value of None will autoscale.
axes if not None, vars will be plotted into this Matplotlib Axes object
figaspect desired aspect ratio of figure. If arg is a number, use that aspect ratio. If arg is ‘auto’,
the aspect ratio will be determined from the Variable’s mesh.
density [float or 2-tuple] Controls the closeness of streamlines. When density = 1, the domain
is divided into a 25x25 grid—density linearly scales this grid. Each cell in the grid can have,
at most, one traversing streamline. For different densities in each direction, use [density_x,
density_y].
linewidth [Numeric or rank-0 MeshVariable] vary linewidth when given a CellVariable or Face-
Variable of same type as vars.
color [matplotlib color code, or rank-0 MeshVariable] Streamline color. When given an array
with the type as vars, color values are converted to colors using cmap.
cmap [Colormap] Colormap used to plot streamlines and arrows. Only necessary when using
an MeshVariable input for color.
norm [Normalize] Normalize object used to scale luminance data to 0, 1. If None, stretch
(min, max) to (0, 1). Only necessary when color is an MeshVariable.
arrowsize [float] Factor scale arrow size.
arrowstyle [str] Arrow style specification. See FancyArrowPatch.
minlength [float] Minimum length of streamline in axes coordinates.
fipy.viewers.matplotlibViewer.matplotlibVectorViewer module
class fipy.viewers.matplotlibViewer.matplotlibVectorViewer.MatplotlibVectorViewer(vars,
ti-
tle=None,
scale=None,
spar-
sity=None,
log=False,
lim-
its={},
axes=None,
fi-
gaspect=’auto’,
**kwlim-
its)
Bases: fipy.viewers.matplotlibViewer.matplotlib2DViewer.AbstractMatplotlib2DViewer
Displays a vector plot of a 2D rank-1 CellVariable or FaceVariable object using Matplotlib
>>> from fipy import *
>>> mesh = Grid2D(nx=50, ny=100, dx=0.1, dy=0.01)
>>> x, y = mesh.cellCenters
>>> xyVar = CellVariable(mesh=mesh, name="x y", value=x * y)
>>> k = Variable(name="k", value=1.)
>>> viewer = MatplotlibVectorViewer(vars=numerix.sin(k * xyVar).grad,
... title="MatplotlibVectorViewer test")
>>> for kval in numerix.arange(1, 10):
... k.setValue(kval)
... viewer.plot()
>>> viewer._promptForOpinion()
Creates a Matplotlib2DViewer.
Parameters
vars a rank-1 CellVariable or FaceVariable object.
title displayed at the top of the Viewer window
scale if not None, scale all arrow lengths by this value
sparsity if not None, then this number of arrows will be randomly chosen (weighted by the cell
volume or face area)
log if True, arrow length goes at the base-10 logarithm of the magnitude
limits [dict] a (deprecated) alternative to limit keyword arguments
xmin, xmax, ymin, ymax, datamin, datamax displayed range of data. Any limit set to a (de-
fault) value of None will autoscale.
axes if not None, vars will be plotted into this Matplotlib Axes object
figaspect desired aspect ratio of figure. If arg is a number, use that aspect ratio. If arg is ‘auto’,
the aspect ratio will be determined from the Variable’s mesh.
quiver(sparsity=None, scale=None)
fipy.viewers.matplotlibViewer.matplotlibViewer module
class fipy.viewers.matplotlibViewer.matplotlibViewer.AbstractMatplotlibViewer(vars,
ti-
tle=None,
fi-
gaspect=1.0,
cmap=None,
col-
or-
bar=None,
axes=None,
log=False,
**kwlim-
its)
Bases: fipy.viewers.viewer.AbstractViewer
The AbstractMatplotlibViewer is the base class for the viewers that use the Matplotlib python plotting package.
Create a AbstractMatplotlibViewer.
Parameters
vars a CellVariable or tuple of CellVariable objects to plot
fipy.viewers.matplotlibViewer.test module
Module contents
>>> pylab.ion()
>>> fig = pylab.figure()
>>> viewer._promptForOpinion()
Parameters
vars a CellVariable or tuple of CellVariable objects to plot
title displayed at the top of the Viewer window
xlog log scaling of x axis if True
ylog log scaling of y axis if True
limits [dict] a (deprecated) alternative to limit keyword arguments
xmin, xmax, datamin, datamax displayed range of data. Any limit set to a (default) value of
None will autoscale. (ymin and ymax are synonyms for datamin and datamax).
legend place a legend at the specified position, if not None
axes if not None, vars will be plotted into this Matplotlib Axes object
log
logarithmic data scaling
class fipy.viewers.matplotlibViewer.Matplotlib2DGridViewer(vars, title=None, lim-
its={}, cmap=None,
colorbar=’vertical’,
axes=None, fi-
gaspect=’auto’, **kwlim-
its)
Bases: fipy.viewers.matplotlibViewer.matplotlib2DViewer.AbstractMatplotlib2DViewer
Displays an image plot of a 2D CellVariable object using Matplotlib.
>>> from fipy import *
>>> mesh = Grid2D(nx=50, ny=100, dx=0.1, dy=0.01)
>>> x, y = mesh.cellCenters
>>> xyVar = CellVariable(mesh=mesh, name="x y", value=x * y)
>>> k = Variable(name="k", value=0.)
>>> viewer = Matplotlib2DGridViewer(vars=numerix.sin(k * xyVar),
... limits={'ymin': 0.1, 'ymax': 0.9},
... datamin=-0.9, datamax=2.0,
... title="Matplotlib2DGridViewer test")
>>> for kval in range(10):
... k.setValue(kval)
... viewer.plot()
>>> viewer._promptForOpinion()
Creates a Matplotlib2DGridViewer.
Parameters
vars A CellVariable object.
Creates a Matplotlib2DViewer.
Parameters
vars a CellVariable object.
title displayed at the top of the Viewer window
limits [dict] a (deprecated) alternative to limit keyword arguments
xmin, xmax, ymin, ymax, datamin, datamax displayed range of data. Any limit set to a (de-
fault) value of None will autoscale.
cmap the colormap. Defaults to matplotlib.cm.jet
colorbar plot a colorbar in specified orientation if not None
axes if not None, vars will be plotted into this Matplotlib Axes object
figaspect desired aspect ratio of figure. If arg is a number, use that aspect ratio. If arg is ‘auto’,
the aspect ratio will be determined from the Variable’s mesh.
class fipy.viewers.matplotlibViewer.Matplotlib2DViewer(vars, title=None, lim-
its={}, cmap=None, color-
bar=’vertical’, axes=None,
figaspect=’auto’, **kwlimits)
Bases: fipy.viewers.matplotlibViewer.matplotlib2DViewer.AbstractMatplotlib2DViewer
Displays a contour plot of a 2D CellVariable object.
The Matplotlib2DViewer plots a 2D CellVariable using Matplotlib.
>>> from fipy import *
>>> mesh = (Grid2D(nx=5, ny=10, dx=0.1, dy=0.1)
... + (Tri2D(nx=5, ny=5, dx=0.1, dy=0.1)
... + ((0.5,), (0.2,))))
>>> x, y = mesh.cellCenters
>>> xyVar = CellVariable(mesh=mesh, name="x y", value=x * y)
>>> k = Variable(name="k", value=0.)
>>> viewer = Matplotlib2DViewer(vars=numerix.sin(k * xyVar),
... limits={'ymin': 0.1, 'ymax': 0.9},
... datamin=-0.9, datamax=2.0,
... title="Matplotlib2DViewer test")
>>> for kval in range(10):
... k.setValue(kval)
... viewer.plot()
>>> viewer._promptForOpinion()
Creates a Matplotlib2DViewer.
Parameters
vars a CellVariable object.
title displayed at the top of the Viewer window
limits [dict] a (deprecated) alternative to limit keyword arguments
cmap the colormap. Defaults to matplotlib.cm.jet
xmin, xmax, ymin, ymax, datamin, datamax displayed range of data. Any limit set to a (de-
fault) value of None will autoscale.
colorbar plot a colorbar in specified orientation if not None
axes if not None, vars will be plotted into this Matplotlib Axes object
figaspect desired aspect ratio of figure. If arg is a number, use that aspect ratio. If arg is ‘auto’,
the aspect ratio will be determined from the Variable’s mesh.
class fipy.viewers.matplotlibViewer.MatplotlibVectorViewer(vars, title=None,
scale=None, spar-
sity=None, log=False,
limits={}, axes=None, fi-
gaspect=’auto’, **kwlim-
its)
Bases: fipy.viewers.matplotlibViewer.matplotlib2DViewer.AbstractMatplotlib2DViewer
Displays a vector plot of a 2D rank-1 CellVariable or FaceVariable object using Matplotlib
>>> from fipy import *
>>> mesh = Grid2D(nx=50, ny=100, dx=0.1, dy=0.01)
>>> x, y = mesh.cellCenters
>>> xyVar = CellVariable(mesh=mesh, name="x y", value=x * y)
>>> k = Variable(name="k", value=1.)
>>> viewer = MatplotlibVectorViewer(vars=numerix.sin(k * xyVar).grad,
... title="MatplotlibVectorViewer test")
>>> for kval in numerix.arange(1, 10):
... k.setValue(kval)
... viewer.plot()
>>> viewer._promptForOpinion()
Creates a Matplotlib2DViewer.
Parameters
vars a rank-1 CellVariable or FaceVariable object.
title displayed at the top of the Viewer window
scale if not None, scale all arrow lengths by this value
sparsity if not None, then this number of arrows will be randomly chosen (weighted by the cell
volume or face area)
log if True, arrow length goes at the base-10 logarithm of the magnitude
limits [dict] a (deprecated) alternative to limit keyword arguments
xmin, xmax, ymin, ymax, datamin, datamax displayed range of data. Any limit set to a (de-
fault) value of None will autoscale.
axes if not None, vars will be plotted into this Matplotlib Axes object
figaspect desired aspect ratio of figure. If arg is a number, use that aspect ratio. If arg is ‘auto’,
the aspect ratio will be determined from the Variable’s mesh.
quiver(sparsity=None, scale=None)
class fipy.viewers.matplotlibViewer.MatplotlibStreamViewer(vars, title=None,
log=False, lim-
its={}, axes=None,
figaspect=’auto’, den-
sity=1, linewidth=None,
color=None,
cmap=None,
norm=None, arrow-
size=1, arrowstyle=’-|>’,
minlength=0.1, **kwlim-
its)
Bases: fipy.viewers.matplotlibViewer.matplotlib2DViewer.AbstractMatplotlib2DViewer
Displays a stream plot of a 2D rank-1 CellVariable or FaceVariable object using Matplotlib
One issue is that this Viewer relies on scipy.interpolate.griddata, which interpolates on the convex hull of the
data. The results is that streams are plotted across any concavities in the mesh.
Another issue is that it does not seem possible to remove the streams without calling cla(), which means that
different set of streams cannot be overlaid.
>>> from fipy import *
>>> mesh = Grid2D(nx=50, ny=100, dx=0.1, dy=0.01)
>>> x, y = mesh.cellCenters
>>> xyVar = CellVariable(mesh=mesh, name="x y", value=x * y)
>>> k = Variable(name="k", value=1.)
>>> viewer = MatplotlibStreamViewer(vars=numerix.sin(k * xyVar).grad,
... title="MatplotlibStreamViewer test")
>>> for kval in numerix.arange(1, 10):
... k.setValue(kval)
... viewer.plot()
>>> viewer._promptForOpinion()
... k.setValue(kval)
... viewer.plot()
>>> viewer._promptForOpinion()
Creates a MatplotlibStreamViewer.
Parameters
vars a rank-1 CellVariable or FaceVariable object.
title displayed at the top of the Viewer window
log if True, arrow length goes at the base-10 logarithm of the magnitude
limits [dict] a (deprecated) alternative to limit keyword arguments
xmin, xmax, ymin, ymax, datamin, datamax displayed range of data. Any limit set to a (de-
fault) value of None will autoscale.
axes if not None, vars will be plotted into this Matplotlib Axes object
figaspect desired aspect ratio of figure. If arg is a number, use that aspect ratio. If arg is ‘auto’,
the aspect ratio will be determined from the Variable’s mesh.
density [float or 2-tuple] Controls the closeness of streamlines. When density = 1, the domain
is divided into a 25x25 grid—density linearly scales this grid. Each cell in the grid can have,
at most, one traversing streamline. For different densities in each direction, use [density_x,
density_y].
linewidth [Numeric or rank-0 MeshVariable] vary linewidth when given a CellVariable or Face-
Variable of same type as vars.
color [matplotlib color code, or rank-0 MeshVariable] Streamline color. When given an array
with the type as vars, color values are converted to colors using cmap.
cmap [Colormap] Colormap used to plot streamlines and arrows. Only necessary when using
an MeshVariable input for color.
norm [Normalize] Normalize object used to scale luminance data to 0, 1. If None, stretch
(min, max) to (0, 1). Only necessary when color is an MeshVariable.
arrowsize [float] Factor scale arrow size.
arrowstyle [str] Arrow style specification. See FancyArrowPatch.
minlength [float] Minimum length of streamline in axes coordinates.
Submodules
fipy.viewers.mayaviViewer.mayaviClient module
Create a MayaviClient.
Parameters
vars a CellVariable or tuple of CellVariable objects to plot
title displayed at the top of the Viewer window
xmin, xmax, ymin, ymax, zmin, zmax, datamin, datamax displayed range of data. A 1D
Viewer will only use xmin and xmax, a 2D viewer will also use ymin and ymax, and so
on. All viewers will use datamin and datamax. Any limit set to a (default) value of None
will autoscale.
daemon_file the path to the script to run the separate MayaVi viewer process. Defaults to
“fipy/viewers/mayaviViewer/mayaviDaemon.py”
fps frames per second to attempt to display
plot(filename=None)
fipy.viewers.mayaviViewer.mayaviDaemon module
fipy.viewers.mayaviViewer.test module
Module contents
Create a MayaviClient.
Parameters
vars a CellVariable or tuple of CellVariable objects to plot
title displayed at the top of the Viewer window
xmin, xmax, ymin, ymax, zmin, zmax, datamin, datamax displayed range of data. A 1D
Viewer will only use xmin and xmax, a 2D viewer will also use ymin and ymax, and so
on. All viewers will use datamin and datamax. Any limit set to a (default) value of None
will autoscale.
daemon_file the path to the script to run the separate MayaVi viewer process. Defaults to
“fipy/viewers/mayaviViewer/mayaviDaemon.py”
fps frames per second to attempt to display
plot(filename=None)
Submodules
fipy.viewers.vtkViewer.test module
fipy.viewers.vtkViewer.vtkCellViewer module
fipy.viewers.vtkViewer.vtkFaceViewer module
fipy.viewers.vtkViewer.vtkViewer module
Module contents
xmin, xmax, ymin, ymax, zmin, zmax, datamin, datamax displayed range of data. Any limit
set to a (default) value of None will autoscale.
28.2 Submodules
class fipy.viewers.multiViewer.MultiViewer(viewers)
Bases: fipy.viewers.viewer.AbstractViewer
Treat a collection of different viewers (such for different 2D plots or 1D plots with different axes) as a single
viewer that will plot() all subviewers simultaneously.
Parameters
viewers [list] the viewers to bind together
getViewers(*args, **kwds)
Deprecated since version 3.0: use the viewers property instead
plot()
setLimits(limits={}, **kwlimits)
Creates a TSVViewer.
Any cell centers that lie outside the limits provided will not be included. Any values that lie outside the datamin
or datamax will be replaced with nan.
All variables must have the same mesh.
It tries to do something reasonable with rank-1 CellVariable and FaceVariable objects.
Parameters
vars a CellVariable, a FaceVariable, a tuple of CellVariable objects, or a tuple of FaceVariable
objects to plot
title displayed at the top of the Viewer window
limits [dict] a (deprecated) alternative to limit keyword arguments
xmin, xmax, ymin, ymax, zmin, zmax, datamin, datamax displayed range of data. Any limit
set to a (default) value of None will autoscale.
plot(filename=None)
“plot” the coordinates and values of the variables to filename. If filename is not provided, “plots” to stdout.
>>> from fipy.meshes import Grid1D
>>> m = Grid1D(nx = 3, dx = 0.4)
>>> from fipy.variables.cellVariable import CellVariable
>>> v = CellVariable(mesh = m, name = "var", value = (0, 2, 5))
>>> TSVViewer(vars = (v, v.grad)).plot()
x var var_gauss_grad_x
0.2 0 2.5
0.6 2 6.25
1 5 3.75
Parameters
filename If not None, the name of a file to save the image into.
>>> pylab.ion()
>>> fig = pylab.figure()
... axes=ax1,
... legend=None)
>>> viewer._promptForOpinion()
Parameters
vars a CellVariable or tuple of CellVariable objects to plot
title displayed at the top of the Viewer window
xlog log scaling of x axis if True
log
logarithmic data scaling
class fipy.viewers.Matplotlib2DGridViewer(vars, title=None, limits={}, cmap=None, col-
orbar=’vertical’, axes=None, figaspect=’auto’,
**kwlimits)
Bases: fipy.viewers.matplotlibViewer.matplotlib2DViewer.AbstractMatplotlib2DViewer
Displays an image plot of a 2D CellVariable object using Matplotlib.
>>> from fipy import *
>>> mesh = Grid2D(nx=50, ny=100, dx=0.1, dy=0.01)
>>> x, y = mesh.cellCenters
>>> xyVar = CellVariable(mesh=mesh, name="x y", value=x * y)
>>> k = Variable(name="k", value=0.)
>>> viewer = Matplotlib2DGridViewer(vars=numerix.sin(k * xyVar),
... limits={'ymin': 0.1, 'ymax': 0.9},
... datamin=-0.9, datamax=2.0,
... title="Matplotlib2DGridViewer test")
>>> for kval in range(10):
... k.setValue(kval)
... viewer.plot()
>>> viewer._promptForOpinion()
Creates a Matplotlib2DGridViewer.
Parameters
vars A CellVariable object.
title displayed at the top of the Viewer window
limits [dict] a (deprecated) alternative to limit keyword arguments
cmap The colormap. Defaults to matplotlib.cm.jet
xmin, xmax, ymin, ymax, datamin, datamax displayed range of data. Any limit set to a (de-
fault) value of None will autoscale.
colorbar plot a colorbar in specified orientation if not None
axes if not None, vars will be plotted into this Matplotlib Axes object
figaspect desired aspect ratio of figure. If arg is a number, use that aspect ratio. If arg is ‘auto’,
the aspect ratio will be determined from the Variable’s mesh.
class fipy.viewers.Matplotlib2DGridContourViewer(vars, title=None, limits={}, cmap=None,
colorbar=’vertical’, axes=None, fi-
gaspect=’auto’, **kwlimits)
Bases: fipy.viewers.matplotlibViewer.matplotlib2DViewer.AbstractMatplotlib2DViewer
Displays a contour plot of a 2D CellVariable object.
The Matplotlib2DGridContourViewer plots a 2D CellVariable using Matplotlib.
Creates a Matplotlib2DViewer.
Parameters
vars a CellVariable object.
title displayed at the top of the Viewer window
limits [dict] a (deprecated) alternative to limit keyword arguments
xmin, xmax, ymin, ymax, datamin, datamax displayed range of data. Any limit set to a (de-
fault) value of None will autoscale.
cmap the colormap. Defaults to matplotlib.cm.jet
colorbar plot a colorbar in specified orientation if not None
axes if not None, vars will be plotted into this Matplotlib Axes object
figaspect desired aspect ratio of figure. If arg is a number, use that aspect ratio. If arg is ‘auto’,
the aspect ratio will be determined from the Variable’s mesh.
class fipy.viewers.Matplotlib2DViewer(vars, title=None, limits={}, cmap=None, color-
bar=’vertical’, axes=None, figaspect=’auto’, **kwlim-
its)
Bases: fipy.viewers.matplotlibViewer.matplotlib2DViewer.AbstractMatplotlib2DViewer
Displays a contour plot of a 2D CellVariable object.
The Matplotlib2DViewer plots a 2D CellVariable using Matplotlib.
>>> from fipy import *
>>> mesh = (Grid2D(nx=5, ny=10, dx=0.1, dy=0.1)
... + (Tri2D(nx=5, ny=5, dx=0.1, dy=0.1)
... + ((0.5,), (0.2,))))
>>> x, y = mesh.cellCenters
>>> xyVar = CellVariable(mesh=mesh, name="x y", value=x * y)
>>> k = Variable(name="k", value=0.)
>>> viewer = Matplotlib2DViewer(vars=numerix.sin(k * xyVar),
... limits={'ymin': 0.1, 'ymax': 0.9},
... datamin=-0.9, datamax=2.0,
... title="Matplotlib2DViewer test")
>>> for kval in range(10):
... k.setValue(kval)
... viewer.plot()
>>> viewer._promptForOpinion()
Creates a Matplotlib2DViewer.
Parameters
vars a CellVariable object.
title displayed at the top of the Viewer window
limits [dict] a (deprecated) alternative to limit keyword arguments
cmap the colormap. Defaults to matplotlib.cm.jet
xmin, xmax, ymin, ymax, datamin, datamax displayed range of data. Any limit set to a (de-
fault) value of None will autoscale.
colorbar plot a colorbar in specified orientation if not None
axes if not None, vars will be plotted into this Matplotlib Axes object
figaspect desired aspect ratio of figure. If arg is a number, use that aspect ratio. If arg is ‘auto’,
the aspect ratio will be determined from the Variable’s mesh.
class fipy.viewers.MatplotlibVectorViewer(vars, title=None, scale=None, sparsity=None,
log=False, limits={}, axes=None, fi-
gaspect=’auto’, **kwlimits)
Bases: fipy.viewers.matplotlibViewer.matplotlib2DViewer.AbstractMatplotlib2DViewer
Displays a vector plot of a 2D rank-1 CellVariable or FaceVariable object using Matplotlib
>>> from fipy import *
>>> mesh = Grid2D(nx=50, ny=100, dx=0.1, dy=0.01)
>>> x, y = mesh.cellCenters
>>> xyVar = CellVariable(mesh=mesh, name="x y", value=x * y)
>>> k = Variable(name="k", value=1.)
>>> viewer = MatplotlibVectorViewer(vars=numerix.sin(k * xyVar).grad,
... title="MatplotlibVectorViewer test")
>>> for kval in numerix.arange(1, 10):
... k.setValue(kval)
... viewer.plot()
>>> viewer._promptForOpinion()
... viewer.plot()
>>> viewer._promptForOpinion()
Creates a Matplotlib2DViewer.
Parameters
vars a rank-1 CellVariable or FaceVariable object.
title displayed at the top of the Viewer window
scale if not None, scale all arrow lengths by this value
sparsity if not None, then this number of arrows will be randomly chosen (weighted by the cell
volume or face area)
log if True, arrow length goes at the base-10 logarithm of the magnitude
limits [dict] a (deprecated) alternative to limit keyword arguments
xmin, xmax, ymin, ymax, datamin, datamax displayed range of data. Any limit set to a (de-
fault) value of None will autoscale.
axes if not None, vars will be plotted into this Matplotlib Axes object
figaspect desired aspect ratio of figure. If arg is a number, use that aspect ratio. If arg is ‘auto’,
the aspect ratio will be determined from the Variable’s mesh.
quiver(sparsity=None, scale=None)
class fipy.viewers.MatplotlibStreamViewer(vars, title=None, log=False, limits={}, axes=None,
figaspect=’auto’, density=1, linewidth=None,
color=None, cmap=None, norm=None, ar-
rowsize=1, arrowstyle=’-|>’, minlength=0.1,
**kwlimits)
Bases: fipy.viewers.matplotlibViewer.matplotlib2DViewer.AbstractMatplotlib2DViewer
Displays a stream plot of a 2D rank-1 CellVariable or FaceVariable object using Matplotlib
One issue is that this Viewer relies on scipy.interpolate.griddata, which interpolates on the convex hull of the
data. The results is that streams are plotted across any concavities in the mesh.
Another issue is that it does not seem possible to remove the streams without calling cla(), which means that
different set of streams cannot be overlaid.
>>> from fipy import *
>>> mesh = Grid2D(nx=50, ny=100, dx=0.1, dy=0.01)
>>> x, y = mesh.cellCenters
>>> xyVar = CellVariable(mesh=mesh, name="x y", value=x * y)
>>> k = Variable(name="k", value=1.)
>>> viewer = MatplotlibStreamViewer(vars=numerix.sin(k * xyVar).grad,
... title="MatplotlibStreamViewer test")
>>> for kval in numerix.arange(1, 10):
... k.setValue(kval)
... viewer.plot()
>>> viewer._promptForOpinion()
Creates a MatplotlibStreamViewer.
Parameters
vars a rank-1 CellVariable or FaceVariable object.
title displayed at the top of the Viewer window
log if True, arrow length goes at the base-10 logarithm of the magnitude
limits [dict] a (deprecated) alternative to limit keyword arguments
xmin, xmax, ymin, ymax, datamin, datamax displayed range of data. Any limit set to a (de-
fault) value of None will autoscale.
axes if not None, vars will be plotted into this Matplotlib Axes object
figaspect desired aspect ratio of figure. If arg is a number, use that aspect ratio. If arg is ‘auto’,
the aspect ratio will be determined from the Variable’s mesh.
density [float or 2-tuple] Controls the closeness of streamlines. When density = 1, the domain
is divided into a 25x25 grid—density linearly scales this grid. Each cell in the grid can have,
at most, one traversing streamline. For different densities in each direction, use [density_x,
density_y].
linewidth [Numeric or rank-0 MeshVariable] vary linewidth when given a CellVariable or Face-
Variable of same type as vars.
color [matplotlib color code, or rank-0 MeshVariable] Streamline color. When given an array
with the type as vars, color values are converted to colors using cmap.
cmap [Colormap] Colormap used to plot streamlines and arrows. Only necessary when using
an MeshVariable input for color.
norm [Normalize] Normalize object used to scale luminance data to 0, 1. If None, stretch
(min, max) to (0, 1). Only necessary when color is an MeshVariable.
arrowsize [float] Factor scale arrow size.
arrowstyle [str] Arrow style specification. See FancyArrowPatch.
minlength [float] Minimum length of streamline in axes coordinates.
class fipy.viewers.MayaviClient(vars, title=None, daemon_file=None, fps=1.0, **kwlimits)
Bases: fipy.viewers.viewer.AbstractViewer
The MayaviClient uses the Mayavi python plotting package.
>>> from fipy import *
>>> mesh = Grid1D(nx=100)
>>> x, = mesh.cellCenters
>>> xVar = CellVariable(mesh=mesh, name="x", value=x)
>>> k = Variable(name="k", value=0.)
>>> viewer = MayaviClient(vars=(numerix.sin(k * xVar), numerix.cos(k * xVar / numerix.pi)),
... limits={'xmin': 10, 'xmax': 90},
... datamin=-0.9, datamax=2.0,
... title="MayaviClient test")
>>> for kval in numerix.arange(0,0.3,0.03):
... k.setValue(kval)
... viewer.plot()
>>> viewer._promptForOpinion()
>>> x, y, z = mesh.cellCenters
>>> xyzVar = CellVariable(mesh=mesh, name=r"x y z", value=x * y * z)
>>> k = Variable(name="k", value=0.)
>>> viewer = MayaviClient(vars=numerix.sin(k * xyzVar),
... limits={'ymin': 0.1, 'ymax': 0.9},
... datamin=-0.9, datamax=2.0,
... title="MayaviClient test")
>>> for kval in range(10):
... k.setValue(kval)
... viewer.plot()
>>> viewer._promptForOpinion()
Create a MayaviClient.
Parameters
vars a CellVariable or tuple of CellVariable objects to plot
title displayed at the top of the Viewer window
xmin, xmax, ymin, ymax, zmin, zmax, datamin, datamax displayed range of data. A 1D
Viewer will only use xmin and xmax, a 2D viewer will also use ymin and ymax, and so
on. All viewers will use datamin and datamax. Any limit set to a (default) value of None
will autoscale.
daemon_file the path to the script to run the separate MayaVi viewer process. Defaults to
“fipy/viewers/mayaviViewer/mayaviDaemon.py”
fps frames per second to attempt to display
plot(filename=None)
class fipy.viewers.MultiViewer(viewers)
Bases: fipy.viewers.viewer.AbstractViewer
Treat a collection of different viewers (such for different 2D plots or 1D plots with different axes) as a single
viewer that will plot() all subviewers simultaneously.
Parameters
viewers [list] the viewers to bind together
getViewers(*args, **kwds)
Deprecated since version 3.0: use the viewers property instead
plot()
setLimits(limits={}, **kwlimits)
class fipy.viewers.TSVViewer(vars, title=None, limits={}, **kwlimits)
Bases: fipy.viewers.viewer.AbstractViewer
“Views” one or more variables in tab-separated-value format.
Output is a list of coordinates and variable values at each cell center.
File contents will be, e.g.:
title
x y ... var0 var2 ...
0.0 0.0 ... 3.14 1.41 ...
1.0 0.0 ... 2.72 0.866 ...
:
:
Creates a TSVViewer.
Any cell centers that lie outside the limits provided will not be included. Any values that lie outside the datamin
or datamax will be replaced with nan.
All variables must have the same mesh.
It tries to do something reasonable with rank-1 CellVariable and FaceVariable objects.
Parameters
vars a CellVariable, a FaceVariable, a tuple of CellVariable objects, or a tuple of FaceVariable
objects to plot
title displayed at the top of the Viewer window
limits [dict] a (deprecated) alternative to limit keyword arguments
xmin, xmax, ymin, ymax, zmin, zmax, datamin, datamax displayed range of data. Any limit
set to a (default) value of None will autoscale.
plot(filename=None)
“plot” the coordinates and values of the variables to filename. If filename is not provided, “plots” to stdout.
>>> from fipy.meshes import Grid1D
>>> m = Grid1D(nx = 3, dx = 0.4)
>>> from fipy.variables.cellVariable import CellVariable
>>> v = CellVariable(mesh = m, name = "var", value = (0, 2, 5))
>>> TSVViewer(vars = (v, v.grad)).plot()
x var var_gauss_grad_x
0.2 0 2.5
0.6 2 6.25
1 5 3.75
Parameters
filename If not None, the name of a file to save the image into.
xmin, xmax, ymin, ymax, zmin, zmax, datamin, datamax displayed range of data. A 1D
Viewer will only use xmin and xmax, a 2D viewer will also use ymin and ymax, and so
on. All viewers will use datamin and datamax. Any limit set to a (default) value of None
will autoscale.
class fipy.viewers.VTKCellViewer(vars, title=None, limits={}, **kwlimits)
Bases: fipy.viewers.vtkViewer.vtkViewer.VTKViewer
Renders CellVariable data in VTK format
Creates a VTKViewer
Parameters
vars a _MeshVariable or a tuple of them
title displayed at the top of the Viewer window
limits [dict] a (deprecated) alternative to limit keyword arguments
xmin, xmax, ymin, ymax, zmin, zmax, datamin, datamax displayed range of data. Any limit
set to a (default) value of None will autoscale.
class fipy.viewers.VTKFaceViewer(vars, title=None, limits={}, **kwlimits)
Bases: fipy.viewers.vtkViewer.vtkViewer.VTKViewer
Renders _MeshVariable data in VTK format
Creates a VTKViewer
Parameters
vars a _MeshVariable or a tuple of them
title displayed at the top of the Viewer window
limits [dict] a (deprecated) alternative to limit keyword arguments
xmin, xmax, ymin, ymax, zmin, zmax, datamin, datamax displayed range of data. Any limit
set to a (default) value of None will autoscale.
exception fipy.viewers.MeshDimensionError
Bases: exceptions.IndexError
class fipy.viewers.DummyViewer(vars, title=None, **kwlimits)
Bases: fipy.viewers.viewer.AbstractViewer
Create a AbstractViewer object.
Parameters
vars a CellVariable or tuple of CellVariable objects to plot
title displayed at the top of the Viewer window
xmin, xmax, ymin, ymax, zmin, zmax, datamin, datamax displayed range of data. A 1D
Viewer will only use xmin and xmax, a 2D viewer will also use ymin and ymax, and so
on. All viewers will use datamin and datamax. Any limit set to a (default) value of None
will autoscale.
plot(filename=None)
fipy.viewers.Viewer(vars, title=None, limits={}, FIPY_VIEWER=None, **kwlimits)
Generic function for creating a Viewer.
The Viewer factory will search the module tree and return an instance of the first Viewer it finds that supports the
dimensions of vars. Setting the ‘FIPY_VIEWER‘ environment variable to either ‘gist‘, ‘gnuplot‘, ‘matplotlib‘,
‘tsv‘, or ‘vtk‘ will specify the viewer.
The kwlimits or limits parameters can be used to constrain the view. For example:
Viewer(vars=some1Dvar, xmin=0.5, xmax=None, datamax=3)
or:
Viewer(vars=some1Dvar,
limits={'xmin': 0.5, 'xmax': None, 'datamax': 3})
will return a viewer that displays a line plot from an x value of 0.5 up to the largest x value in the dataset. The
data values will be truncated at an upper value of 3, but will have no lower limit.
Parameters
vars a CellVariable or tuple of CellVariable objects to plot
title displayed at the top of the Viewer window
limits [dict] a (deprecated) alternative to limit keyword arguments
FIPY_VIEWER a specific viewer to attempt (possibly multiple times for multiple variables)
xmin, xmax, ymin, ymax, zmin, zmax, datamin, datamax displayed range of data. A 1D
Viewer will only use xmin and xmax, a 2D viewer will also use ymin and ymax, and so
on. All viewers will use datamin and datamax. Any limit set to a (default) value of None
will autoscale.
423
FiPy Manual, Release 3.1.3
[17] K. R Elder, K Thornton, and J. J Hoyt. The kirkendall effect in the phase field crystal model. Philosophical
Magagazine, 91(1):151–164, Jan 2011. doi:10.1080/14786435.2010.506427.
[18] S. V. Patankar. Numerical Heat Transfer and Fluid Flow. Taylor and Francis, 1980.
[19] H. K. Versteeg and W. Malalasekera. An Introduction to Computational Fluid Dynamics. Longman Scientific and
Technical, 1995.
[20] C. Mattiussi. An analysis of finite volume, finite element, and finite difference methods using
some concepts from algebraic topology. Journal of Computational Physics, 133:289–309, 1997. URL:
http://lis.epfl.ch/publications/JCP1997.pdf.
[21] J. A. Sethian. Level Set Methods and Fast Marching Methods. Cambridge University Press, 1996.
[22] William H. Press, Saul A. Teukolsky, William T. Vetterling, and Brian P. Flannery. Numerical Recipes in C: the
Art of Scientific Computing. Cambridge University Press, 2nd edition, 1999.
[23] D. Wheeler, D. Josell, and T. P. Moffat. Modeling superconformal electrodeposition using the level set method.
Journal of The Electrochemical Society, 150(5):C302–C310, 2003. doi:10.1149/1.1562598.
[24] D. Josell, D. Wheeler, and T. P. Moffat. Gold superfill in submicrometer trenches: experiment and prediction.
Journal of The Electrochemical Society, 153(1):C11–C18, 2006. doi:10.1149/1.2128765.
[25] T. P. Moffat, D. Wheeler, S. K. Kim, and D. Josell. Curvature enhanced adsorbate coverage model for electrode-
position. Journal of The Electrochemical Society, 153(2):C127–C132, 2006. doi:10.1149/1.2165580.
[26] A. A. Wheeler, W. J. Boettinger, and G. B. McFadden. Phase-field model for isothermal phase transitions in
binary alloys. Physical Review A, 45(10):7424–7439, 1992.
[27] J. A. Warren and W. J. Boettinger. Prediction of dendritic growth and microsegregation in a binary alloy using
the phase field method. Acta Metallurgica et Materialia, 43(2):689–703, 1995.
[28] J. E. Guyer, W. J. Boettinger, J. A. Warren, and G. B. McFadden. Phase field modeling of electrochemistry I:
Equilibrium. Physical Review E, 69:021603, 2004. arXiv:cond-mat/0308173, doi:10.1103/PhysRevE.69.021603.
[29] J. E. Guyer, W. J. Boettinger, J. A. Warren, and G. B. McFadden. Phase field modeling of electrochemistry II:
Kinetics. Physical Review E, 69:021604, 2004. arXiv:cond-mat/0308179, doi:10.1103/PhysRevE.69.021604.
[30] Guido van Rossum. Python Reference Manual. URL: http://docs.python.org/ref/.
[31] Ben Collins-Sussman, Brian W. Fitzpatrick, and C. Michael Pilato. Version Control with Subversion. O’Reilly
Media, 2004. URL: http://svnbook.red-bean.com.
[32] T. P. Moffat, D. Wheeler, and D. Josell. Superfilling and the curvature enhanced acceler-
ator coverage mechanism. The Electrochemical Society, Interface, 13(4):46–52, 2004. URL:
http://www.electrochem.org/publications/interface/winter2004/IF12-04-Pg46.pdf.
[33] J. H. Ferziger and M. Perić. Computational Methods for Fluid Dynamics. Springer, 1996.
424 Bibliography
Python Module Index
e 173
examples.cahnHilliard.mesh2DCoupled, 161 examples.updating.update0_1to1_0, 184
examples.cahnHilliard.sphere, 164 examples.updating.update1_0to2_0, 180
examples.convection.exponential1D.mesh1D,examples.updating.update2_0to3_0, 179
89
f
examples.convection.exponential1DSource.mesh1D,
90 fipy.boundaryConditions, 196
examples.convection.robin, 91 fipy.boundaryConditions.boundaryCondition,
examples.convection.source, 93 195
examples.diffusion.anisotropy, 87 fipy.boundaryConditions.constraint, 195
examples.diffusion.circle, 76 fipy.boundaryConditions.fixedFlux, 195
examples.diffusion.coupled, 72 fipy.boundaryConditions.fixedValue, 196
examples.diffusion.electrostatics, 81 fipy.boundaryConditions.nthOrderBoundaryCondition,
examples.diffusion.mesh1D, 55 196
examples.diffusion.mesh20x20, 74 fipy.boundaryConditions.test, 196
fipy.matrices, 199
examples.diffusion.nthOrder.input4thOrder1D,
85 fipy.matrices.offsetSparseMatrix, 199
examples.flow.stokesCavity, 167 fipy.matrices.pysparseMatrix, 199
examples.levelSet.advection.circle, 142 fipy.matrices.scipyMatrix, 199
examples.levelSet.advection.mesh1D, 141 fipy.matrices.sparseMatrix, 199
fipy.matrices.test, 199
examples.levelSet.distanceFunction.circle,
140 fipy.meshes, 223
fipy.meshes.abstractMesh, 203
examples.levelSet.distanceFunction.mesh1D,
139 fipy.meshes.builders, 201
examples.levelSet.electroChem.gold, 148 fipy.meshes.builders.abstractGridBuilder,
examples.levelSet.electroChem.howToWriteAScript,201
153 fipy.meshes.builders.grid1DBuilder, 201
examples.levelSet.electroChem.leveler, fipy.meshes.builders.grid2DBuilder, 201
149 fipy.meshes.builders.grid3DBuilder, 201
fipy.meshes.builders.periodicGrid1DBuilder,
examples.levelSet.electroChem.simpleTrenchSystem,
145 201
examples.phase.anisotropy, 118 fipy.meshes.builders.utilityClasses, 201
examples.phase.binaryCoupled, 103 fipy.meshes.cylindricalNonUniformGrid1D,
examples.phase.impingement.mesh20x20, 207
124 fipy.meshes.cylindricalNonUniformGrid2D,
examples.phase.impingement.mesh40x1, 121 208
examples.phase.polyxtal, 127 fipy.meshes.cylindricalUniformGrid1D,
examples.phase.polyxtalCoupled, 133 208
examples.phase.quaternary, 112 fipy.meshes.cylindricalUniformGrid2D,
examples.phase.simple, 95 209
examples.reactiveWetting.liquidVapor1D, fipy.meshes.factoryMeshes, 209
425
FiPy Manual, Release 3.1.3
429
FiPy Manual, Release 3.1.3
arcsin() (fipy.tools.dimensions.physicalField.PhysicalField C
method), 294 cacheMatrix, 169
arcsin() (fipy.tools.PhysicalField method), 318 cacheMatrix() (fipy.terms.term.Term method), 263
arcsin() (fipy.variables.Variable method), 358 cacheMe() (fipy.variables.Variable method), 358
arcsin() (fipy.variables.variable.Variable method), 352 cacheMe() (fipy.variables.variable.Variable method), 352
arcsinh() (fipy.variables.Variable method), 358 cacheRHSvector, 169
arcsinh() (fipy.variables.variable.Variable method), 352 cacheRHSvector() (fipy.terms.term.Term method), 263
arctan, 119 calcDistanceFunction() (fipy.variables.DistanceVariable
arctan() (fipy.tools.dimensions.physicalField.PhysicalField method), 382
method), 295 calcDistanceFunction() (fipy.variables.distanceVariable.DistanceVariable
arctan() (fipy.tools.PhysicalField method), 318 method), 337
arctan() (fipy.variables.Variable method), 358 ceil() (fipy.tools.dimensions.physicalField.PhysicalField
arctan() (fipy.variables.variable.Variable method), 352 method), 295
arctan2, 119 ceil() (fipy.tools.PhysicalField method), 319
arctan2() (fipy.tools.dimensions.physicalField.PhysicalField ceil() (fipy.variables.Variable method), 358
method), 295 ceil() (fipy.variables.variable.Variable method), 352
arctan2() (fipy.tools.PhysicalField method), 318 cellCenters (fipy.meshes.abstractMesh.AbstractMesh at-
arctan2() (fipy.variables.Variable method), 358 tribute), 203
arctan2() (fipy.variables.variable.Variable method), 352 cellCenters (fipy.meshes.PeriodicGrid1D attribute), 225
arctanh() (fipy.tools.dimensions.physicalField.PhysicalField cellCenters (fipy.meshes.periodicGrid1D.PeriodicGrid1D
method), 295 attribute), 219
arctanh() (fipy.tools.PhysicalField method), 319 cellDistanceVectors (fipy.meshes.abstractMesh.AbstractMesh
arctanh() (fipy.variables.Variable method), 358 attribute), 203
arctanh() (fipy.variables.variable.Variable method), 352 cellFaceIDs (fipy.meshes.abstractMesh.AbstractMesh at-
arithmeticFaceValue (fipy.variables.CellVariable at- tribute), 203
tribute), 364 cellInterfaceAreas (fipy.variables.DistanceVariable
arithmeticFaceValue (fipy.variables.cellVariable.CellVariable attribute), 382
attribute), 329 cellInterfaceAreas (fipy.variables.distanceVariable.DistanceVariable
arithmeticFaceValue (fipy.variables.ModularVariable at- attribute), 337
tribute), 370 CellTerm (class in fipy.terms.cellTerm), 254
arithmeticFaceValue (fipy.variables.modularVariable.ModularVariable
cellToFaceDistanceVectors
attribute), 346 (fipy.meshes.abstractMesh.AbstractMesh
array, 108 attribute), 203
aspect2D (fipy.meshes.abstractMesh.AbstractMesh at- CellVariable, 86, 91, 96, 104, 112, 122, 125, 155, 168,
tribute), 203 185, 187
CellVariable (class in fipy.variables), 363
B CellVariable (class in fipy.variables.cellVariable), 329
Barrier() (fipy.tools.comms.commWrapper.CommWrapper cellVolumeAverage (fipy.variables.CellVariable at-
method), 289 tribute), 364
Barrier() (fipy.tools.comms.dummyComm.DummyComm cellVolumeAverage (fipy.variables.cellVariable.CellVariable
method), 290 attribute), 330
Base (class in package.subpackage.base), 191 cellVolumes (fipy.meshes.abstractMesh.AbstractMesh at-
bcast() (fipy.tools.comms.commWrapper.CommWrapper tribute), 203
method), 289 cellVolumes (fipy.meshes.cylindricalUniformGrid1D.CylindricalUniformGr
bcast() (fipy.tools.comms.mpi4pyCommWrapper.Mpi4pyCommWrapper attribute), 208
method), 290 cellVolumes (fipy.meshes.cylindricalUniformGrid2D.CylindricalUniformGr
BetaNoiseVariable (class in fipy.variables), 370 attribute), 209
BetaNoiseVariable (class in CentralDifferenceConvectionTerm (class in fipy.terms),
fipy.variables.betaNoiseVariable), 327 274
BoundaryCondition (class in CentralDifferenceConvectionTerm (class in
fipy.boundaryConditions.boundaryCondition), fipy.terms.centralDiffConvectionTerm), 254
195 command line option
Buildbot, 49 –inline, 20
–lsmlib, 21
430 Index
FiPy Manual, Release 3.1.3
Index 431
FiPy Manual, Release 3.1.3
432 Index
FiPy Manual, Release 3.1.3
Index 433
FiPy Manual, Release 3.1.3
434 Index
FiPy Manual, Release 3.1.3
Index 435
FiPy Manual, Release 3.1.3
436 Index
FiPy Manual, Release 3.1.3
Index 437
FiPy Manual, Release 3.1.3
438 Index
FiPy Manual, Release 3.1.3
isCompatible() (fipy.tools.dimensions.physicalField.PhysicalField
LinearCGSSolver (class in fipy.solvers.scipy), 239
method), 299 LinearCGSSolver (class in
isCompatible() (fipy.tools.dimensions.physicalField.PhysicalUnit fipy.solvers.scipy.linearCGSSolver), 238
method), 304 LinearGMRESSolver (class in fipy.solvers), 244
isCompatible() (fipy.tools.PhysicalField method), 322 LinearGMRESSolver (class in fipy.solvers.pysparse), 237
isDimensionless() (fipy.tools.dimensions.physicalField.PhysicalUnit
LinearGMRESSolver (class in
method), 304 fipy.solvers.pysparse.linearGMRESSolver),
isDimensionlessOrAngle() 235
(fipy.tools.dimensions.physicalField.PhysicalUnit LinearGMRESSolver (class in fipy.solvers.scipy), 240
method), 304 LinearGMRESSolver (class in
isFloat() (in module fipy.tools.numerix), 313 fipy.solvers.scipy.linearGMRESSolver), 238
isInt() (in module fipy.tools.numerix), 313 LinearJORSolver (class in fipy.solvers), 244
isInverseAngle() (fipy.tools.dimensions.physicalField.PhysicalUnit
LinearJORSolver (class in fipy.solvers.pysparse), 237
method), 304 LinearJORSolver (class in
itemset() (fipy.tools.dimensions.physicalField.PhysicalField fipy.solvers.pysparse.linearJORSolver), 235
method), 299 LinearLUSolver, 109, 185
itemset() (fipy.tools.PhysicalField method), 322 LinearLUSolver (class in fipy.solvers), 244
itemset() (fipy.variables.Variable method), 361 LinearLUSolver (class in fipy.solvers.pysparse), 237
itemset() (fipy.variables.variable.Variable method), 354 LinearLUSolver (class in
itemsize (fipy.tools.dimensions.physicalField.PhysicalField fipy.solvers.pysparse.linearLUSolver), 235
attribute), 299 LinearLUSolver (class in fipy.solvers.scipy), 240
itemsize (fipy.tools.PhysicalField attribute), 322 LinearLUSolver (class in
itemsize (fipy.variables.Variable attribute), 361 fipy.solvers.scipy.linearLUSolver), 239
itemsize (fipy.variables.variable.Variable attribute), 354 LinearPCGSolver (class in fipy.solvers), 244
Iterator, 185 LinearPCGSolver (class in fipy.solvers.pysparse), 236
LinearPCGSolver (class in
J fipy.solvers.pysparse.linearPCGSolver), 236
JacobiPreconditioner (class in fipy.solvers), 245 LinearPCGSolver (class in fipy.solvers.scipy), 240
JacobiPreconditioner (class in fipy.solvers.pysparse), 237 LinearPCGSolver (class in
JacobiPreconditioner (class in fipy.solvers.scipy.linearPCGSolver), 239
fipy.solvers.pysparse.preconditioners), 234 LINFerror() (in module fipy.steppers), 248
JacobiPreconditioner (class in LINFnorm() (in module fipy.tools.numerix), 313
loadtxt, 124, 126, 159
fipy.solvers.pysparse.preconditioners.jacobiPreconditioner),
234 log, 108, 114
justErrorVector() (fipy.terms.term.Term method), 263 log (fipy.viewers.Matplotlib1DViewer attribute), 413
justResidualVector() (fipy.terms.term.Term method), 264 log (fipy.viewers.matplotlibViewer.Matplotlib1DViewer
attribute), 397
L log (fipy.viewers.matplotlibViewer.matplotlib1DViewer.Matplotlib1DViewe
L1error() (in module fipy.steppers), 248 attribute), 387
L1norm() (in module fipy.tools.numerix), 313 log (fipy.viewers.matplotlibViewer.matplotlibViewer.AbstractMatplotlibView
L2error() (in module fipy.steppers), 248 attribute), 395
L2norm() (in module fipy.tools.numerix), 313 log() (fipy.tools.dimensions.physicalField.PhysicalField
LD_LIBRARY_PATH, 15 method), 299
leastSquaresGrad (fipy.variables.CellVariable attribute), log() (fipy.tools.PhysicalField method), 322
367 log() (fipy.variables.Variable method), 361
leastSquaresGrad (fipy.variables.cellVariable.CellVariable log() (fipy.variables.variable.Variable method), 354
attribute), 332 log10() (fipy.tools.dimensions.physicalField.PhysicalField
LinearBicgstabSolver (class in fipy.solvers.scipy), 240 method), 299
LinearBicgstabSolver (class in log10() (fipy.tools.PhysicalField method), 323
fipy.solvers.scipy.linearBicgstabSolver), 238 log10() (fipy.variables.Variable method), 361
LinearCGSSolver (class in fipy.solvers), 243 log10() (fipy.variables.variable.Variable method), 354
LinearCGSSolver (class in fipy.solvers.pysparse), 236
LinearCGSSolver (class in M
fipy.solvers.pysparse.linearCGSSolver), 234 mag (fipy.variables.Variable attribute), 361
Index 439
FiPy Manual, Release 3.1.3
440 Index
FiPy Manual, Release 3.1.3
Index 441
FiPy Manual, Release 3.1.3
442 Index
FiPy Manual, Release 3.1.3
Index 443
FiPy Manual, Release 3.1.3
444 Index
FiPy Manual, Release 3.1.3
UpwindConvectionTerm (class in
fipy.terms.upwindConvectionTerm), 266
user_options (fipy.tools.copy_script.Copy_script at-
tribute), 306
user_options (fipy.tools.performance.efficiency_test.Efficiency_test
attribute), 305
V
value (fipy.variables.Variable attribute), 363
value (fipy.variables.variable.Variable attribute), 357
VanLeerConvectionTerm (class in fipy.terms), 280
VanLeerConvectionTerm (class in
fipy.terms.vanLeerConvectionTerm), 268
Variable, 101, 104
Variable (class in fipy.variables), 357
Variable (class in fipy.variables.variable), 350
VectorCoeffError, 269
vertexCoords (fipy.meshes.uniformGrid1D.UniformGrid1D
attribute), 222
vertexCoords (fipy.meshes.uniformGrid2D.UniformGrid2D
attribute), 222
vertexCoords (fipy.meshes.uniformGrid3D.UniformGrid3D
attribute), 223
Viewer() (in module fipy.viewers), 421
Vitals (class in fipy.tools), 326
Vitals (class in fipy.tools.vitals), 316
VTKCellDataSet (fipy.meshes.abstractMesh.AbstractMesh
attribute), 203
VTKCellViewer (class in fipy.viewers), 421
VTKCellViewer (class in fipy.viewers.vtkViewer), 407
VTKCellViewer (class in
fipy.viewers.vtkViewer.vtkCellViewer), 406
VTKFaceDataSet (fipy.meshes.abstractMesh.AbstractMesh
attribute), 203
VTKFaceViewer (class in fipy.viewers), 421
VTKFaceViewer (class in fipy.viewers.vtkViewer), 407
VTKFaceViewer (class in
fipy.viewers.vtkViewer.vtkFaceViewer), 406
VTKViewer (class in fipy.viewers.vtkViewer.vtkViewer),
406
VTKViewer() (in module fipy.viewers), 420
VTKViewer() (in module fipy.viewers.vtkViewer), 407
W
write() (in module fipy.tools.dump), 307
X
x (fipy.meshes.abstractMesh.AbstractMesh attribute), 206
Y
y (fipy.meshes.abstractMesh.AbstractMesh attribute), 206
Z
z (fipy.meshes.abstractMesh.AbstractMesh attribute), 206
Index 445