Merge lp:~jamesmadd/dolfin-adjoint/timestepping into lp:dolfin-adjoint

Proposed by James Maddison
Status: Merged
Approved by: Patrick Farrell
Approved revision: 671
Merged at revision: 661
Proposed branch: lp:~jamesmadd/dolfin-adjoint/timestepping
Merge into: lp:dolfin-adjoint
Diff against target: 19208 lines (+18605/-0)
100 files modified
timestepping/COPYING (+674/-0)
timestepping/COPYING.LESSER (+165/-0)
timestepping/manual/bibliography.bib (+51/-0)
timestepping/manual/examples/TimeFunction (+38/-0)
timestepping/manual/examples/TimeLevel (+49/-0)
timestepping/manual/examples/TimeLevels (+42/-0)
timestepping/manual/examples/add_solve (+52/-0)
timestepping/manual/examples/add_solve_statics (+54/-0)
timestepping/manual/examples/diffusion_fe (+65/-0)
timestepping/manual/examples/diffusion_fe_adjoined (+77/-0)
timestepping/manual/examples/statics (+33/-0)
timestepping/manual/examples/time_discretisations (+267/-0)
timestepping/manual/examples/wrappers (+28/-0)
timestepping/manual/makefile (+24/-0)
timestepping/manual/manual.tex (+1636/-0)
timestepping/python/dolfin_adjoint_timestepping/__init__.py (+51/-0)
timestepping/python/dolfin_adjoint_timestepping/dolfin_adjoint_timestepping.py (+439/-0)
timestepping/python/timestepping/__init__.py (+112/-0)
timestepping/python/timestepping/caches.py (+266/-0)
timestepping/python/timestepping/checkpointing.py (+394/-0)
timestepping/python/timestepping/embedded_cpp.py (+232/-0)
timestepping/python/timestepping/equation_solvers.py (+556/-0)
timestepping/python/timestepping/exceptions.py (+56/-0)
timestepping/python/timestepping/fenics_overrides.py (+261/-0)
timestepping/python/timestepping/fenics_patches.py (+184/-0)
timestepping/python/timestepping/fenics_utils.py (+437/-0)
timestepping/python/timestepping/fenics_versions.py (+156/-0)
timestepping/python/timestepping/parameters.py (+71/-0)
timestepping/python/timestepping/pre_assembled_adjoint.py (+548/-0)
timestepping/python/timestepping/pre_assembled_equations.py (+444/-0)
timestepping/python/timestepping/pre_assembled_forms.py (+397/-0)
timestepping/python/timestepping/quadrature.py (+157/-0)
timestepping/python/timestepping/statics.py (+144/-0)
timestepping/python/timestepping/time_functions.py (+433/-0)
timestepping/python/timestepping/time_levels.py (+346/-0)
timestepping/python/timestepping/time_systems.py (+1978/-0)
timestepping/python/timestepping/versions.py (+134/-0)
timestepping/python/timestepping/vtu_io.py (+342/-0)
timestepping/test (+197/-0)
timestepping/tests/fenics/dolfin_vtkprecision (+68/-0)
timestepping/tests/fenics/ffc_1d_circumradius (+33/-0)
timestepping/tests/long/advection_2d_limiter_rk3 (+288/-0)
timestepping/tests/long/advection_2d_rk3 (+166/-0)
timestepping/tests/long/boussinesq-navier-stokes_cn_picard2 (+415/-0)
timestepping/tests/long/cahn-hilliard_cn_newton (+131/-0)
timestepping/tests/long/data/circle_100.geo (+17/-0)
timestepping/tests/long/data/square_10-200.geo (+21/-0)
timestepping/tests/long/data/square_100.geo (+12/-0)
timestepping/tests/long/lorenz_cn_newton (+175/-0)
timestepping/tests/long/mantle_convection/composition.py (+56/-0)
timestepping/tests/long/mantle_convection/mantle_convection (+204/-0)
timestepping/tests/long/mantle_convection/numerics.py (+60/-0)
timestepping/tests/long/mantle_convection/parameters.py (+105/-0)
timestepping/tests/long/mantle_convection/stokes.py (+57/-0)
timestepping/tests/long/mantle_convection/temperature.py (+88/-0)
timestepping/tests/long/navier-stokes_cn_picard2 (+379/-0)
timestepping/tests/short/advection-diffusion_cn (+89/-0)
timestepping/tests/short/advection_1d_cn (+90/-0)
timestepping/tests/short/advection_1d_cn_da (+104/-0)
timestepping/tests/short/advection_1d_dgt (+127/-0)
timestepping/tests/short/advection_1d_fe (+105/-0)
timestepping/tests/short/advection_1d_rk4 (+124/-0)
timestepping/tests/short/burgers_ab2 (+100/-0)
timestepping/tests/short/burgers_ab2_da (+113/-0)
timestepping/tests/short/burgers_ab3 (+105/-0)
timestepping/tests/short/burgers_be_custom_newton (+113/-0)
timestepping/tests/short/burgers_be_custom_newton_da (+126/-0)
timestepping/tests/short/burgers_be_newton (+105/-0)
timestepping/tests/short/burgers_be_newton_da (+118/-0)
timestepping/tests/short/burgers_be_picard (+136/-0)
timestepping/tests/short/burgers_be_picard_da (+149/-0)
timestepping/tests/short/burgers_fe (+105/-0)
timestepping/tests/short/burgers_fe_da (+118/-0)
timestepping/tests/short/burgers_leapfrog (+102/-0)
timestepping/tests/short/burgers_leapfrog_da (+115/-0)
timestepping/tests/short/burgers_rk2 (+101/-0)
timestepping/tests/short/bve_ab3 (+161/-0)
timestepping/tests/short/bve_rk2 (+104/-0)
timestepping/tests/short/data/square_125.geo (+12/-0)
timestepping/tests/short/elastodynamics (+195/-0)
timestepping/tests/short/fibonacci (+47/-0)
timestepping/tests/short/linearised_shallow_water_cn/kelvin_new.py (+71/-0)
timestepping/tests/short/linearised_shallow_water_cn/shallow_water (+78/-0)
timestepping/tests/short/linearised_shallow_water_cn/sw_lib.py (+283/-0)
timestepping/tests/short/qg_ab3 (+305/-0)
timestepping/tests/short/shm_cn (+106/-0)
timestepping/tests/short/shm_cn_da (+118/-0)
timestepping/tests/short/stokes (+261/-0)
timestepping/tests/unit/add_solve_assignment (+46/-0)
timestepping/tests/unit/assign_ml2 (+50/-0)
timestepping/tests/unit/assign_ml3 (+51/-0)
timestepping/tests/unit/counting (+135/-0)
timestepping/tests/unit/embedded_cpp (+56/-0)
timestepping/tests/unit/incomplete_quadrature (+64/-0)
timestepping/tests/unit/lumped_mass (+61/-0)
timestepping/tests/unit/mass (+84/-0)
timestepping/tests/unit/minimise_functional (+50/-0)
timestepping/tests/unit/non_static_solver (+73/-0)
timestepping/tests/unit/scale (+45/-0)
timestepping/tests/unit/vtu_io (+39/-0)
To merge this branch: bzr merge lp:~jamesmadd/dolfin-adjoint/timestepping
Reviewer Review Type Date Requested Status
Patrick Farrell Pending
Review via email: mp+162989@code.launchpad.net

Description of the change

Timestepping library, with manual, tests, and dolfin-adjoint integration

To post a comment you must log in.
644. By James Maddison <email address hidden>

Corrections to the manual

645. By James Maddison <email address hidden>

Do not delete the checkpoints directory

646. By James Maddison <email address hidden>

Create the checkpoints directory

647. By James Maddison <email address hidden>

Do not delete checkpoint files

648. By James Maddison <email address hidden>

Fix a parallel deadlock when checkpointing to disk

Revision history for this message
Patrick Farrell (pefarrell) wrote :

Hi James!

Sorry for the delay.

Very minor comments. All of these are optional suggestions, feel free to disregard.

a) Can you make your test script edit the PYTHONPATH/sys.path to always use the local copy of timestepping?

b) Is there any way to run the tests in parallel?

c) Actually, more generally, is there a compelling reason not to use the dolfin-adjoint test tool?

d) Is it safe to always put checkpoints in checkpoints~/? That seems a bit strange. Is it safe if more than one process is executing the same code on the same machine at once?

e) Can you make the makefile in the manual delete the .dvi and the .ps? (Is there a reason you don't use pdflatex?)

f) In the manual, can you hypersetup those hideous red boxes away? Maybe:

\definecolor{DarkBlue}{rgb}{0.00,0.00,0.55}
\hypersetup{
    linkcolor = DarkBlue,
    anchorcolor = DarkBlue,
    citecolor = DarkBlue,
    filecolor = DarkBlue,
    pagecolor = DarkBlue,
    urlcolor = DarkBlue,
    colorlinks = true,
}

g) In the manual, you have a "is non-linear then the a non-linear equation .. must be solved." Get rid of the the.

h) In the manual, maybe use the same lstlisting settings as we have in the paper?

i) When I run the tests with the test harness (precise, fenics from PPA), I get

Successes: 51
Failures: 2
Failures:
/data/pfarrell/src/dolfin-adjoint/timestepping/tests/short/burgers_leapfrog_da
/data/pfarrell/src/dolfin-adjoint/timestepping/tests/short/linearised_shallow_water_cn/shallow_water

burgers_leapfrog_da fails with

-4.89248513609469882e+01 2.22399876292911358e-12
Traceback (most recent call last):
  File "./burgers_leapfrog_da", line 114, in <module>
    assert(err < 7.0e-13)
AssertionError

shallow_water fails with

Assembling form with rank 2
4.10821370684035065e-05
4.10821370682097189e-05
1.93787585804627849e-16
8.51321880127267620e-07
8.51321895311346568e-07
1.51840789485756049e-14
Traceback (most recent call last):
  File "./shallow_water", line 73, in <module>
    assert(err < 7.0e-15)
AssertionError

j) How do you feel about refactoring timestepping.py into multiple files .. ?

Revision history for this message
James Maddison (jamesmadd) wrote :

a, e, f, g, h, i, j are all straightforward, so I'll fix these.

Regarding b, c, d:

> d) Is it safe to always put checkpoints in checkpoints~/? That seems a bit
> strange. Is it safe if more than one process is executing the same code on the
> same machine at once?

I'm not actually sure how to handle this one. It's convenient to have the checkpoint files in a single directory, so that it's easy to clean them up afterwards, but it's inconvenient in that you can only run one test in a directory at once. Do you have any suggestions?

649. By James Maddison <email address hidden>

Always pick up local library versions when running the timestepping tests

650. By James Maddison <email address hidden>

Fix link style, lstlisting style, and a typo

651. By James Maddison <email address hidden>

Relax some test tolerances

652. By James Maddison <email address hidden>

Fix bibliography formatting

653. By James Maddison <email address hidden>

Start refactoring the timestepping library

654. By James Maddison <email address hidden>

Fix to test script

655. By James Maddison <email address hidden>

Separate out exception classes

656. By James Maddison <email address hidden>

Separate out C++ embedding

Revision history for this message
Patrick Farrell (pefarrell) wrote :

Maybe you could use the tempfile.mkdtemp function to create a guaranteed-unique temporary directory, and put them in that?

While looking at what dolfin-adjoint does, I see it doesn't do anything better than your timestepping library at the moment ..

657. By James Maddison <email address hidden>

Separate out version code and FEniCS patches

658. By James Maddison <email address hidden>

Separate out VTK I/O

659. By James Maddison <email address hidden>

Separate out FEniCS utilities, quadrature, and time levels

Revision history for this message
James Maddison (jamesmadd) wrote :

> Maybe you could use the tempfile.mkdtemp function to create a guaranteed-
> unique temporary directory, and put them in that?

That might be a problem if a model leaves large amounts of data in /tmp (e.g. due to a crash or termination).

660. By James Maddison <email address hidden>

Separate out time dependent functions

Revision history for this message
James Maddison (jamesmadd) wrote :

c) Actually, more generally, is there a compelling reason not to use the dolfin-adjoint test tool?

Does the tool support parallel tests with MPI?

661. By James Maddison <email address hidden>

Separate out static declarations

662. By James Maddison <email address hidden>

Separate out system solvers and caches

663. By James Maddison <email address hidden>

Separate out pre-assembly

664. By James Maddison <email address hidden>

Final refactoring

Revision history for this message
James Maddison (jamesmadd) wrote :

a, e, f, g, h, i, j are complete. python/timestepping/time_systems.py is still a large source file, but contains four (large) quite closely connected classes.

665. By James Maddison <email address hidden>

Fix circular dependency

666. By James Maddison <email address hidden>

- Give classes in time_systems.py more descriptive names
- More graceful resolution of a namespace clash with dolfin-adjoint
- Make checkpointing less likely to lead to deadlocks in parallel
- Make dependency processing less likely to lead to deadlocks in parallel
- Work around an obscure bug with FEniCS 1.0 and SciPy 0.12.0
- Add is_static method to pre-assembled forms
- Make static bi-linear form pre-assembly more repeatable

667. By James Maddison <email address hidden>

Delete manual test script

668. By James Maddison <email address hidden>

Delete redundant exclusion of 'test'

669. By James Maddison <email address hidden>

Add multi-process support to the test script

Revision history for this message
James Maddison (jamesmadd) wrote :

> b) Is there any way to run the tests in parallel?

This has also been addressed. Run the test script with -n [PROCS] (PROCS = 0 for all system cores).

670. By James Maddison <email address hidden>

Minor bugfix to parallel testing

671. By James Maddison <email address hidden>

Parallel testing optimisation

672. By James R. Maddison <email address hidden>

Add dates of first addition of FEniCS derived code

673. By James R. Maddison <email address hidden>

Documentation and constructor argument fix

674. By James R. Maddison <email address hidden>

Add some code addition dates

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'timestepping'
2=== added file 'timestepping/COPYING'
3--- timestepping/COPYING 1970-01-01 00:00:00 +0000
4+++ timestepping/COPYING 2013-05-15 14:57:23 +0000
5@@ -0,0 +1,674 @@
6+ GNU GENERAL PUBLIC LICENSE
7+ Version 3, 29 June 2007
8+
9+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
10+ Everyone is permitted to copy and distribute verbatim copies
11+ of this license document, but changing it is not allowed.
12+
13+ Preamble
14+
15+ The GNU General Public License is a free, copyleft license for
16+software and other kinds of works.
17+
18+ The licenses for most software and other practical works are designed
19+to take away your freedom to share and change the works. By contrast,
20+the GNU General Public License is intended to guarantee your freedom to
21+share and change all versions of a program--to make sure it remains free
22+software for all its users. We, the Free Software Foundation, use the
23+GNU General Public License for most of our software; it applies also to
24+any other work released this way by its authors. You can apply it to
25+your programs, too.
26+
27+ When we speak of free software, we are referring to freedom, not
28+price. Our General Public Licenses are designed to make sure that you
29+have the freedom to distribute copies of free software (and charge for
30+them if you wish), that you receive source code or can get it if you
31+want it, that you can change the software or use pieces of it in new
32+free programs, and that you know you can do these things.
33+
34+ To protect your rights, we need to prevent others from denying you
35+these rights or asking you to surrender the rights. Therefore, you have
36+certain responsibilities if you distribute copies of the software, or if
37+you modify it: responsibilities to respect the freedom of others.
38+
39+ For example, if you distribute copies of such a program, whether
40+gratis or for a fee, you must pass on to the recipients the same
41+freedoms that you received. You must make sure that they, too, receive
42+or can get the source code. And you must show them these terms so they
43+know their rights.
44+
45+ Developers that use the GNU GPL protect your rights with two steps:
46+(1) assert copyright on the software, and (2) offer you this License
47+giving you legal permission to copy, distribute and/or modify it.
48+
49+ For the developers' and authors' protection, the GPL clearly explains
50+that there is no warranty for this free software. For both users' and
51+authors' sake, the GPL requires that modified versions be marked as
52+changed, so that their problems will not be attributed erroneously to
53+authors of previous versions.
54+
55+ Some devices are designed to deny users access to install or run
56+modified versions of the software inside them, although the manufacturer
57+can do so. This is fundamentally incompatible with the aim of
58+protecting users' freedom to change the software. The systematic
59+pattern of such abuse occurs in the area of products for individuals to
60+use, which is precisely where it is most unacceptable. Therefore, we
61+have designed this version of the GPL to prohibit the practice for those
62+products. If such problems arise substantially in other domains, we
63+stand ready to extend this provision to those domains in future versions
64+of the GPL, as needed to protect the freedom of users.
65+
66+ Finally, every program is threatened constantly by software patents.
67+States should not allow patents to restrict development and use of
68+software on general-purpose computers, but in those that do, we wish to
69+avoid the special danger that patents applied to a free program could
70+make it effectively proprietary. To prevent this, the GPL assures that
71+patents cannot be used to render the program non-free.
72+
73+ The precise terms and conditions for copying, distribution and
74+modification follow.
75+
76+ TERMS AND CONDITIONS
77+
78+ 0. Definitions.
79+
80+ "This License" refers to version 3 of the GNU General Public License.
81+
82+ "Copyright" also means copyright-like laws that apply to other kinds of
83+works, such as semiconductor masks.
84+
85+ "The Program" refers to any copyrightable work licensed under this
86+License. Each licensee is addressed as "you". "Licensees" and
87+"recipients" may be individuals or organizations.
88+
89+ To "modify" a work means to copy from or adapt all or part of the work
90+in a fashion requiring copyright permission, other than the making of an
91+exact copy. The resulting work is called a "modified version" of the
92+earlier work or a work "based on" the earlier work.
93+
94+ A "covered work" means either the unmodified Program or a work based
95+on the Program.
96+
97+ To "propagate" a work means to do anything with it that, without
98+permission, would make you directly or secondarily liable for
99+infringement under applicable copyright law, except executing it on a
100+computer or modifying a private copy. Propagation includes copying,
101+distribution (with or without modification), making available to the
102+public, and in some countries other activities as well.
103+
104+ To "convey" a work means any kind of propagation that enables other
105+parties to make or receive copies. Mere interaction with a user through
106+a computer network, with no transfer of a copy, is not conveying.
107+
108+ An interactive user interface displays "Appropriate Legal Notices"
109+to the extent that it includes a convenient and prominently visible
110+feature that (1) displays an appropriate copyright notice, and (2)
111+tells the user that there is no warranty for the work (except to the
112+extent that warranties are provided), that licensees may convey the
113+work under this License, and how to view a copy of this License. If
114+the interface presents a list of user commands or options, such as a
115+menu, a prominent item in the list meets this criterion.
116+
117+ 1. Source Code.
118+
119+ The "source code" for a work means the preferred form of the work
120+for making modifications to it. "Object code" means any non-source
121+form of a work.
122+
123+ A "Standard Interface" means an interface that either is an official
124+standard defined by a recognized standards body, or, in the case of
125+interfaces specified for a particular programming language, one that
126+is widely used among developers working in that language.
127+
128+ The "System Libraries" of an executable work include anything, other
129+than the work as a whole, that (a) is included in the normal form of
130+packaging a Major Component, but which is not part of that Major
131+Component, and (b) serves only to enable use of the work with that
132+Major Component, or to implement a Standard Interface for which an
133+implementation is available to the public in source code form. A
134+"Major Component", in this context, means a major essential component
135+(kernel, window system, and so on) of the specific operating system
136+(if any) on which the executable work runs, or a compiler used to
137+produce the work, or an object code interpreter used to run it.
138+
139+ The "Corresponding Source" for a work in object code form means all
140+the source code needed to generate, install, and (for an executable
141+work) run the object code and to modify the work, including scripts to
142+control those activities. However, it does not include the work's
143+System Libraries, or general-purpose tools or generally available free
144+programs which are used unmodified in performing those activities but
145+which are not part of the work. For example, Corresponding Source
146+includes interface definition files associated with source files for
147+the work, and the source code for shared libraries and dynamically
148+linked subprograms that the work is specifically designed to require,
149+such as by intimate data communication or control flow between those
150+subprograms and other parts of the work.
151+
152+ The Corresponding Source need not include anything that users
153+can regenerate automatically from other parts of the Corresponding
154+Source.
155+
156+ The Corresponding Source for a work in source code form is that
157+same work.
158+
159+ 2. Basic Permissions.
160+
161+ All rights granted under this License are granted for the term of
162+copyright on the Program, and are irrevocable provided the stated
163+conditions are met. This License explicitly affirms your unlimited
164+permission to run the unmodified Program. The output from running a
165+covered work is covered by this License only if the output, given its
166+content, constitutes a covered work. This License acknowledges your
167+rights of fair use or other equivalent, as provided by copyright law.
168+
169+ You may make, run and propagate covered works that you do not
170+convey, without conditions so long as your license otherwise remains
171+in force. You may convey covered works to others for the sole purpose
172+of having them make modifications exclusively for you, or provide you
173+with facilities for running those works, provided that you comply with
174+the terms of this License in conveying all material for which you do
175+not control copyright. Those thus making or running the covered works
176+for you must do so exclusively on your behalf, under your direction
177+and control, on terms that prohibit them from making any copies of
178+your copyrighted material outside their relationship with you.
179+
180+ Conveying under any other circumstances is permitted solely under
181+the conditions stated below. Sublicensing is not allowed; section 10
182+makes it unnecessary.
183+
184+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
185+
186+ No covered work shall be deemed part of an effective technological
187+measure under any applicable law fulfilling obligations under article
188+11 of the WIPO copyright treaty adopted on 20 December 1996, or
189+similar laws prohibiting or restricting circumvention of such
190+measures.
191+
192+ When you convey a covered work, you waive any legal power to forbid
193+circumvention of technological measures to the extent such circumvention
194+is effected by exercising rights under this License with respect to
195+the covered work, and you disclaim any intention to limit operation or
196+modification of the work as a means of enforcing, against the work's
197+users, your or third parties' legal rights to forbid circumvention of
198+technological measures.
199+
200+ 4. Conveying Verbatim Copies.
201+
202+ You may convey verbatim copies of the Program's source code as you
203+receive it, in any medium, provided that you conspicuously and
204+appropriately publish on each copy an appropriate copyright notice;
205+keep intact all notices stating that this License and any
206+non-permissive terms added in accord with section 7 apply to the code;
207+keep intact all notices of the absence of any warranty; and give all
208+recipients a copy of this License along with the Program.
209+
210+ You may charge any price or no price for each copy that you convey,
211+and you may offer support or warranty protection for a fee.
212+
213+ 5. Conveying Modified Source Versions.
214+
215+ You may convey a work based on the Program, or the modifications to
216+produce it from the Program, in the form of source code under the
217+terms of section 4, provided that you also meet all of these conditions:
218+
219+ a) The work must carry prominent notices stating that you modified
220+ it, and giving a relevant date.
221+
222+ b) The work must carry prominent notices stating that it is
223+ released under this License and any conditions added under section
224+ 7. This requirement modifies the requirement in section 4 to
225+ "keep intact all notices".
226+
227+ c) You must license the entire work, as a whole, under this
228+ License to anyone who comes into possession of a copy. This
229+ License will therefore apply, along with any applicable section 7
230+ additional terms, to the whole of the work, and all its parts,
231+ regardless of how they are packaged. This License gives no
232+ permission to license the work in any other way, but it does not
233+ invalidate such permission if you have separately received it.
234+
235+ d) If the work has interactive user interfaces, each must display
236+ Appropriate Legal Notices; however, if the Program has interactive
237+ interfaces that do not display Appropriate Legal Notices, your
238+ work need not make them do so.
239+
240+ A compilation of a covered work with other separate and independent
241+works, which are not by their nature extensions of the covered work,
242+and which are not combined with it such as to form a larger program,
243+in or on a volume of a storage or distribution medium, is called an
244+"aggregate" if the compilation and its resulting copyright are not
245+used to limit the access or legal rights of the compilation's users
246+beyond what the individual works permit. Inclusion of a covered work
247+in an aggregate does not cause this License to apply to the other
248+parts of the aggregate.
249+
250+ 6. Conveying Non-Source Forms.
251+
252+ You may convey a covered work in object code form under the terms
253+of sections 4 and 5, provided that you also convey the
254+machine-readable Corresponding Source under the terms of this License,
255+in one of these ways:
256+
257+ a) Convey the object code in, or embodied in, a physical product
258+ (including a physical distribution medium), accompanied by the
259+ Corresponding Source fixed on a durable physical medium
260+ customarily used for software interchange.
261+
262+ b) Convey the object code in, or embodied in, a physical product
263+ (including a physical distribution medium), accompanied by a
264+ written offer, valid for at least three years and valid for as
265+ long as you offer spare parts or customer support for that product
266+ model, to give anyone who possesses the object code either (1) a
267+ copy of the Corresponding Source for all the software in the
268+ product that is covered by this License, on a durable physical
269+ medium customarily used for software interchange, for a price no
270+ more than your reasonable cost of physically performing this
271+ conveying of source, or (2) access to copy the
272+ Corresponding Source from a network server at no charge.
273+
274+ c) Convey individual copies of the object code with a copy of the
275+ written offer to provide the Corresponding Source. This
276+ alternative is allowed only occasionally and noncommercially, and
277+ only if you received the object code with such an offer, in accord
278+ with subsection 6b.
279+
280+ d) Convey the object code by offering access from a designated
281+ place (gratis or for a charge), and offer equivalent access to the
282+ Corresponding Source in the same way through the same place at no
283+ further charge. You need not require recipients to copy the
284+ Corresponding Source along with the object code. If the place to
285+ copy the object code is a network server, the Corresponding Source
286+ may be on a different server (operated by you or a third party)
287+ that supports equivalent copying facilities, provided you maintain
288+ clear directions next to the object code saying where to find the
289+ Corresponding Source. Regardless of what server hosts the
290+ Corresponding Source, you remain obligated to ensure that it is
291+ available for as long as needed to satisfy these requirements.
292+
293+ e) Convey the object code using peer-to-peer transmission, provided
294+ you inform other peers where the object code and Corresponding
295+ Source of the work are being offered to the general public at no
296+ charge under subsection 6d.
297+
298+ A separable portion of the object code, whose source code is excluded
299+from the Corresponding Source as a System Library, need not be
300+included in conveying the object code work.
301+
302+ A "User Product" is either (1) a "consumer product", which means any
303+tangible personal property which is normally used for personal, family,
304+or household purposes, or (2) anything designed or sold for incorporation
305+into a dwelling. In determining whether a product is a consumer product,
306+doubtful cases shall be resolved in favor of coverage. For a particular
307+product received by a particular user, "normally used" refers to a
308+typical or common use of that class of product, regardless of the status
309+of the particular user or of the way in which the particular user
310+actually uses, or expects or is expected to use, the product. A product
311+is a consumer product regardless of whether the product has substantial
312+commercial, industrial or non-consumer uses, unless such uses represent
313+the only significant mode of use of the product.
314+
315+ "Installation Information" for a User Product means any methods,
316+procedures, authorization keys, or other information required to install
317+and execute modified versions of a covered work in that User Product from
318+a modified version of its Corresponding Source. The information must
319+suffice to ensure that the continued functioning of the modified object
320+code is in no case prevented or interfered with solely because
321+modification has been made.
322+
323+ If you convey an object code work under this section in, or with, or
324+specifically for use in, a User Product, and the conveying occurs as
325+part of a transaction in which the right of possession and use of the
326+User Product is transferred to the recipient in perpetuity or for a
327+fixed term (regardless of how the transaction is characterized), the
328+Corresponding Source conveyed under this section must be accompanied
329+by the Installation Information. But this requirement does not apply
330+if neither you nor any third party retains the ability to install
331+modified object code on the User Product (for example, the work has
332+been installed in ROM).
333+
334+ The requirement to provide Installation Information does not include a
335+requirement to continue to provide support service, warranty, or updates
336+for a work that has been modified or installed by the recipient, or for
337+the User Product in which it has been modified or installed. Access to a
338+network may be denied when the modification itself materially and
339+adversely affects the operation of the network or violates the rules and
340+protocols for communication across the network.
341+
342+ Corresponding Source conveyed, and Installation Information provided,
343+in accord with this section must be in a format that is publicly
344+documented (and with an implementation available to the public in
345+source code form), and must require no special password or key for
346+unpacking, reading or copying.
347+
348+ 7. Additional Terms.
349+
350+ "Additional permissions" are terms that supplement the terms of this
351+License by making exceptions from one or more of its conditions.
352+Additional permissions that are applicable to the entire Program shall
353+be treated as though they were included in this License, to the extent
354+that they are valid under applicable law. If additional permissions
355+apply only to part of the Program, that part may be used separately
356+under those permissions, but the entire Program remains governed by
357+this License without regard to the additional permissions.
358+
359+ When you convey a copy of a covered work, you may at your option
360+remove any additional permissions from that copy, or from any part of
361+it. (Additional permissions may be written to require their own
362+removal in certain cases when you modify the work.) You may place
363+additional permissions on material, added by you to a covered work,
364+for which you have or can give appropriate copyright permission.
365+
366+ Notwithstanding any other provision of this License, for material you
367+add to a covered work, you may (if authorized by the copyright holders of
368+that material) supplement the terms of this License with terms:
369+
370+ a) Disclaiming warranty or limiting liability differently from the
371+ terms of sections 15 and 16 of this License; or
372+
373+ b) Requiring preservation of specified reasonable legal notices or
374+ author attributions in that material or in the Appropriate Legal
375+ Notices displayed by works containing it; or
376+
377+ c) Prohibiting misrepresentation of the origin of that material, or
378+ requiring that modified versions of such material be marked in
379+ reasonable ways as different from the original version; or
380+
381+ d) Limiting the use for publicity purposes of names of licensors or
382+ authors of the material; or
383+
384+ e) Declining to grant rights under trademark law for use of some
385+ trade names, trademarks, or service marks; or
386+
387+ f) Requiring indemnification of licensors and authors of that
388+ material by anyone who conveys the material (or modified versions of
389+ it) with contractual assumptions of liability to the recipient, for
390+ any liability that these contractual assumptions directly impose on
391+ those licensors and authors.
392+
393+ All other non-permissive additional terms are considered "further
394+restrictions" within the meaning of section 10. If the Program as you
395+received it, or any part of it, contains a notice stating that it is
396+governed by this License along with a term that is a further
397+restriction, you may remove that term. If a license document contains
398+a further restriction but permits relicensing or conveying under this
399+License, you may add to a covered work material governed by the terms
400+of that license document, provided that the further restriction does
401+not survive such relicensing or conveying.
402+
403+ If you add terms to a covered work in accord with this section, you
404+must place, in the relevant source files, a statement of the
405+additional terms that apply to those files, or a notice indicating
406+where to find the applicable terms.
407+
408+ Additional terms, permissive or non-permissive, may be stated in the
409+form of a separately written license, or stated as exceptions;
410+the above requirements apply either way.
411+
412+ 8. Termination.
413+
414+ You may not propagate or modify a covered work except as expressly
415+provided under this License. Any attempt otherwise to propagate or
416+modify it is void, and will automatically terminate your rights under
417+this License (including any patent licenses granted under the third
418+paragraph of section 11).
419+
420+ However, if you cease all violation of this License, then your
421+license from a particular copyright holder is reinstated (a)
422+provisionally, unless and until the copyright holder explicitly and
423+finally terminates your license, and (b) permanently, if the copyright
424+holder fails to notify you of the violation by some reasonable means
425+prior to 60 days after the cessation.
426+
427+ Moreover, your license from a particular copyright holder is
428+reinstated permanently if the copyright holder notifies you of the
429+violation by some reasonable means, this is the first time you have
430+received notice of violation of this License (for any work) from that
431+copyright holder, and you cure the violation prior to 30 days after
432+your receipt of the notice.
433+
434+ Termination of your rights under this section does not terminate the
435+licenses of parties who have received copies or rights from you under
436+this License. If your rights have been terminated and not permanently
437+reinstated, you do not qualify to receive new licenses for the same
438+material under section 10.
439+
440+ 9. Acceptance Not Required for Having Copies.
441+
442+ You are not required to accept this License in order to receive or
443+run a copy of the Program. Ancillary propagation of a covered work
444+occurring solely as a consequence of using peer-to-peer transmission
445+to receive a copy likewise does not require acceptance. However,
446+nothing other than this License grants you permission to propagate or
447+modify any covered work. These actions infringe copyright if you do
448+not accept this License. Therefore, by modifying or propagating a
449+covered work, you indicate your acceptance of this License to do so.
450+
451+ 10. Automatic Licensing of Downstream Recipients.
452+
453+ Each time you convey a covered work, the recipient automatically
454+receives a license from the original licensors, to run, modify and
455+propagate that work, subject to this License. You are not responsible
456+for enforcing compliance by third parties with this License.
457+
458+ An "entity transaction" is a transaction transferring control of an
459+organization, or substantially all assets of one, or subdividing an
460+organization, or merging organizations. If propagation of a covered
461+work results from an entity transaction, each party to that
462+transaction who receives a copy of the work also receives whatever
463+licenses to the work the party's predecessor in interest had or could
464+give under the previous paragraph, plus a right to possession of the
465+Corresponding Source of the work from the predecessor in interest, if
466+the predecessor has it or can get it with reasonable efforts.
467+
468+ You may not impose any further restrictions on the exercise of the
469+rights granted or affirmed under this License. For example, you may
470+not impose a license fee, royalty, or other charge for exercise of
471+rights granted under this License, and you may not initiate litigation
472+(including a cross-claim or counterclaim in a lawsuit) alleging that
473+any patent claim is infringed by making, using, selling, offering for
474+sale, or importing the Program or any portion of it.
475+
476+ 11. Patents.
477+
478+ A "contributor" is a copyright holder who authorizes use under this
479+License of the Program or a work on which the Program is based. The
480+work thus licensed is called the contributor's "contributor version".
481+
482+ A contributor's "essential patent claims" are all patent claims
483+owned or controlled by the contributor, whether already acquired or
484+hereafter acquired, that would be infringed by some manner, permitted
485+by this License, of making, using, or selling its contributor version,
486+but do not include claims that would be infringed only as a
487+consequence of further modification of the contributor version. For
488+purposes of this definition, "control" includes the right to grant
489+patent sublicenses in a manner consistent with the requirements of
490+this License.
491+
492+ Each contributor grants you a non-exclusive, worldwide, royalty-free
493+patent license under the contributor's essential patent claims, to
494+make, use, sell, offer for sale, import and otherwise run, modify and
495+propagate the contents of its contributor version.
496+
497+ In the following three paragraphs, a "patent license" is any express
498+agreement or commitment, however denominated, not to enforce a patent
499+(such as an express permission to practice a patent or covenant not to
500+sue for patent infringement). To "grant" such a patent license to a
501+party means to make such an agreement or commitment not to enforce a
502+patent against the party.
503+
504+ If you convey a covered work, knowingly relying on a patent license,
505+and the Corresponding Source of the work is not available for anyone
506+to copy, free of charge and under the terms of this License, through a
507+publicly available network server or other readily accessible means,
508+then you must either (1) cause the Corresponding Source to be so
509+available, or (2) arrange to deprive yourself of the benefit of the
510+patent license for this particular work, or (3) arrange, in a manner
511+consistent with the requirements of this License, to extend the patent
512+license to downstream recipients. "Knowingly relying" means you have
513+actual knowledge that, but for the patent license, your conveying the
514+covered work in a country, or your recipient's use of the covered work
515+in a country, would infringe one or more identifiable patents in that
516+country that you have reason to believe are valid.
517+
518+ If, pursuant to or in connection with a single transaction or
519+arrangement, you convey, or propagate by procuring conveyance of, a
520+covered work, and grant a patent license to some of the parties
521+receiving the covered work authorizing them to use, propagate, modify
522+or convey a specific copy of the covered work, then the patent license
523+you grant is automatically extended to all recipients of the covered
524+work and works based on it.
525+
526+ A patent license is "discriminatory" if it does not include within
527+the scope of its coverage, prohibits the exercise of, or is
528+conditioned on the non-exercise of one or more of the rights that are
529+specifically granted under this License. You may not convey a covered
530+work if you are a party to an arrangement with a third party that is
531+in the business of distributing software, under which you make payment
532+to the third party based on the extent of your activity of conveying
533+the work, and under which the third party grants, to any of the
534+parties who would receive the covered work from you, a discriminatory
535+patent license (a) in connection with copies of the covered work
536+conveyed by you (or copies made from those copies), or (b) primarily
537+for and in connection with specific products or compilations that
538+contain the covered work, unless you entered into that arrangement,
539+or that patent license was granted, prior to 28 March 2007.
540+
541+ Nothing in this License shall be construed as excluding or limiting
542+any implied license or other defenses to infringement that may
543+otherwise be available to you under applicable patent law.
544+
545+ 12. No Surrender of Others' Freedom.
546+
547+ If conditions are imposed on you (whether by court order, agreement or
548+otherwise) that contradict the conditions of this License, they do not
549+excuse you from the conditions of this License. If you cannot convey a
550+covered work so as to satisfy simultaneously your obligations under this
551+License and any other pertinent obligations, then as a consequence you may
552+not convey it at all. For example, if you agree to terms that obligate you
553+to collect a royalty for further conveying from those to whom you convey
554+the Program, the only way you could satisfy both those terms and this
555+License would be to refrain entirely from conveying the Program.
556+
557+ 13. Use with the GNU Affero General Public License.
558+
559+ Notwithstanding any other provision of this License, you have
560+permission to link or combine any covered work with a work licensed
561+under version 3 of the GNU Affero General Public License into a single
562+combined work, and to convey the resulting work. The terms of this
563+License will continue to apply to the part which is the covered work,
564+but the special requirements of the GNU Affero General Public License,
565+section 13, concerning interaction through a network will apply to the
566+combination as such.
567+
568+ 14. Revised Versions of this License.
569+
570+ The Free Software Foundation may publish revised and/or new versions of
571+the GNU General Public License from time to time. Such new versions will
572+be similar in spirit to the present version, but may differ in detail to
573+address new problems or concerns.
574+
575+ Each version is given a distinguishing version number. If the
576+Program specifies that a certain numbered version of the GNU General
577+Public License "or any later version" applies to it, you have the
578+option of following the terms and conditions either of that numbered
579+version or of any later version published by the Free Software
580+Foundation. If the Program does not specify a version number of the
581+GNU General Public License, you may choose any version ever published
582+by the Free Software Foundation.
583+
584+ If the Program specifies that a proxy can decide which future
585+versions of the GNU General Public License can be used, that proxy's
586+public statement of acceptance of a version permanently authorizes you
587+to choose that version for the Program.
588+
589+ Later license versions may give you additional or different
590+permissions. However, no additional obligations are imposed on any
591+author or copyright holder as a result of your choosing to follow a
592+later version.
593+
594+ 15. Disclaimer of Warranty.
595+
596+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
597+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
598+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
599+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
600+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
601+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
602+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
603+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
604+
605+ 16. Limitation of Liability.
606+
607+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
608+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
609+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
610+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
611+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
612+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
613+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
614+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
615+SUCH DAMAGES.
616+
617+ 17. Interpretation of Sections 15 and 16.
618+
619+ If the disclaimer of warranty and limitation of liability provided
620+above cannot be given local legal effect according to their terms,
621+reviewing courts shall apply local law that most closely approximates
622+an absolute waiver of all civil liability in connection with the
623+Program, unless a warranty or assumption of liability accompanies a
624+copy of the Program in return for a fee.
625+
626+ END OF TERMS AND CONDITIONS
627+
628+ How to Apply These Terms to Your New Programs
629+
630+ If you develop a new program, and you want it to be of the greatest
631+possible use to the public, the best way to achieve this is to make it
632+free software which everyone can redistribute and change under these terms.
633+
634+ To do so, attach the following notices to the program. It is safest
635+to attach them to the start of each source file to most effectively
636+state the exclusion of warranty; and each file should have at least
637+the "copyright" line and a pointer to where the full notice is found.
638+
639+ <one line to give the program's name and a brief idea of what it does.>
640+ Copyright (C) <year> <name of author>
641+
642+ This program is free software: you can redistribute it and/or modify
643+ it under the terms of the GNU General Public License as published by
644+ the Free Software Foundation, either version 3 of the License, or
645+ (at your option) any later version.
646+
647+ This program is distributed in the hope that it will be useful,
648+ but WITHOUT ANY WARRANTY; without even the implied warranty of
649+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
650+ GNU General Public License for more details.
651+
652+ You should have received a copy of the GNU General Public License
653+ along with this program. If not, see <http://www.gnu.org/licenses/>.
654+
655+Also add information on how to contact you by electronic and paper mail.
656+
657+ If the program does terminal interaction, make it output a short
658+notice like this when it starts in an interactive mode:
659+
660+ <program> Copyright (C) <year> <name of author>
661+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
662+ This is free software, and you are welcome to redistribute it
663+ under certain conditions; type `show c' for details.
664+
665+The hypothetical commands `show w' and `show c' should show the appropriate
666+parts of the General Public License. Of course, your program's commands
667+might be different; for a GUI interface, you would use an "about box".
668+
669+ You should also get your employer (if you work as a programmer) or school,
670+if any, to sign a "copyright disclaimer" for the program, if necessary.
671+For more information on this, and how to apply and follow the GNU GPL, see
672+<http://www.gnu.org/licenses/>.
673+
674+ The GNU General Public License does not permit incorporating your program
675+into proprietary programs. If your program is a subroutine library, you
676+may consider it more useful to permit linking proprietary applications with
677+the library. If this is what you want to do, use the GNU Lesser General
678+Public License instead of this License. But first, please read
679+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
680
681=== added file 'timestepping/COPYING.LESSER'
682--- timestepping/COPYING.LESSER 1970-01-01 00:00:00 +0000
683+++ timestepping/COPYING.LESSER 2013-05-15 14:57:23 +0000
684@@ -0,0 +1,165 @@
685+ GNU LESSER GENERAL PUBLIC LICENSE
686+ Version 3, 29 June 2007
687+
688+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
689+ Everyone is permitted to copy and distribute verbatim copies
690+ of this license document, but changing it is not allowed.
691+
692+
693+ This version of the GNU Lesser General Public License incorporates
694+the terms and conditions of version 3 of the GNU General Public
695+License, supplemented by the additional permissions listed below.
696+
697+ 0. Additional Definitions.
698+
699+ As used herein, "this License" refers to version 3 of the GNU Lesser
700+General Public License, and the "GNU GPL" refers to version 3 of the GNU
701+General Public License.
702+
703+ "The Library" refers to a covered work governed by this License,
704+other than an Application or a Combined Work as defined below.
705+
706+ An "Application" is any work that makes use of an interface provided
707+by the Library, but which is not otherwise based on the Library.
708+Defining a subclass of a class defined by the Library is deemed a mode
709+of using an interface provided by the Library.
710+
711+ A "Combined Work" is a work produced by combining or linking an
712+Application with the Library. The particular version of the Library
713+with which the Combined Work was made is also called the "Linked
714+Version".
715+
716+ The "Minimal Corresponding Source" for a Combined Work means the
717+Corresponding Source for the Combined Work, excluding any source code
718+for portions of the Combined Work that, considered in isolation, are
719+based on the Application, and not on the Linked Version.
720+
721+ The "Corresponding Application Code" for a Combined Work means the
722+object code and/or source code for the Application, including any data
723+and utility programs needed for reproducing the Combined Work from the
724+Application, but excluding the System Libraries of the Combined Work.
725+
726+ 1. Exception to Section 3 of the GNU GPL.
727+
728+ You may convey a covered work under sections 3 and 4 of this License
729+without being bound by section 3 of the GNU GPL.
730+
731+ 2. Conveying Modified Versions.
732+
733+ If you modify a copy of the Library, and, in your modifications, a
734+facility refers to a function or data to be supplied by an Application
735+that uses the facility (other than as an argument passed when the
736+facility is invoked), then you may convey a copy of the modified
737+version:
738+
739+ a) under this License, provided that you make a good faith effort to
740+ ensure that, in the event an Application does not supply the
741+ function or data, the facility still operates, and performs
742+ whatever part of its purpose remains meaningful, or
743+
744+ b) under the GNU GPL, with none of the additional permissions of
745+ this License applicable to that copy.
746+
747+ 3. Object Code Incorporating Material from Library Header Files.
748+
749+ The object code form of an Application may incorporate material from
750+a header file that is part of the Library. You may convey such object
751+code under terms of your choice, provided that, if the incorporated
752+material is not limited to numerical parameters, data structure
753+layouts and accessors, or small macros, inline functions and templates
754+(ten or fewer lines in length), you do both of the following:
755+
756+ a) Give prominent notice with each copy of the object code that the
757+ Library is used in it and that the Library and its use are
758+ covered by this License.
759+
760+ b) Accompany the object code with a copy of the GNU GPL and this license
761+ document.
762+
763+ 4. Combined Works.
764+
765+ You may convey a Combined Work under terms of your choice that,
766+taken together, effectively do not restrict modification of the
767+portions of the Library contained in the Combined Work and reverse
768+engineering for debugging such modifications, if you also do each of
769+the following:
770+
771+ a) Give prominent notice with each copy of the Combined Work that
772+ the Library is used in it and that the Library and its use are
773+ covered by this License.
774+
775+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
776+ document.
777+
778+ c) For a Combined Work that displays copyright notices during
779+ execution, include the copyright notice for the Library among
780+ these notices, as well as a reference directing the user to the
781+ copies of the GNU GPL and this license document.
782+
783+ d) Do one of the following:
784+
785+ 0) Convey the Minimal Corresponding Source under the terms of this
786+ License, and the Corresponding Application Code in a form
787+ suitable for, and under terms that permit, the user to
788+ recombine or relink the Application with a modified version of
789+ the Linked Version to produce a modified Combined Work, in the
790+ manner specified by section 6 of the GNU GPL for conveying
791+ Corresponding Source.
792+
793+ 1) Use a suitable shared library mechanism for linking with the
794+ Library. A suitable mechanism is one that (a) uses at run time
795+ a copy of the Library already present on the user's computer
796+ system, and (b) will operate properly with a modified version
797+ of the Library that is interface-compatible with the Linked
798+ Version.
799+
800+ e) Provide Installation Information, but only if you would otherwise
801+ be required to provide such information under section 6 of the
802+ GNU GPL, and only to the extent that such information is
803+ necessary to install and execute a modified version of the
804+ Combined Work produced by recombining or relinking the
805+ Application with a modified version of the Linked Version. (If
806+ you use option 4d0, the Installation Information must accompany
807+ the Minimal Corresponding Source and Corresponding Application
808+ Code. If you use option 4d1, you must provide the Installation
809+ Information in the manner specified by section 6 of the GNU GPL
810+ for conveying Corresponding Source.)
811+
812+ 5. Combined Libraries.
813+
814+ You may place library facilities that are a work based on the
815+Library side by side in a single library together with other library
816+facilities that are not Applications and are not covered by this
817+License, and convey such a combined library under terms of your
818+choice, if you do both of the following:
819+
820+ a) Accompany the combined library with a copy of the same work based
821+ on the Library, uncombined with any other library facilities,
822+ conveyed under the terms of this License.
823+
824+ b) Give prominent notice with the combined library that part of it
825+ is a work based on the Library, and explaining where to find the
826+ accompanying uncombined form of the same work.
827+
828+ 6. Revised Versions of the GNU Lesser General Public License.
829+
830+ The Free Software Foundation may publish revised and/or new versions
831+of the GNU Lesser General Public License from time to time. Such new
832+versions will be similar in spirit to the present version, but may
833+differ in detail to address new problems or concerns.
834+
835+ Each version is given a distinguishing version number. If the
836+Library as you received it specifies that a certain numbered version
837+of the GNU Lesser General Public License "or any later version"
838+applies to it, you have the option of following the terms and
839+conditions either of that published version or of any later version
840+published by the Free Software Foundation. If the Library as you
841+received it does not specify a version number of the GNU Lesser
842+General Public License, you may choose any version of the GNU Lesser
843+General Public License ever published by the Free Software Foundation.
844+
845+ If the Library as you received it specifies that a proxy can decide
846+whether future versions of the GNU Lesser General Public License shall
847+apply, that proxy's public statement of acceptance of any version is
848+permanent authorization for you to choose that version for the
849+Library.
850
851=== added directory 'timestepping/manual'
852=== added file 'timestepping/manual/bibliography.bib'
853--- timestepping/manual/bibliography.bib 1970-01-01 00:00:00 +0000
854+++ timestepping/manual/bibliography.bib 2013-05-15 14:57:23 +0000
855@@ -0,0 +1,51 @@
856+% This file was created with JabRef 2.9b2.
857+% Encoding: UTF-8
858+
859+@BOOK{donea2003,
860+ title = {Finite element methods for flow problems},
861+ publisher = {John Wiley \& Sons},
862+ year = {2003},
863+ author = {Donea, J. and Huerta, A.},
864+ owner = {james},
865+ quality = {1},
866+ timestamp = {2013.05.08}
867+}
868+
869+@BOOK{hairer1987,
870+ title = {Solving ordinary differential equations I: Nonstiff problems},
871+ publisher = {Springer},
872+ year = {1987},
873+ author = {Hairer, E. and N{\o}rsett, S. P. and Wanner, G.},
874+ owner = {james},
875+ timestamp = {2013.05.07}
876+}
877+
878+@BOOK{iserles2009,
879+ title = {A first course in the numerical analysis of differential equations},
880+ publisher = {Cambridge University Press},
881+ year = {2009},
882+ author = {Iserles, A.},
883+ edition = {2nd},
884+ owner = {james},
885+ timestamp = {2013.05.07}
886+}
887+
888+@BOOK{mitchell1980,
889+ title = {The finite difference method in partial differential equations},
890+ publisher = {John Wiley \& Sons},
891+ year = {1980},
892+ author = {Mitchell, A. R. and Griffiths, D. F.},
893+ owner = {james},
894+ timestamp = {2013.05.07}
895+}
896+
897+@BOOK{logg2012,
898+ title = {Automated solution of differential equations by the finite element
899+ method: The FEniCS book},
900+ publisher = {Springer},
901+ year = {2012},
902+ editor = {Logg, A. and Mardal, K.-A. and Wells, G. N.},
903+ owner = {james},
904+ timestamp = {2013.01.09}
905+}
906+
907
908=== added directory 'timestepping/manual/examples'
909=== added file 'timestepping/manual/examples/TimeFunction'
910--- timestepping/manual/examples/TimeFunction 1970-01-01 00:00:00 +0000
911+++ timestepping/manual/examples/TimeFunction 2013-05-15 14:57:23 +0000
912@@ -0,0 +1,38 @@
913+#!/usr/bin/env python
914+
915+# Copyright (C) 2013 University of Oxford
916+#
917+# This program is free software: you can redistribute it and/or modify
918+# it under the terms of the GNU Lesser General Public License as published by
919+# the Free Software Foundation, version 3 of the License
920+#
921+# This program is distributed in the hope that it will be useful,
922+# but WITHOUT ANY WARRANTY; without even the implied warranty of
923+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
924+# GNU Lesser General Public License for more details.
925+#
926+# You should have received a copy of the GNU Lesser General Public License
927+# along with this program. If not, see <http://www.gnu.org/licenses/>.
928+
929+from dolfin import *
930+from timestepping import *
931+
932+# Define a simple structured mesh on the unit square
933+mesh = UnitSquareMesh(10, 10)
934+# P1 function space
935+space_p1 = FunctionSpace(mesh, "CG", 1)
936+# P2_{DG} function space
937+space_p2dg = FunctionSpace(mesh, "DG", 2)
938+
939+# Define two different sets of time levels
940+levels_1 = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
941+levels_2 = TimeLevels(levels = [n], cycle_map = {},
942+ last_past_level = n - 1)
943+
944+# Define time dependent functions on the levels
945+F1 = TimeFunction(levels_1, space_p1, name = "F1")
946+F2 = TimeFunction(levels_2, space_p2dg, name = "F2")
947+
948+# An equation involving the two time dependent functions
949+test_p1 = TestFunction(space_p1)
950+eq = inner(test_p1, F1[n + 1]) * dx == inner(test_p1, F2[n]) * dx
951\ No newline at end of file
952
953=== added file 'timestepping/manual/examples/TimeLevel'
954--- timestepping/manual/examples/TimeLevel 1970-01-01 00:00:00 +0000
955+++ timestepping/manual/examples/TimeLevel 2013-05-15 14:57:23 +0000
956@@ -0,0 +1,49 @@
957+#!/usr/bin/env python
958+
959+# Copyright (C) 2013 University of Oxford
960+#
961+# This program is free software: you can redistribute it and/or modify
962+# it under the terms of the GNU Lesser General Public License as published by
963+# the Free Software Foundation, version 3 of the License
964+#
965+# This program is distributed in the hope that it will be useful,
966+# but WITHOUT ANY WARRANTY; without even the implied warranty of
967+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
968+# GNU Lesser General Public License for more details.
969+#
970+# You should have received a copy of the GNU Lesser General Public License
971+# along with this program. If not, see <http://www.gnu.org/licenses/>.
972+
973+from timestepping import *
974+from fractions import Fraction
975+
976+# Create a TimeLevel with an offset of 2
977+n1 = TimeLevel(2)
978+# Create a TimeLevel with an offset of -1/2
979+n2 = TimeLevel(-Fraction(1, 2))
980+# Create a FinalTimeLevel with an offset of 0
981+N1 = FinalTimeLevel()
982+
983+# Create a TimeLevel with an offset of 2
984+n3 = n + 2
985+# Create a TimeLevel with an offset of -3/2
986+n4 = n - Fraction(3, 2)
987+
988+# Create a FinalTimeLevel with an offset of 1
989+N2 = N + 1
990+
991+print n1 == n2 # False
992+print n1 > n2 # True
993+print n1 == n3 # True
994+print n1 >= n3 # True
995+
996+print N1 == N2 # False
997+print N1 <= N2 # True
998+
999+assert(not n1 == n2)
1000+assert(n1 > n2)
1001+assert(n1 == n3)
1002+assert(n1 >= n3)
1003+
1004+assert(not N1 == N2)
1005+assert(N1 <= N2)
1006\ No newline at end of file
1007
1008=== added file 'timestepping/manual/examples/TimeLevels'
1009--- timestepping/manual/examples/TimeLevels 1970-01-01 00:00:00 +0000
1010+++ timestepping/manual/examples/TimeLevels 2013-05-15 14:57:23 +0000
1011@@ -0,0 +1,42 @@
1012+#!/usr/bin/env python
1013+
1014+# Copyright (C) 2013 University of Oxford
1015+#
1016+# This program is free software: you can redistribute it and/or modify
1017+# it under the terms of the GNU Lesser General Public License as published by
1018+# the Free Software Foundation, version 3 of the License
1019+#
1020+# This program is distributed in the hope that it will be useful,
1021+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1022+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1023+# GNU Lesser General Public License for more details.
1024+#
1025+# You should have received a copy of the GNU Lesser General Public License
1026+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1027+
1028+from timestepping import *
1029+from fractions import *
1030+
1031+# Create a TimeLevels with one past level and one future level, with
1032+# the past level data replaced by the future level data during the
1033+# timestep variable cycle. Suitable for use with forward Euler,
1034+# backward Euler, or Crank-Nicolson schemes.
1035+levels_1 = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
1036+
1037+# Create a TimeLevels with one future level
1038+levels_2 = TimeLevels(levels = [n], cycle_map = {},
1039+ last_past_level = n - 1)
1040+
1041+# Create a TimeLevels with one past level and two future levels,
1042+# with the past level data replaced by the latest future level data
1043+# during the timestep variable cycle. Suitable for use with a
1044+# second order Runge-Kutta scheme.
1045+levels_3 = TimeLevels(levels = [n, n + Fraction(1, 2), n + 1],
1046+ cycle_map = {n:n + 1})
1047+
1048+# Create a TimeLevels with three past levels and one future level,
1049+# with the past levels replaced by the nearest later level during
1050+# the timestep variable cycle. Suitable for use with a third order
1051+# Adams-Bashforth scheme.
1052+levels_4 = TimeLevels(levels = [n - 2, n - 1, n, n + 1],
1053+ cycle_map = {n - 2:n - 1, n - 1:n, n:n + 1})
1054\ No newline at end of file
1055
1056=== added file 'timestepping/manual/examples/add_solve'
1057--- timestepping/manual/examples/add_solve 1970-01-01 00:00:00 +0000
1058+++ timestepping/manual/examples/add_solve 2013-05-15 14:57:23 +0000
1059@@ -0,0 +1,52 @@
1060+#!/usr/bin/env python
1061+
1062+# Copyright (C) 2013 University of Oxford
1063+#
1064+# This program is free software: you can redistribute it and/or modify
1065+# it under the terms of the GNU Lesser General Public License as published by
1066+# the Free Software Foundation, version 3 of the License
1067+#
1068+# This program is distributed in the hope that it will be useful,
1069+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1070+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1071+# GNU Lesser General Public License for more details.
1072+#
1073+# You should have received a copy of the GNU Lesser General Public License
1074+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1075+
1076+from dolfin import *
1077+from timestepping import *
1078+
1079+# Define a simple structured mesh on the unit interval
1080+mesh = UnitIntervalMesh(10)
1081+# P1 function space
1082+space = FunctionSpace(mesh, "CG", 1)
1083+
1084+# Model parameters and boundary conditions
1085+dt = Constant(0.05)
1086+bc1 = DirichletBC(space, 1.0, "on_boundary && near(x[0], 0.0)")
1087+bc2 = DirichletBC(space, 0.0, "on_boundary && near(x[0], 1.0)")
1088+bcs = [bc1, bc2]
1089+nu = Constant(0.01)
1090+
1091+# Define time levels
1092+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
1093+# A time dependent function
1094+u = TimeFunction(levels, space, name = "u")
1095+
1096+# Initialise a TimeSystem
1097+system = TimeSystem()
1098+
1099+# Add an initial assignment
1100+u_ic = Function(space, name = "u_ic")
1101+u_ic.assign(Constant(0.0))
1102+bc1.apply(u_ic.vector())
1103+system.add_solve(u_ic, u[0])
1104+# Register a simple diffusion equation, discretised in time
1105+# using forward Euler
1106+test = TestFunction(space)
1107+system.add_solve(
1108+ inner(test, (1.0 / dt) * (u[n + 1] - u[n])) * dx ==
1109+ -nu * inner(grad(test), grad(u[n])) * dx,
1110+ u[n + 1], bcs,
1111+ solver_parameters = {"linear_solver":"lu"})
1112\ No newline at end of file
1113
1114=== added file 'timestepping/manual/examples/add_solve_statics'
1115--- timestepping/manual/examples/add_solve_statics 1970-01-01 00:00:00 +0000
1116+++ timestepping/manual/examples/add_solve_statics 2013-05-15 14:57:23 +0000
1117@@ -0,0 +1,54 @@
1118+#!/usr/bin/env python
1119+
1120+# Copyright (C) 2013 University of Oxford
1121+#
1122+# This program is free software: you can redistribute it and/or modify
1123+# it under the terms of the GNU Lesser General Public License as published by
1124+# the Free Software Foundation, version 3 of the License
1125+#
1126+# This program is distributed in the hope that it will be useful,
1127+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1128+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1129+# GNU Lesser General Public License for more details.
1130+#
1131+# You should have received a copy of the GNU Lesser General Public License
1132+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1133+
1134+from dolfin import *
1135+from timestepping import *
1136+
1137+# Define a simple structured mesh on the unit interval
1138+mesh = UnitIntervalMesh(10)
1139+# P1 function space
1140+space = FunctionSpace(mesh, "CG", 1)
1141+
1142+# Model parameters and boundary conditions
1143+dt = StaticConstant(0.05)
1144+bc1 = StaticDirichletBC(space, 1.0,
1145+ "on_boundary && near(x[0], 0.0)")
1146+bc2 = StaticDirichletBC(space, 0.0,
1147+ "on_boundary && near(x[0], 1.0)")
1148+bcs = [bc1, bc2]
1149+nu = StaticConstant(0.01)
1150+
1151+# Define time levels
1152+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
1153+# A time dependent function
1154+u = TimeFunction(levels, space, name = "u")
1155+
1156+# Initialise a TimeSystem
1157+system = TimeSystem()
1158+
1159+# Add an initial assignment
1160+u_ic = StaticFunction(space, name = "u_ic")
1161+u_ic.assign(Constant(0.0))
1162+bc1.apply(u_ic.vector())
1163+system.add_solve(u_ic, u[0])
1164+# Register a simple diffusion equation, discretised in time
1165+# using forward Euler
1166+test = TestFunction(space)
1167+system.add_solve(
1168+ inner(test, (1.0 / dt) * (u[n + 1] - u[n])) * dx ==
1169+ -nu * inner(grad(test), grad(u[n])) * dx,
1170+ u[n + 1], bcs,
1171+ solver_parameters = {"linear_solver":"lu"})
1172\ No newline at end of file
1173
1174=== added file 'timestepping/manual/examples/diffusion_fe'
1175--- timestepping/manual/examples/diffusion_fe 1970-01-01 00:00:00 +0000
1176+++ timestepping/manual/examples/diffusion_fe 2013-05-15 14:57:23 +0000
1177@@ -0,0 +1,65 @@
1178+#!/usr/bin/env python
1179+
1180+# Copyright (C) 2013 University of Oxford
1181+#
1182+# This program is free software: you can redistribute it and/or modify
1183+# it under the terms of the GNU Lesser General Public License as published by
1184+# the Free Software Foundation, version 3 of the License
1185+#
1186+# This program is distributed in the hope that it will be useful,
1187+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1188+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1189+# GNU Lesser General Public License for more details.
1190+#
1191+# You should have received a copy of the GNU Lesser General Public License
1192+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1193+
1194+from dolfin import *
1195+from timestepping import *
1196+
1197+# Define a simple structured mesh on the unit interval
1198+mesh = UnitIntervalMesh(10)
1199+# P1 function space
1200+space = FunctionSpace(mesh, "CG", 1)
1201+
1202+# Model parameters and boundary conditions
1203+dt = StaticConstant(0.05)
1204+bc1 = StaticDirichletBC(space, 1.0,
1205+ "on_boundary && near(x[0], 0.0)")
1206+bc2 = StaticDirichletBC(space, 0.0,
1207+ "on_boundary && near(x[0], 1.0)")
1208+bcs = [bc1, bc2]
1209+nu = StaticConstant(0.01)
1210+
1211+# Define time levels
1212+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
1213+# A time dependent function
1214+u = TimeFunction(levels, space, name = "u")
1215+
1216+# Initialise a TimeSystem
1217+system = TimeSystem()
1218+
1219+# Add an initial assignment
1220+u_ic = StaticFunction(space, name = "u_ic")
1221+u_ic.assign(Constant(0.0))
1222+bc1.apply(u_ic.vector())
1223+system.add_solve(u_ic, u[0])
1224+# Register a simple diffusion equation, discretised in time
1225+# using forward Euler
1226+test = TestFunction(space)
1227+system.add_solve(
1228+ inner(test, (1.0 / dt) * (u[n + 1] - u[n])) * dx ==
1229+ -nu * inner(grad(test), grad(u[n])) * dx,
1230+ u[n + 1], bcs,
1231+ solver_parameters = {"linear_solver":"lu"})
1232+
1233+# Assemble the TimeSystem
1234+system = system.assemble()
1235+
1236+# Timestep the model
1237+t = 0.0
1238+while t * (1.0 + 1.0e-9) < 1.0:
1239+ system.timestep()
1240+ t += float(dt)
1241+# Finalise
1242+system.finalise()
1243\ No newline at end of file
1244
1245=== added file 'timestepping/manual/examples/diffusion_fe_adjoined'
1246--- timestepping/manual/examples/diffusion_fe_adjoined 1970-01-01 00:00:00 +0000
1247+++ timestepping/manual/examples/diffusion_fe_adjoined 2013-05-15 14:57:23 +0000
1248@@ -0,0 +1,77 @@
1249+#!/usr/bin/env python
1250+
1251+# Copyright (C) 2013 University of Oxford
1252+#
1253+# This program is free software: you can redistribute it and/or modify
1254+# it under the terms of the GNU Lesser General Public License as published by
1255+# the Free Software Foundation, version 3 of the License
1256+#
1257+# This program is distributed in the hope that it will be useful,
1258+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1259+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1260+# GNU Lesser General Public License for more details.
1261+#
1262+# You should have received a copy of the GNU Lesser General Public License
1263+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1264+
1265+from dolfin import *
1266+from timestepping import *
1267+
1268+# Define a simple structured mesh on the unit interval
1269+mesh = UnitIntervalMesh(10)
1270+# P1 function space
1271+space = FunctionSpace(mesh, "CG", 1)
1272+
1273+# Model parameters and boundary conditions
1274+dt = StaticConstant(0.05)
1275+bc1 = StaticDirichletBC(space, 1.0,
1276+ "on_boundary && near(x[0], 0.0)")
1277+bc2 = StaticDirichletBC(space, 0.0,
1278+ "on_boundary && near(x[0], 1.0)")
1279+bcs = [bc1, bc2]
1280+nu = StaticConstant(0.01)
1281+
1282+# Define time levels
1283+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
1284+# A time dependent function
1285+u = TimeFunction(levels, space, name = "u")
1286+
1287+# Initialise a TimeSystem
1288+system = TimeSystem()
1289+
1290+# Add an initial assignment
1291+u_ic = StaticFunction(space, name = "u_ic")
1292+u_ic.assign(Constant(0.0))
1293+bc1.apply(u_ic.vector())
1294+system.add_solve(u_ic, u[0])
1295+# Register a simple diffusion equation, discretised in time
1296+# using forward Euler
1297+test = TestFunction(space)
1298+system.add_solve(
1299+ inner(test, (1.0 / dt) * (u[n + 1] - u[n])) * dx ==
1300+ -nu * inner(grad(test), grad(u[n])) * dx,
1301+ u[n + 1], bcs,
1302+ solver_parameters = {"linear_solver":"lu"})
1303+
1304+# Assemble the TimeSystem, enabling the adjoint. Set the
1305+# functional to be equal to spatial integral of the final u.
1306+system = system.assemble(adjoint = True, functional = u[N] * dx)
1307+
1308+# Timestep the model
1309+t = 0.0
1310+while t * (1.0 + 1.0e-9) < 1.0:
1311+ system.timestep()
1312+ t += float(dt)
1313+# Finalise
1314+system.finalise()
1315+
1316+# Perform a model constrained derivative calculation
1317+dJdm = system.compute_gradient(nu)
1318+
1319+# Verify the stored forward model data
1320+system.verify_checkpoints()
1321+# Verify the computed derivative using a Taylor remainder
1322+# convergence test
1323+orders = system.taylor_test(nu, grad = dJdm)
1324+# Check the convergence order
1325+assert((orders > 1.99).all())
1326\ No newline at end of file
1327
1328=== added file 'timestepping/manual/examples/statics'
1329--- timestepping/manual/examples/statics 1970-01-01 00:00:00 +0000
1330+++ timestepping/manual/examples/statics 2013-05-15 14:57:23 +0000
1331@@ -0,0 +1,33 @@
1332+#!/usr/bin/env python
1333+
1334+# Copyright (C) 2013 University of Oxford
1335+#
1336+# This program is free software: you can redistribute it and/or modify
1337+# it under the terms of the GNU Lesser General Public License as published by
1338+# the Free Software Foundation, version 3 of the License
1339+#
1340+# This program is distributed in the hope that it will be useful,
1341+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1342+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1343+# GNU Lesser General Public License for more details.
1344+#
1345+# You should have received a copy of the GNU Lesser General Public License
1346+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1347+
1348+from dolfin import *
1349+from timestepping import *
1350+
1351+# Create a constant which is declared as static
1352+c = StaticConstant(1.0, name = "c")
1353+
1354+# Define a simple structured mesh on the unit interval
1355+mesh = UnitIntervalMesh(10)
1356+# P1 function space
1357+space = FunctionSpace(mesh, "CG", 1)
1358+# Create a function which is declared as static
1359+F = StaticFunction(space, name = "F")
1360+# Initialise the static function
1361+F.interpolate(Expression("x[0]"))
1362+
1363+# Create a Dirichlet boundary condition which is declared as static
1364+bc = StaticDirichletBC(space, 0.0, "on_boundary")
1365\ No newline at end of file
1366
1367=== added file 'timestepping/manual/examples/time_discretisations'
1368--- timestepping/manual/examples/time_discretisations 1970-01-01 00:00:00 +0000
1369+++ timestepping/manual/examples/time_discretisations 2013-05-15 14:57:23 +0000
1370@@ -0,0 +1,267 @@
1371+#!/usr/bin/env python
1372+
1373+# Copyright (C) 2013 University of Oxford
1374+#
1375+# This program is free software: you can redistribute it and/or modify
1376+# it under the terms of the GNU Lesser General Public License as published by
1377+# the Free Software Foundation, version 3 of the License
1378+#
1379+# This program is distributed in the hope that it will be useful,
1380+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1381+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1382+# GNU Lesser General Public License for more details.
1383+#
1384+# You should have received a copy of the GNU Lesser General Public License
1385+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1386+
1387+from dolfin import *
1388+from timestepping import *
1389+
1390+mesh = UnitIntervalMesh(10)
1391+space = FunctionSpace(mesh, "CG", 1)
1392+test = TestFunction(space)
1393+
1394+T_ic = StaticFunction(space, name = "T_ic")
1395+T_ic.assign(Constant(1.0))
1396+x = StaticFunction(space, name = "x")
1397+x.interpolate(Expression("x[0]"))
1398+def F(test, T):
1399+ return inner(test, x) * dx
1400+dt = StaticConstant(0.5)
1401+solver_parameters = {"linear_solver":"lu"}
1402+
1403+def check(name, system, T_f, ns_i = 0, tol = 0.0):
1404+ system = system.assemble()
1405+ system.timestep(ns = 2)
1406+ system.finalise()
1407+ if not isinstance(T_f, dolfin.Function):
1408+ lT_f = Function(space, name = "T_f")
1409+ pa_solve(inner(test, lT_f) * dx == inner(test, T_f) * dx,
1410+ lT_f, solver_parameters = solver_parameters)
1411+ T_f = lT_f
1412+ err = (T_f.vector() - (T_ic.vector() + 0.5 * (ns_i + 2) * x.vector())).norm("linf")
1413+ print "%s: %.17e" % (name, err)
1414+ assert(err < tol)
1415+ return
1416+
1417+# First order Adams-Bashforth
1418+
1419+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
1420+levels_dT = TimeLevels(levels = [n], cycle_map = {},
1421+ last_past_level = n - 1)
1422+T = TimeFunction(levels, space, name = "T")
1423+dT = TimeFunction(levels_dT, space, name = "dT")
1424+
1425+system = TimeSystem()
1426+
1427+system.add_solve(T_ic, T[0])
1428+
1429+system.add_solve(inner(test, dT[n]) * dx == dt * F(test, T[n]),
1430+ dT[n], solver_parameters = solver_parameters)
1431+system.add_solve(LinearCombination((1.0, T[n]), (1.0, dT[n])),
1432+ T[n + 1])
1433+
1434+check("AB1", system, T[N], tol = 3.0e-15)
1435+
1436+# Second order Adams-Bashforth
1437+
1438+levels = TimeLevels(levels = [n, n + 1, n + 2],
1439+ cycle_map = {n:n + 1, n + 1:n + 2}, last_past_level = n + 1)
1440+levels_dT = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1},
1441+ last_past_level = n)
1442+T = TimeFunction(levels, space, name = "T")
1443+dT = TimeFunction(levels_dT, space, name = "dT")
1444+
1445+system = TimeSystem()
1446+
1447+system.add_solve(T_ic, T[0])
1448+
1449+system.add_solve(inner(test, dT[0]) * dx == dt * F(test, T[0]),
1450+ dT[0], solver_parameters = solver_parameters)
1451+system.add_solve(LinearCombination((1.0, T[0]),
1452+ (1.0, dT[0])),
1453+ T[1])
1454+
1455+system.add_solve(inner(test, dT[n + 1]) * dx ==
1456+ dt * F(test, T[n + 1]),
1457+ dT[n + 1], solver_parameters = solver_parameters)
1458+system.add_solve(LinearCombination((1.0, T[n + 1]),
1459+ (3.0 / 2.0, dT[n + 1]),
1460+ (-1.0 / 2.0, dT[n])),
1461+ T[n + 2])
1462+
1463+check("AB2", system, T[N + 1], ns_i = 1, tol = 4.0e-15)
1464+
1465+# Third order Adams-Bashforth
1466+
1467+from fractions import Fraction
1468+hf = Fraction(1, 2)
1469+
1470+levels = TimeLevels(levels = [n, n + hf, n + 1, n + 2, n + 3],
1471+ cycle_map = {n:n + 1, n + 1:n + 2, n + 2:n + 3},
1472+ last_past_level = n + 2)
1473+levels_dT = TimeLevels(levels = [n, n + hf, n + 1, n + 2],
1474+ cycle_map = {n:n + 1, n + 1:n + 2}, last_past_level = n + 1)
1475+T = TimeFunction(levels, space, name = "T")
1476+dT = TimeFunction(levels_dT, space, name = "dT")
1477+
1478+system = TimeSystem()
1479+
1480+system.add_solve(T_ic, T[0])
1481+
1482+system.add_solve(inner(test, dT[0]) * dx == dt * F(test, T[0]),
1483+ dT[0], solver_parameters = solver_parameters)
1484+system.add_solve(LinearCombination((1.0, T[0]),
1485+ (0.5, dT[0])),
1486+ T[hf])
1487+
1488+system.add_solve(inner(test, dT[hf]) * dx == dt * F(test, T[hf]),
1489+ dT[hf], solver_parameters = solver_parameters)
1490+system.add_solve(LinearCombination((1.0, T[0]),
1491+ (1.0, dT[hf])),
1492+ T[1])
1493+
1494+system.add_solve(inner(test, dT[1]) * dx == dt * F(test, T[1]),
1495+ dT[1], solver_parameters = solver_parameters)
1496+system.add_solve(LinearCombination((1.0, T[1]),
1497+ (3.0 / 2.0, dT[1]),
1498+ (-1.0 / 2.0, dT[0])),
1499+ T[2])
1500+
1501+system.add_solve(inner(test, dT[n + 2]) * dx ==
1502+ dt * F(test, T[n + 2]),
1503+ dT[n + 2], solver_parameters = solver_parameters)
1504+system.add_solve(LinearCombination((1.0, T[n + 2]),
1505+ (23.0 / 12.0, dT[n + 2]),
1506+ (-4.0 / 3.0, dT[n + 1]),
1507+ (5.0 / 12.0, dT[n])),
1508+ T[n + 3])
1509+
1510+check("AB3", system, T[N + 2], ns_i = 2, tol = 6.0e-15)
1511+
1512+# Leapfrog
1513+
1514+levels = TimeLevels(levels = [n, n + 1, n + 2],
1515+ cycle_map = {n:n + 1, n + 1:n + 2}, last_past_level = n + 1)
1516+levels_dT = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1},
1517+ last_past_level = n)
1518+T = TimeFunction(levels, space, name = "T")
1519+dT = TimeFunction(levels_dT, space, name = "dT")
1520+
1521+system = TimeSystem()
1522+
1523+system.add_solve(T_ic, T[0])
1524+
1525+system.add_solve(inner(test, dT[0]) * dx == dt * F(test, T[0]),
1526+ dT[0], solver_parameters = solver_parameters)
1527+system.add_solve(LinearCombination((1.0, T[0]),
1528+ (1.0, dT[0])),
1529+ T[1])
1530+
1531+system.add_solve(inner(test, dT[n + 1]) * dx ==
1532+ dt * F(test, T[n + 1]),
1533+ dT[n + 1], solver_parameters = solver_parameters)
1534+system.add_solve(LinearCombination((1.0, T[n]),
1535+ (2.0, dT[n + 1])),
1536+ T[n + 2])
1537+
1538+check("LF", system, T[N + 1], ns_i = 1, tol = 4.0e-15)
1539+
1540+# Backward Euler
1541+
1542+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
1543+T = TimeFunction(levels, space, name = "T")
1544+
1545+system = TimeSystem()
1546+
1547+system.add_solve(T_ic, T[0])
1548+
1549+system.add_solve(inner(test, T[n + 1]) * dx ==
1550+ inner(test, T[n]) * dx + dt * F(test, T[n + 1]),
1551+ T[n + 1], solver_parameters = solver_parameters)
1552+
1553+check("AM1", system, T[N], tol = 3.0e-15)
1554+
1555+# Crank-Nicolson (Implicit trapezium rule)
1556+
1557+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
1558+T = TimeFunction(levels, space, name = "T")
1559+
1560+system = TimeSystem()
1561+
1562+system.add_solve(T_ic, T[0])
1563+
1564+system.add_solve(inner(test, T[n + 1]) * dx ==
1565+ inner(test, T[n]) * dx +
1566+ dt * (0.5 * F(test, T[n + 1]) + 0.5 * F(test, T[n])),
1567+ T[n + 1], solver_parameters = solver_parameters)
1568+
1569+check("AM2", system, T[N], tol = 3.0e-15)
1570+
1571+# Crank-Nicolson (Implicit midpoint rule)
1572+
1573+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
1574+T = TimeFunction(levels, space, name = "T")
1575+
1576+system = TimeSystem()
1577+
1578+system.add_solve(T_ic, T[0])
1579+
1580+system.add_solve(inner(test, T[n + 1]) * dx ==
1581+ inner(test, T[n]) * dx +
1582+ dt * F(test, 0.5 * T[n + 1] + 0.5 * T[n]),
1583+ T[n + 1], solver_parameters = solver_parameters)
1584+
1585+check("IMR", system, T[N], tol = 3.0e-15)
1586+
1587+# Second order explicit Runge-Kutta
1588+
1589+from fractions import Fraction
1590+hf = Fraction(1, 2)
1591+
1592+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
1593+levels_F = TimeLevels(levels = [n, n + hf], cycle_map = {},
1594+ last_past_level = n - hf)
1595+T = TimeFunction(levels, space, name = "T")
1596+F_s = TimeFunction(levels_F, space, name = "F_s")
1597+
1598+system = TimeSystem()
1599+
1600+system.add_solve(T_ic, T[0])
1601+
1602+system.add_solve(inner(test, F_s[n]) * dx == F(test, T[n]),
1603+ F_s[n], solver_parameters = solver_parameters)
1604+system.add_solve(inner(test, F_s[n + hf]) * dx ==
1605+ F(test, T[n] + 0.5 * dt * F_s[n]),
1606+ F_s[n + hf], solver_parameters = solver_parameters)
1607+
1608+system.add_solve(LinearCombination((1.0, T[n]),
1609+ (dt, F_s[n + hf])),
1610+ T[n + 1])
1611+
1612+check("RK2", system, T[N], tol = 5.0e-16)
1613+
1614+# P1_DG
1615+
1616+spaces = space * space
1617+tests = TestFunction(spaces)
1618+test1, test2 = split(tests)
1619+
1620+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
1621+T = TimeFunction(levels, spaces, name = "T")
1622+
1623+system = TimeSystem()
1624+
1625+system.add_solve(inner(tests, T[0]) * dx == inner(test2, T_ic) * dx,
1626+ T[0], solver_parameters = solver_parameters)
1627+
1628+m_11 = m_22 = 1.0 / 3.0
1629+m_12 = m_21 = 1.0 / 6.0
1630+system.add_solve(
1631+ inner(test1, 0.5 * T[n + 1][1] + 0.5 * T[n + 1][0] - T[n][1])*dx +
1632+ inner(test2, 0.5 * T[n + 1][1] - 0.5 * T[n + 1][0]) * dx ==
1633+ dt*(m_11 * F(test1, T[n + 1][0]) + m_12 * F(test1, T[n + 1][1]) +
1634+ m_21 * F(test2, T[n + 1][0]) + m_22 * F(test2, T[n + 1][1])),
1635+ T[n + 1], solver_parameters = {"linear_solver":"lu"})
1636+
1637+check("P1_DG", system, split(T[n])[1], tol = 1.0e-14)
1638\ No newline at end of file
1639
1640=== added file 'timestepping/manual/examples/wrappers'
1641--- timestepping/manual/examples/wrappers 1970-01-01 00:00:00 +0000
1642+++ timestepping/manual/examples/wrappers 2013-05-15 14:57:23 +0000
1643@@ -0,0 +1,28 @@
1644+#!/usr/bin/env python
1645+
1646+# Copyright (C) 2013 University of Oxford
1647+#
1648+# This program is free software: you can redistribute it and/or modify
1649+# it under the terms of the GNU Lesser General Public License as published by
1650+# the Free Software Foundation, version 3 of the License
1651+#
1652+# This program is distributed in the hope that it will be useful,
1653+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1654+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1655+# GNU Lesser General Public License for more details.
1656+#
1657+# You should have received a copy of the GNU Lesser General Public License
1658+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1659+
1660+from dolfin import *
1661+from timestepping import *
1662+
1663+# A named Constant
1664+c = Constant(1.0, name = "c")
1665+
1666+# Define a simple structured mesh on the unit interval
1667+mesh = UnitIntervalMesh(10)
1668+# P1 function space
1669+space = FunctionSpace(mesh, "CG", 1)
1670+# A named Function
1671+F = Function(space, name = "F")
1672\ No newline at end of file
1673
1674=== added file 'timestepping/manual/makefile'
1675--- timestepping/manual/makefile 1970-01-01 00:00:00 +0000
1676+++ timestepping/manual/makefile 2013-05-15 14:57:23 +0000
1677@@ -0,0 +1,24 @@
1678+LATEXFLAGS=-halt-on-error
1679+BIBTEXFLAGS=
1680+
1681+TEX = $(wildcard *.tex)
1682+PDF = $(addsuffix .pdf, $(basename $(TEX)))
1683+
1684+default: $(PDF)
1685+
1686+%.pdf: %.tex bibliography.bib
1687+ pdflatex $(LATEXFLAGS) $<
1688+ bibtex $(BIBTEXFLAGS) $(basename $<) || true
1689+ pdflatex $(LATEXFLAGS) $<
1690+ pdflatex $(LATEXFLAGS) $<
1691+ pdflatex $(LATEXFLAGS) $<
1692+ pdflatex $(LATEXFLAGS) $<
1693+
1694+clean:
1695+ rm -f $(PDF)
1696+ rm -f $(addsuffix .aux, $(basename $(TEX)))
1697+ rm -f $(addsuffix .bbl, $(basename $(TEX)))
1698+ rm -f $(addsuffix .blg, $(basename $(TEX)))
1699+ rm -f $(addsuffix .log, $(basename $(TEX)))
1700+ rm -f $(addsuffix .out, $(basename $(TEX)))
1701+ rm -f $(addsuffix .toc, $(basename $(TEX)))
1702
1703=== added file 'timestepping/manual/manual.tex'
1704--- timestepping/manual/manual.tex 1970-01-01 00:00:00 +0000
1705+++ timestepping/manual/manual.tex 2013-05-15 14:57:23 +0000
1706@@ -0,0 +1,1636 @@
1707+\documentclass[a4paper]{book}
1708+
1709+\usepackage{amsmath}
1710+\usepackage{amssymb}
1711+\usepackage{array}
1712+\usepackage{graphicx}
1713+\usepackage{hyperref}
1714+\usepackage{listings}
1715+\usepackage{longtable}
1716+\usepackage{natbib}
1717+\usepackage{nicefrac}
1718+\usepackage{ulem}
1719+\usepackage[svgnames]{xcolor}
1720+
1721+\hypersetup{
1722+ colorlinks = true,
1723+ citecolor = DarkBlue,
1724+ linkcolor = DarkBlue,
1725+ urlcolor = DarkBlue
1726+}
1727+
1728+\lstset{
1729+ language = Python,
1730+ basicstyle = \ttfamily\footnotesize,
1731+ frame = single,
1732+ commentstyle = \color{Grey},
1733+ keywordstyle = \color{blue},
1734+ stringstyle = \color{DarkViolet},
1735+ keepspaces = true,
1736+ showspaces = false,
1737+ showstringspaces = false
1738+}
1739+
1740+\newcommand{\version}{1.2.0}
1741+
1742+\begin{document}
1743+
1744+\begin{titlepage}
1745+\begin{center}
1746+
1747+\Huge{\verb+timestepping+ Manual} \\[0.02\textheight]
1748+\large{The high-level representation of transient finite element models with DOLFIN} \\[0.02\textheight]
1749+\Huge{Version \version} \\[0.15\textheight]
1750+\vfill
1751+\large{\today}
1752+
1753+\end{center}
1754+\end{titlepage}
1755+
1756+\tableofcontents
1757+
1758+\chapter{Introduction}
1759+
1760+\section{Overview}
1761+
1762+The \verb+timestepping+ Python library is an extension for DOLFIN, adding a
1763+high-level representation for time dependent finite element models.
1764+Specifically, the \verb+timestepping+ library enables the rapid development of
1765+efficient timestepping finite element models using a symbolic representation of
1766+the entire model discretisation. The FEniCS system (for which DOLFIN acts as a
1767+front-end) uses automated code generation to convert a symbolic representation
1768+of a finite element spatial discretisation into a working model. The
1769+\verb+timestepping+ library builds on this principle, adding a syntax to enable
1770+a symbolic representation of a temporal discretisation.
1771+
1772+A machine interpretable representation of a discretisation is amenable to
1773+automated analysis and manipulation. In particular, if a symbolic representation
1774+of a model time discretisation is available then a number of optimisation
1775+strategies can be applied automatically. This enables time independent data,
1776+which may be expensive to recompute, to be computed once and cached ahead of the
1777+execution of the main model time loop. Moreover, the FEniCS system supplies
1778+symbolic manipulation tools which enable one to differentiate and adjoin
1779+spatially discretised equations. This functionality can be combined with a
1780+high-level representation of the temporal discretisation to enable the automatic
1781+derivation of an associated discrete adjoint model. Since the resulting adjoint
1782+model itself has a symbolic representation, it is also amenable to the
1783+application of optimisation strategies. Combined, the approach allows the entire
1784+model to be specified using only a symbolic representation of model equations,
1785+yields an efficient implementation of the forward model code, and enables the
1786+automated derivation and efficient implementation of an associated discrete
1787+adjoint model code.
1788+
1789+Since the \verb+timestepping+ library builds upon the FEniCS system, it inherits
1790+benefits associated with the application of automated code generation
1791+techniques. In particular, automated code generation can be used to separate the
1792+specification of a numerical model from the details of its implementation. The
1793+\verb+timestepping+ library inherits much of the portability of the FEniCS
1794+system: architectures and parallelisation strategies supported by the FEniCS
1795+system can be used in combination with the \verb+timestepping+ library.
1796+
1797+This manual describes the functionality and interfaces provided by the
1798+\linebreak \verb+timestepping+ Python library. This library makes extensive use
1799+of the DOLFIN Python library, but the functionality and interfaces provided by
1800+DOLFIN will not be described in detail here. For further details of DOLFIN and
1801+the FEniCS system the reader is instead referred to \citet{logg2012}. In
1802+addition, some familiarity with the Python language, the finite element method,
1803+simple time discretisations, and adjoint modelling is assumed.
1804+
1805+\section{General principles}\label{sect:principles}
1806+
1807+The \verb+timestepping+ library works on the principle that a timestepping
1808+model can be broken into three key stages:
1809+\begin{enumerate}
1810+ \item The initialisation stage: A series of equations which are solved
1811+ exactly once.
1812+ \item The timestepping stage: A series of equations which are solved
1813+ repeatedly, and potentially a very large number of times.
1814+ \item The finalisation stage: A series of equations which are solved
1815+ exactly once.
1816+\end{enumerate}
1817+The library further assumes that the timestepping stage can be sub-divided into
1818+two key stages:
1819+\begin{enumerate}
1820+ \item The timestep solve stage: A series of timestep equations are solved,
1821+ using earlier time data as input and yielding future time data as
1822+ output.
1823+ \item The timestep variable cycle stage: Past time data is replaced using
1824+ later time data.
1825+\end{enumerate}
1826+The interfaces provided by the library mimic this structure: the library
1827+provides a syntax for the description of each of these key stages. The
1828+\verb+timestepping+ library may not be suitable for problems which are
1829+incompatible with this representation.
1830+
1831+The implementation of the \verb+timestepping+ library makes a series of
1832+additional assumptions regarding the performance demands of the timestepping
1833+model. While these are not in principle essential to the development of time
1834+discretisation specific optimisations, they have nevertheless generally been
1835+applied in the development of the library. The assumptions are:
1836+\begin{enumerate}
1837+ \item Model initialisation is cheap.
1838+ \item Work performed on every model timestep is expensive.
1839+ \item Caching of data is preferable to recalculation.
1840+\end{enumerate}
1841+Since it is assumed that model initialisation is cheap, it is generally assumed
1842+that the cost associated with the manipulation and analysis of model equations
1843+is negligible. The timestepping model itself is optimised, but the process of
1844+generating the optimised model is not. It is further generally assumed that
1845+all work performed by a model timestep should be minimised, and therefore
1846+that aggressive caching strategies should be applied. It is assumed that
1847+sufficient memory is available for such caching\footnote{Storage of the forward
1848+model solution, for the purposes of performing an adjoint calculation, is a
1849+notable exception to this principle.}
1850+
1851+\section{Dependencies}
1852+
1853+The \verb+timestepping+ library has been tested with the following dependency
1854+versions:
1855+
1856+\begin{center}
1857+\begin{tabular}{| c | c | }
1858+\hline
1859+Dependency & Tested dependency versions \\
1860+\hline
1861+Python & 2.7.3, 2.7.4 \\
1862+\hline
1863+NumPy & 1.6.1, 1.7.1 \\
1864+SciPy & 0.9.0, 0.11.0 \\
1865+VTK & 5.8.0 \\
1866+PETSc & 3.1.0p8, 3.2.0p5 \\
1867+\hline
1868+DOLFIN & 1.0.0, 1.1.1+, 1.2.0 \\
1869+FIAT & 1.0.0, 1.1.0 \\
1870+FErari & 1.0.0 \\
1871+FFC & 1.0.0, 1.1.0, 1.2.0 \\
1872+Instant & 1.0.0, 1.1.0, 1.2.0 \\
1873+SyFi & 1.0 \\
1874+UFC & 2.0.5, 2.1.1, 2.2.0 \\
1875+UFL & 1.0.0, 1.1.0, 1.2.0 \\
1876+Viper & 1.0.0 \\
1877+\hline
1878+\end{tabular}
1879+\end{center}
1880+
1881+\section{Library interfaces}
1882+
1883+The \verb+timestepping+ library can be divided into a primary high-level
1884+interface and a secondary low-level interface. The high-level interface requires
1885+interaction with high-level data types representing features such as model time
1886+levels, functions, and model equations, and provides a syntax enabling the
1887+description and implementation of a model with the structure discussed in
1888+section \ref{sect:principles}. The low-level interface provides additional
1889+advanced features, for example relating to the definition of explicit time
1890+dependency, but requires interaction with the internal structure of the
1891+\verb+timestepping+ library.
1892+
1893+This manual documents only the primary high-level interface. Further
1894+documentation relating to all functions and classes in the \verb+timestepping+
1895+library can be accessed via the online documentation. For example, in a Python
1896+shell:
1897+\begin{lstlisting}
1898+>>> import timestepping
1899+>>> help(timestepping)
1900+\end{lstlisting}
1901+will display the online documentation.
1902+
1903+\chapter{The high-level interface}
1904+
1905+This chapter details the primary high-level interface provided by the \linebreak
1906+\verb+timestepping+ library. This interface provides a syntax enabling the
1907+description and implementation of a model with the structure discussed in
1908+section \ref{sect:principles}.
1909+
1910+\section{Accessing the library}
1911+
1912+The \verb+timestepping+ library is intended to be used in combination with
1913+DOLFIN. The \verb+timestepping+ library overrides some aspects of the DOLFIN
1914+interface, and hence the standard way of accessing the library is via:
1915+\begin{lstlisting}
1916+from dolfin import *
1917+from timestepping import *
1918+\end{lstlisting}
1919+
1920+\section{Changes to DOLFIN interfaces}\label{sect:wrappers}
1921+
1922+The \verb+timestepping+ library provides \verb+Constant+ and \verb+Function+
1923+functions, which wrap the DOLFIN \verb+dolfin.Constant+ and
1924+\verb+dolfin.Function+ constructors. The arguments accepted by these functions
1925+are identical to the arguments accepted by the DOLFIN constructors, except that
1926+each accepts an optional string \verb+name+ keyword argument defining the
1927+\verb+Constant+ or \verb+Function+ name.
1928+
1929+\subsection*{Examples}
1930+
1931+\begin{lstlisting}
1932+from dolfin import *
1933+from timestepping import *
1934+
1935+# A named Constant
1936+c = Constant(1.0, name = "c")
1937+
1938+# Define a simple structured mesh on the unit interval
1939+mesh = UnitIntervalMesh(10)
1940+# P1 function space
1941+space = FunctionSpace(mesh, "CG", 1)
1942+# A named Function
1943+F = Function(space, name = "F")
1944+\end{lstlisting}
1945+
1946+\section{Time level abstraction}
1947+
1948+Model time levels are represented in the following way:
1949+\begin{enumerate}
1950+ \item Initialisation stage time levels: Via an integer or
1951+ \verb+fractions.Fraction+.
1952+ \item Timestep stage time levels: Via a \verb+TimeLevel+ object.
1953+ \item Finalisation stage time levels: Via a \verb+FinalTimeLevel+ object.
1954+\end{enumerate}
1955+
1956+In the initialisation stage time levels are represented using an integer
1957+or a \verb+fractions.Fraction+. The value zero is used to indicate the
1958+transition point between the initialisation stage and the timestepping stage.
1959+
1960+In the timestepping stage time levels are represented using a \verb+TimeLevel+
1961+object. A \verb+TimeLevel+ has an associated integer or
1962+\verb+fractions.Fraction+ ``offset'', enabling the logical comparison of
1963+different \verb+TimeLevel+ objects. The \verb+timestepping+ library provides
1964+an abstract handle \verb+n+, which is a \linebreak \verb+TimeLevel+ assigned an
1965+offset value of zero.
1966+
1967+In the finalisation stage time levels are represented using a
1968+\verb+FinalTimeLevel+ object. A \verb+FinalTimeLevel+ has an associated integer
1969+or \verb+fractions.Fraction+ ``offset'' enabling the logical comparison of
1970+different \verb+FinalTimeLevel+ objects. The \verb+timestepping+ library
1971+provides an abstract handle \verb+N+, which is a \linebreak
1972+\verb+FinalTimeLevel+ assigned an offset value of zero. An offset value of zero
1973+is used to indicate the transition point between the timestepping stage and the
1974+finalisation stage.
1975+
1976+In the initialisation stage levels represented using an integer or \linebreak
1977+\verb+fractions.Fraction+ are well defined, but those represented using a
1978+\verb+TimeLevel+ or \verb+FinalTimeLevel+ are not. Immediately after
1979+initialisation levels represented using an integer, \verb+fractions.Fraction+,
1980+or \verb+TimeLevel+ are well defined, but those represented using a
1981+\verb+FinalTimeLevel+ are not. After the first timestep levels represented using
1982+a \verb+TimeLevel+ are well defined, but those represented using an integer,
1983+\verb+fractions.Fraction+, or \verb+FinalTimeLevel+ are not. After finalisation
1984+levels represented using a \verb+TimeLevel+ or \verb+FinalTimeLevel+ are well
1985+defined, but those represented using an integer or \verb+fractions.Fraction+ are
1986+not.
1987+
1988+Immediately after initialisation levels represented using an integer or
1989+\linebreak \verb+fractions.Fraction+ with value \verb+i+ are equivalent to
1990+levels represented using a \verb+TimeLevel+ with value \verb=n + i=. After
1991+finalisation levels represented using a \verb+TimeLevel+ with value \verb=n + i=
1992+are equivalent to those represented using a \linebreak \verb+FinalTimeLevel+
1993+with value \verb=N + i=.
1994+
1995+New \verb+TimeLevel+ and \verb+FinalTimeLevel+ objects can be instantiated
1996+directly. The high-level constructor arguments are:
1997+\begin{lstlisting}
1998+ n_custom = TimeLevel(arg)
1999+ N_custom = FinalTimeLevel(arg)
2000+\end{lstlisting}
2001+where the optional argument is an integer or \verb+fractions.Fraction+ defining
2002+the offset. If this argument is not supplied then a default value of zero is
2003+used. New \verb+TimeLevel+ and \verb+FinalTimeLevel+ objects can also be
2004+instantiated via the addition or subtraction of an integer or
2005+\verb+fractions.Fraction+ from an existing \verb+TimeLevel+ or
2006+\verb+FinalTimeLevel+:
2007+\begin{lstlisting}
2008+n_custom = n + offset_1
2009+N_custom = N - offset_2
2010+\end{lstlisting}
2011+
2012+\subsection*{Examples}
2013+\begin{lstlisting}
2014+from timestepping import *
2015+from fractions import Fraction
2016+
2017+# Create a TimeLevel with an offset of 2
2018+n1 = TimeLevel(2)
2019+# Create a TimeLevel with an offset of -1/2
2020+n2 = TimeLevel(-Fraction(1, 2))
2021+# Create a FinalTimeLevel with an offset of 0
2022+N1 = FinalTimeLevel()
2023+
2024+# Create a TimeLevel with an offset of 2
2025+n3 = n + 2
2026+# Create a TimeLevel with an offset of -3/2
2027+n4 = n - Fraction(3, 2)
2028+
2029+# Create a FinalTimeLevel with an offset of 1
2030+N2 = N + 1
2031+
2032+print n1 == n2 # False
2033+print n1 > n2 # True
2034+print n1 == n3 # True
2035+print n1 >= n3 # True
2036+
2037+print N1 == N2 # False
2038+print N1 <= N2 # True
2039+\end{lstlisting}
2040+
2041+\section{Function time levels}
2042+
2043+A \verb+TimeLevels+ object represents a unique set of model time levels,
2044+combined with a ``cycle map'' defining how data is to be re-assigned during the
2045+timestep variable cycle stage. A \verb+TimeLevels+ also defines the ``present''
2046+point, defining which levels are to be treated as being in the past and which
2047+levels are to be treated as being in the future.
2048+
2049+A \verb+TimeLevels+ object is instantiated via:
2050+\begin{lstlisting}
2051+time_levels = TimeLevels(levels, cycle_map, last_past_level)
2052+\end{lstlisting}
2053+where \verb+levels+ is a list containing \verb+TimeLevel+ objects,
2054+\verb+cycle_map+ is a dictionary with \verb+TimeLevel+ keys and values, and the
2055+optional \verb+last_past_level+ is a \verb+TimeLevel+.
2056+
2057+\verb+levels+ defines the model time levels which are to be represented by the
2058+\verb+TimeLevels+ object. The elements of \verb+levels+ must be unique.
2059+
2060+\verb+cycle_map+ defines the timestep variable cycle. At the end of a model
2061+timestep data corresponding to each key in \verb+cycle_map+ is replaced with
2062+data corresponding to the associated value in the \verb+cycle_map+. The
2063+replacement of data is performed in order, with earlier (lower offset) keys in
2064+\verb+cycle_map+ replaced before later keys. All keys in \verb+cycle_map+ must
2065+be earlier (have a lower offset) than their associated values. The elements in
2066+\verb+cycle_map.values()+ must be unique.
2067+
2068+The optional \verb+last_past_level+ defines the ``present'' point associated
2069+with the \verb+TimeLevels+. Time levels before or equal to
2070+\verb+last_past_level+ (with a lower or equal offset) are treated as being in
2071+the past, while other levels are treated as being in the future. If
2072+\verb+last_past_level+ is not supplied then a default value of \verb+n+ is used.
2073+All keys in \verb+cycle_map+ must be in the past.
2074+
2075+\subsection*{Examples}
2076+
2077+\begin{lstlisting}
2078+from timestepping import *
2079+from fractions import *
2080+
2081+# Create a TimeLevels with one past level and one future level, with
2082+# the past level data replaced by the future level data during the
2083+# timestep variable cycle. Suitable for use with forward Euler,
2084+# backward Euler, or Crank-Nicolson schemes.
2085+levels_1 = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
2086+
2087+# Create a TimeLevels with one future level
2088+levels_2 = TimeLevels(levels = [n], cycle_map = {},
2089+ last_past_level = n - 1)
2090+
2091+# Create a TimeLevels with one past level and two future levels,
2092+# with the past level data replaced by the latest future level data
2093+# during the timestep variable cycle. Suitable for use with a
2094+# second order Runge-Kutta scheme.
2095+levels_3 = TimeLevels(levels = [n, n + Fraction(1, 2), n + 1],
2096+ cycle_map = {n:n + 1})
2097+
2098+# Create a TimeLevels with three past levels and one future level,
2099+# with the past levels replaced by the nearest later level during
2100+# the timestep variable cycle. Suitable for use with a third order
2101+# Adams-Bashforth scheme.
2102+levels_4 = TimeLevels(levels = [n - 2, n - 1, n, n + 1],
2103+ cycle_map = {n - 2:n - 1, n - 1:n, n:n + 1})
2104+\end{lstlisting}
2105+
2106+\section{Time dependent functions}
2107+
2108+A \verb+TimeFunction+ object defines a series of \verb+dolfin.Function+ objects,
2109+each specified on an individual time level.
2110+
2111+A \verb+TimeFunction+ object is instantiated via:
2112+\begin{lstlisting}
2113+tfn = TimeFunction(levels, *args, name = name, **kwargs)
2114+\end{lstlisting}
2115+where \verb+levels+ is a \verb+TimeLevels+, \verb+args+ and \verb+kwargs+ are
2116+arguments as accepted by a \verb+dolfin.Function+ constructor, and \verb+name+
2117+is an optional string representing the function name.
2118+
2119+A symbolic representation of a single function, defined on a single time level,
2120+can be accessed by indexing into the \verb+TimeFunction+ using an integer,
2121+\verb+fractions.Fraction+, \verb+TimeLevel+, or \verb+FinalTimeLevel+. For
2122+example:
2123+\begin{lstlisting}
2124+tfn[0] # Representation of the function at time level 0
2125+tfn[n + 1] # Representation of the function at time level n + 1
2126+tfn[N - 2] # Representation of the function at time level N - 2
2127+\end{lstlisting}
2128+The index provided must match a level that, given the \verb+TimeLevels+ used to
2129+instantiate the \verb+TimeFunction+, is well defined at some point in the model
2130+execution.
2131+
2132+Prior to initialisation a \verb+dolfin.Function+ returned by indexing into the
2133+\verb+TimeFunction+ can be used for expressing equations symbolically, but
2134+cannot be used to access model data. Immediately after initialisation the
2135+\linebreak \verb+dolfin.Function+ returned by indexing into the
2136+\verb+TimeFunction+ with an integer, \verb+fractions.Fraction+ or
2137+\verb+TimeLevel+ can be used to access model data, provided the time level is in
2138+the past. After the first timestep the \verb+dolfin.Function+ returned by
2139+indexing into the \verb+TimeFunction+ with a \verb+TimeLevel+ can be used to
2140+access model data, provided the time level is in the past. After finalisation
2141+the \verb+dolfin.Function+ returned by indexing into the \verb+TimeFunction+
2142+with a \verb+TimeLevel+ or a \verb+FinalTimeLevel+ can be used to access model
2143+data. In all other cases, the data associated with \verb+dolfin.Function+
2144+objects returned by indexing into the \verb+TimeFunction+ are in an undefined
2145+state.
2146+
2147+\subsection*{Examples}
2148+
2149+\begin{lstlisting}
2150+from dolfin import *
2151+from timestepping import *
2152+
2153+# Define a simple structured mesh on the unit square
2154+mesh = UnitSquareMesh(10, 10)
2155+# P1 function space
2156+space_p1 = FunctionSpace(mesh, "CG", 1)
2157+# P2_{DG} function space
2158+space_p2dg = FunctionSpace(mesh, "DG", 2)
2159+
2160+# Define two different sets of time levels
2161+levels_1 = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
2162+levels_2 = TimeLevels(levels = [n], cycle_map = {},
2163+ last_past_level = n - 1)
2164+
2165+# Define time dependent functions on the levels
2166+F1 = TimeFunction(levels_1, space_p1, name = "F1")
2167+F2 = TimeFunction(levels_2, space_p2dg, name = "F2")
2168+
2169+# An equation involving the two time dependent functions
2170+test_p1 = TestFunction(space_p1)
2171+eq = inner(test_p1, F1[n + 1]) * dx == inner(test_p1, F2[n]) * dx
2172+\end{lstlisting}
2173+
2174+\section{Model equations}
2175+
2176+A \verb+TimeSystem+ object is used to keep a record of model equations.
2177+Model equations can be registered with a \verb+TimeSystem+ by calling the
2178+\verb+add_assignment+ or \verb+add_solve+ methods.
2179+
2180+A \verb+TimeSystem+ is instantiated without arguments:
2181+\begin{lstlisting}
2182+system = TimeSystem()
2183+\end{lstlisting}
2184+
2185+\subsection{Registering assignments}
2186+
2187+An assignment can be registered via the \verb+add_assignment+ method:
2188+\begin{lstlisting}
2189+system.add_assignment(y, x)
2190+\end{lstlisting}
2191+Here \verb+x+ is a \verb+dolfin.Function+ corresponding to a time level of a
2192+\verb+TimeFunction+, and \verb+y+ is a float, a \verb+dolfin.Constant+, a
2193+\verb+dolfin.Function+, a \verb+LinearCombination+ (explained below), or a
2194+general \verb+ufl.expr.Expr+ (an arbitrary expression). This call corresponds to
2195+an equation in which \verb+x+ is set equal to the value of \verb+y+. The value
2196+of \verb+y+ must correspond to a either a constant value or a function defined
2197+in the function space of \verb+x+. The assignment must also be linear, with
2198+\verb+y+ independent of \verb+x+.
2199+
2200+A \verb+LinearCombination+ is instantiated via:
2201+\begin{lstlisting}
2202+lc = LinearCombination(*args)
2203+\end{lstlisting}
2204+where the arguments \verb+args+ are an arbitrary list of tuples
2205+\verb+(alpha, y)+. Each \verb+alpha+ is a float, a \verb+dolfin.Constant+, a
2206+\verb+dolfin.Function+, or a general \linebreak \verb+ufl.expr.Expr+. The
2207+\verb+LinearCombination+ then represents the value of $\sum_i \alpha_i y_i$.
2208+Note that the \verb+LinearCombination+ constructor does not check that all the
2209+\verb+alpha+ are independent of all the \verb+y+, and consequently does not
2210+check that the object represents a true linear combination. All the \verb+y+
2211+must be in the same function space, and all the \verb+alpha+ must correspond
2212+either to a constant value or a function defined in the function space of the
2213+\verb+y+.
2214+
2215+Constant real \verb+dolfin.Function+ objects (i.e. functions defined in a
2216+DOLFIN function space \verb+FunctionSpace(mesh, "R", 0)+) are, when used in
2217+general expressions (\verb+y+ in the \verb+add_solve+ method or an \verb+alpha+
2218+in the \verb+LinearCombination+ constructor), treated as corresponding to a
2219+single constant value.
2220+
2221+\subsection{Registering finite element variational problems}
2222+
2223+An equation representing a finite element spatial discretisation can be
2224+registered via the \verb+add_solve+ method. The arguments of this method are
2225+largely based upon the arguments accepted by the \verb+dolfin.solve+ function.
2226+The following are the high-level interfaces offered by this method:
2227+
2228+\begin{lstlisting}
2229+system.add_solve(y, x)
2230+\end{lstlisting}
2231+where \verb+y+ and \verb+x+ are as expected by the \verb+add_assignment+ method.
2232+This calls the \verb+add_assignment+ method.
2233+
2234+\begin{lstlisting}
2235+system.add_solve(a == L, x, bcs,
2236+ solver_parameters = solver_parameters,
2237+ adjoint_solver_parameters = adjoint_solver_parameters)
2238+\end{lstlisting}
2239+where \verb+a+ is a rank 2 \verb+dolfin.Form+, \verb+L+ is a rank 1 \linebreak
2240+\verb+dolfin.Form+, and \verb+x+ is a \verb+dolfin.Function+ corresponding to a
2241+time level of a \linebreak \verb+TimeFunction+. This registers a (possibly
2242+non-linear) variational problem. Alternatively, one may use:
2243+\begin{lstlisting}
2244+system.add_solve(eq, x, bcs,
2245+ solver_parameters = solver_parameters,
2246+ adjoint_solver_parameters = adjoint_solver_parameters)
2247+\end{lstlisting}
2248+where \verb+eq+ is a (possibly non-linear) \verb+dolfin.Equation+ depending on
2249+\verb+x+, and \verb+x+ is a \verb+dolfin.Function+ corresponding to a time level
2250+of a \verb+TimeFunction+. This registers a (possibly non-linear) variational
2251+problem represented by the equation \verb+eq+.
2252+
2253+If a registered variational problem is non-linear then the problem is solved
2254+using a Newton solver. The optional \verb+bcs+ is a \verb+dolfin.DirichletBC+ or
2255+a list of \verb+dolfin.DirichletBC+ objects, defining Dirichlet boundary
2256+conditions to be applied when solving the variational problem. The optional
2257+\verb+solver_parameters+ is a dictionary of DOLFIN solver parameters for use in
2258+solving the variational problem, while the optional
2259+\verb+adjoint_solver_parameters+ is a dictionary of DOLFIN solver parameters for
2260+use in solving an associated adjoint variational problem. If
2261+\verb+solver_parameters+ is not supplied then the default parameters defined in
2262+\verb+dolfin.parameters+ (at the time of the \verb+TimeSystem.assemble+ call --
2263+see section \ref{sect:assembly}) are used. If \verb+adjoint_solver_parameters+
2264+is not supplied then the forward solver parameters are used in solving an
2265+associated adjoint variational problem.
2266+
2267+\subsection{Permitted equation dependencies}
2268+
2269+The solution \verb+dolfin.Function+ of a registered equation, \verb+x+,
2270+corresponds either to an initialisation stage time level, a timestep stage time
2271+level, or a finalisation stage time level. Any time level dependencies of the
2272+equation must be in this stage. For example, any time level dependencies of an
2273+equation which solves for an initialisation stage time level must themselves
2274+correspond to an initialisation stage time level. The following is a valid
2275+registration:
2276+\begin{lstlisting}
2277+system.add_solve(F[-1], F[0])
2278+\end{lstlisting}
2279+(provide that \verb+F+ is defined on the relevant levels), while the following
2280+is an \emph{invalid} registration:
2281+\begin{lstlisting}
2282+system.add_solve(F[n], F[0])
2283+\end{lstlisting}
2284+
2285+If the solution \verb+dolfin.Function+ of a registered equation, \verb+x+,
2286+corresponds to an initialisation stage time level, then the time level must be
2287+in the past. If \verb+x+ corresponds to a timestep stage or finalisation stage
2288+time level, then the time level must be in the future. No dependencies of a
2289+registered equation can correspond to time levels later than \verb+x+.
2290+
2291+Note that the order of equation registration is irrelevant, and that
2292+circular dependencies are not permitted -- dependency resolution is applied in
2293+the analysis and optimisation step (see section \ref{sect:assembly}).
2294+
2295+\subsection*{Examples}
2296+
2297+\begin{lstlisting}
2298+from dolfin import *
2299+from timestepping import *
2300+
2301+# Define a simple structured mesh on the unit interval
2302+mesh = UnitIntervalMesh(10)
2303+# P1 function space
2304+space = FunctionSpace(mesh, "CG", 1)
2305+
2306+# Model parameters and boundary conditions
2307+dt = Constant(0.05)
2308+bc1 = DirichletBC(space, 1.0, "on_boundary && near(x[0], 0.0)")
2309+bc2 = DirichletBC(space, 0.0, "on_boundary && near(x[0], 1.0)")
2310+bcs = [bc1, bc2]
2311+nu = Constant(0.01)
2312+
2313+# Define time levels
2314+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
2315+# A time dependent function
2316+u = TimeFunction(levels, space, name = "u")
2317+
2318+# Initialise a TimeSystem
2319+system = TimeSystem()
2320+
2321+# Add an initial assignment
2322+u_ic = Function(space, name = "u_ic")
2323+u_ic.assign(Constant(0.0))
2324+bc1.apply(u_ic.vector())
2325+system.add_solve(u_ic, u[0])
2326+# Register a simple diffusion equation, discretised in time
2327+# using forward Euler
2328+test = TestFunction(space)
2329+system.add_solve(
2330+ inner(test, (1.0 / dt) * (u[n + 1] - u[n])) * dx ==
2331+ -nu * inner(grad(test), grad(u[n])) * dx,
2332+ u[n + 1], bcs,
2333+ solver_parameters = {"linear_solver":"lu"})
2334+\end{lstlisting}
2335+See also section \ref{sect:statics} for a modification of this example,
2336+declaring static data.
2337+
2338+\section{Static data}\label{sect:statics}
2339+
2340+Many optimisations of the timestepping model require the identification of
2341+static data, which does not vary throughout the model execution. Additional
2342+functions are provided to facilitate such optimisations.
2343+
2344+The \verb+StaticConstant+ function returns a \verb+dolfin.Constant+ that is
2345+declared as ``static'. The arguments are identical to the \verb+Constant+
2346+wrapper (see section \ref{sect:wrappers}). The \verb+StaticFunction+ function
2347+returns a \verb+dolfin.Function+ that is declared as ``static'. The arguments
2348+are identical to the \verb+Function+ wrapper (see section
2349+\ref{sect:wrappers}). The \verb+StaticDirichletBC+ class defines a
2350+\verb+dolfin.DirichletBC+ that is declared as ``static''. The constructor
2351+arguments are identical to the \verb+dolfin.DirichletBC+ constructor.
2352+
2353+Objects that are declared as ``static'' in this way should not be modified
2354+during the model execution -- doing so leads to undefined behaviour. Objects
2355+declared as static may be modified before the initialisation stage.
2356+
2357+\subsection*{Examples}
2358+
2359+\begin{lstlisting}
2360+from dolfin import *
2361+from timestepping import *
2362+
2363+# Create a constant which is declared as static
2364+c = StaticConstant(1.0, name = "c")
2365+
2366+# Define a simple structured mesh on the unit interval
2367+mesh = UnitIntervalMesh(10)
2368+# P1 function space
2369+space = FunctionSpace(mesh, "CG", 1)
2370+# Create a function which is declared as static
2371+F = StaticFunction(space, name = "F")
2372+# Initialise the static function
2373+F.interpolate(Expression("x[0]"))
2374+
2375+# Create a Dirichlet boundary condition which is declared as static
2376+bc = StaticDirichletBC(space, 0.0, "on_boundary")
2377+\end{lstlisting}
2378+
2379+\begin{lstlisting}
2380+from dolfin import *
2381+from timestepping import *
2382+
2383+# Define a simple structured mesh on the unit interval
2384+mesh = UnitIntervalMesh(10)
2385+# P1 function space
2386+space = FunctionSpace(mesh, "CG", 1)
2387+
2388+# Model parameters and boundary conditions
2389+dt = StaticConstant(0.05)
2390+bc1 = StaticDirichletBC(space, 1.0,
2391+ "on_boundary && near(x[0], 0.0)")
2392+bc2 = StaticDirichletBC(space, 0.0,
2393+ "on_boundary && near(x[0], 1.0)")
2394+bcs = [bc1, bc2]
2395+nu = StaticConstant(0.01)
2396+
2397+# Define time levels
2398+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
2399+# A time dependent function
2400+u = TimeFunction(levels, space, name = "u")
2401+
2402+# Initialise a TimeSystem
2403+system = TimeSystem()
2404+
2405+# Add an initial assignment
2406+u_ic = StaticFunction(space, name = "u_ic")
2407+u_ic.assign(Constant(0.0))
2408+bc1.apply(u_ic.vector())
2409+system.add_solve(u_ic, u[0])
2410+# Register a simple diffusion equation, discretised in time
2411+# using forward Euler
2412+test = TestFunction(space)
2413+system.add_solve(
2414+ inner(test, (1.0 / dt) * (u[n + 1] - u[n])) * dx ==
2415+ -nu * inner(grad(test), grad(u[n])) * dx,
2416+ u[n + 1], bcs,
2417+ solver_parameters = {"linear_solver":"lu"})
2418+\end{lstlisting}
2419+
2420+\section{Analysis and optimisation}\label{sect:assembly}
2421+
2422+The \verb+assemble+ method of a \verb+TimeSystem+ analyses the model, performs
2423+time discretisation specific optimisations, and (by default) performs the
2424+initialisation stage, returning an object suitable for execution of the model
2425+time loop. An adjoint model is enabled by supplying additional arguments to the
2426+\verb+assemble+ method.
2427+
2428+The \verb+assemble+ method returns an \verb+ForwardModel+ if no adjoint model is
2429+enabled, and an \verb+ManagedModel+ otherwise. Note that there is a fairly
2430+strong assumption that at most one \verb+ForwardModel+ or
2431+\verb+ManagedModel+ is instantiated at once. Instantiating two or more at the
2432+same time leads to undefined behaviour.
2433+
2434+\subsection{Forward model only}\label{sect:forward_assembly}
2435+
2436+If an adjoint model is not enabled, the \verb+assemble+ method has high-level
2437+arguments:
2438+\begin{lstlisting}
2439+asystem = system.assemble(initialise = initialise)
2440+\end{lstlisting}
2441+This returns an \verb+ForwardModel+. If the optional \verb+initialise+ is not
2442+supplied or is \verb+True+ then, immediately after the \verb+assemble+ call, the
2443+initialisation stage is complete. Otherwise, the initialisation stage can be
2444+performed via a subsequent call to the \verb+initialise+ method:
2445+\begin{lstlisting}
2446+asystem.initialise()
2447+\end{lstlisting}
2448+
2449+\subsection{Enabling an adjoint model}\label{sect:adjoint_assembly}
2450+
2451+An adjoint model is enabled by calling the \verb+assemble+ method with the
2452+optional \verb+adjoint+ boolean argument set equal to \verb+True+. The remaining
2453+high-level arguments are:
2454+\begin{lstlisting}
2455+asystem = system.assemble(adjoint = adjoint,
2456+ disk_period = disk_period, functional = functional,
2457+ initialise = initialise)
2458+\end{lstlisting}
2459+This returns an \verb+ManagedModel+. If the optional \verb+initialise+ is not
2460+supplied or is \verb+True+ then, immediately after the \verb+assemble+ call, the
2461+initialisation stage is complete. Otherwise, the initialisation stage can be
2462+performed via a subsequent call to the \verb+initialise+ method:
2463+\begin{lstlisting}
2464+asystem.initialise()
2465+\end{lstlisting}
2466+The optional integer \verb+disk_period+ defines how often the forward model
2467+solution is to be checkpointed to disk. Data is checkpointed every
2468+\verb+disk_period+ timesteps, and is stored in a directory \verb+checkpoints~+
2469+relative to the current working directory. If \verb+disk_period+ is not supplied
2470+then the entire forward model solution is stored in memory. In the high-level
2471+interface the optional \verb+functional+ is a rank 0 form.
2472+\verb+dolfin.Function+ dependencies of the functional which correspond to levels
2473+of a \verb+TimeFunction+ must correspond to finalisation stage time levels. If
2474+\verb+functional+ is not supplied to the \verb+assemble+ method then a
2475+functional can instead be specified via the \verb+set_functional+ method:
2476+\begin{lstlisting}
2477+asystem.set_functional(functional)
2478+\end{lstlisting}
2479+
2480+\section{Timestepping}
2481+
2482+The model is timestepped by calling the \verb+timestep+ method of an \linebreak
2483+\verb+ForwardModel+ or \verb+ManagedModel+:
2484+\begin{lstlisting}
2485+asystem.timestep(s = s)
2486+\end{lstlisting}
2487+where the optional integer \verb+s+ is the number of timesteps to perform. If
2488+\verb+s+ is not supplied then a single timestep is performed. The
2489+\verb+timestep+ method performs both the timestep solve and timestep cycle
2490+stages. At the model end the finalisation stage is performed by calling the
2491+\verb+finalise+ method:
2492+\begin{lstlisting}
2493+asystem.finalise()
2494+\end{lstlisting}
2495+
2496+The initialisation stage should be complete before calling the \verb+timestep+
2497+method (see section \ref{sect:assembly}). If an adjoint model is not enabled
2498+then the model can, after a \verb+finalise+ call, be restarted via a call to the
2499+\verb+initialise+ method (see section \ref{sect:forward_assembly}). In this case
2500+the \verb+timestep+ and \verb+finalise+ methods should not be called between the
2501+\verb+finalise+ and \verb+initialise+ calls. If the model is not restarted, or
2502+if an adjoint model is enabled, then the \verb+timestep+ and \verb+finalise+
2503+methods should not be called after the \verb+finalise+ call.
2504+
2505+\subsection*{Examples}
2506+
2507+\begin{lstlisting}
2508+from dolfin import *
2509+from timestepping import *
2510+
2511+# Define a simple structured mesh on the unit interval
2512+mesh = UnitIntervalMesh(10)
2513+# P1 function space
2514+space = FunctionSpace(mesh, "CG", 1)
2515+
2516+# Model parameters and boundary conditions
2517+dt = StaticConstant(0.05)
2518+bc1 = StaticDirichletBC(space, 1.0,
2519+ "on_boundary && near(x[0], 0.0)")
2520+bc2 = StaticDirichletBC(space, 0.0,
2521+ "on_boundary && near(x[0], 1.0)")
2522+bcs = [bc1, bc2]
2523+nu = StaticConstant(0.01)
2524+
2525+# Define time levels
2526+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
2527+# A time dependent function
2528+u = TimeFunction(levels, space, name = "u")
2529+
2530+# Initialise a TimeSystem
2531+system = TimeSystem()
2532+
2533+# Add an initial assignment
2534+u_ic = StaticFunction(space, name = "u_ic")
2535+u_ic.assign(Constant(0.0))
2536+bc1.apply(u_ic.vector())
2537+system.add_solve(u_ic, u[0])
2538+# Register a simple diffusion equation, discretised in time
2539+# using forward Euler
2540+test = TestFunction(space)
2541+system.add_solve(
2542+ inner(test, (1.0 / dt) * (u[n + 1] - u[n])) * dx ==
2543+ -nu * inner(grad(test), grad(u[n])) * dx,
2544+ u[n + 1], bcs,
2545+ solver_parameters = {"linear_solver":"lu"})
2546+
2547+# Assemble the TimeSystem
2548+system = system.assemble()
2549+
2550+# Timestep the model
2551+t = 0.0
2552+while t * (1.0 + 1.0e-9) < 1.0:
2553+ system.timestep()
2554+ t += float(dt)
2555+# Finalise
2556+system.finalise()
2557+\end{lstlisting}
2558+
2559+\section{Adjoint modelling}
2560+
2561+Given a forward model described using a \verb+TimeSystem+, the associated
2562+discrete adjoint model can be derived in the analysis and optimisation step (see
2563+section \ref{sect:adjoint_assembly}). The resulting \verb+ManagedModel+
2564+returned can be used to perform a model constrained derivative calculation.
2565+
2566+\subsection{Checkpointing and storage}
2567+
2568+An adjoint model calculation requires knowledge of the forward model solution
2569+and, as a result, forward model data must be stored. It is often infeasible to
2570+store all required data in memory, and hence disk checkpointing and recovery
2571+must be used. In this procedure forward model data, sufficient to restart the
2572+forward model, are checkpointed periodically, and forward model data required
2573+between checkpoints are re-computed and stored. Forward model checkpointing and
2574+storage is configured in the analysis and optimisation step (see section
2575+\ref{sect:adjoint_assembly}).
2576+
2577+After the forward model finalisation stage (after the \linebreak
2578+\verb+ManagedModel.finalise+ call) stored forward model data can be verified via
2579+the \verb+verify_checkpoints+ method:
2580+\begin{lstlisting}
2581+asystem.verify_checkpoints(tolerance = tolerance)
2582+\end{lstlisting}
2583+where the optional \verb+tolerance+ argument is a positive float defining the
2584+tolerance used in comparisons. The \verb+verify_checkpoints+ method raises an
2585+exception on failure. For serial calculations the forward model data should be
2586+exactly reproducible, and verification should succeed with a tolerance of zero.
2587+For parallel calculations the verification may fail with a zero or small value
2588+for the tolerance. Note that the \verb+verify_checkpoints+ method re-runs the
2589+entire forward model, and hence this should only be used for debugging purposes.
2590+
2591+\subsection{Derivative calculation}
2592+
2593+A model constrained derivative can be computed via the \verb+compute_gradient+
2594+method. The high-level arguments for this method are:
2595+\begin{lstlisting}
2596+grad = asystem.compute_gradient(parameters = parameters,
2597+ project = project,
2598+ project_solver_parameters = project_solver_parameters)
2599+\end{lstlisting}
2600+where \verb+parameters+ is a \verb+dolfin.Constant+, a \verb+dolfin.Function+,
2601+or a list of \verb+dolfin.Constant+ or \verb+dolfin.Function+ objects, the
2602+optional \verb+project+ is a \linebreak boolean, and the optional
2603+\verb+project_solver_parameters+ is a dictionary of solver parameters. If
2604+\verb+parameters+ is a \verb+dolfin.Constant+ or \verb+dolfin.Function+ then it
2605+must have been declared as static (see section \ref{sect:statics}). If
2606+\verb+parameters+ is a list of \verb+dolfin.Constant+ or \verb+dolfin.Function+
2607+objects then all elements must have been declared as static. This method returns
2608+the model constrained derivative of the registered functional (see section
2609+\ref{sect:adjoint_assembly}). If \verb+parameters+ is a list, then the
2610+\verb+i+th element of the return value contains the derivative with respect to
2611+the \verb+i+th element of \verb+parameters+. The derivative with respect to a
2612+\verb+Constant+ parameter is returned as a \verb+Constant+. If \verb+project+ is
2613+\verb+False+ (the default) then the derivative with respect to a \verb+Function+
2614+parameter is returned as a \verb+GenericVector+. If \verb+project+ is
2615+\verb+True+ then the derivative with respect to a \verb+Function+ parameter is
2616+returned as a \verb+(GenericVector, Function)+ pair, where the \verb+Function+
2617+contains the derivative projected onto the parameter trial space, and where
2618+\verb+project_solver_parameters+ defines solver options for the associated mass
2619+matrix inversion.
2620+
2621+The \verb+compute_gradient+ method must be called after the forward model
2622+finalisation stage (after the \verb+ManagedModel.finalise+ call), and a
2623+functional must be registered.
2624+
2625+\subsection{Adjoint verification}
2626+
2627+Let $\vec{m}$ correspond to the degrees of freedom associated with a parameter,
2628+and define a functional
2629+$J \left( \vec{x} \left( \vec{m} \right), \vec{m} \right)$, where $\vec{x}$
2630+corresponds to the degrees of freedom associated with the forward model solution.
2631+Then:
2632+\begin{equation}
2633+ \left| J \left( \vec{x} \left( \vec{m} + \epsilon \vec{\delta m} \right), \vec{m} + \epsilon \vec{\delta m} \right)
2634+ - J \left( \vec{x} \left( \vec{m} \right), \vec{m} \right) \right|
2635+ = {\cal O} \epsilon,
2636+\end{equation}
2637+converges asymptotically at first order in perturbations of the parameter
2638+$\vec{m}$. However:
2639+\begin{equation}
2640+ \left| J \left( \vec{x} \left( \vec{m} + \epsilon \vec{\delta m} \right), \vec{m} + \epsilon \vec{\delta m} \right)
2641+ - J \left( \vec{x} \left( \vec{m} \right), \vec{m} \right)
2642+ - \epsilon \left< \frac{d J}{d \vec{m}}, \vec{\delta m} \right> \right|
2643+ = {\cal O} \epsilon^2,
2644+\end{equation}
2645+converges asymptotically at second order. Hence the result of a model
2646+constrained derivative calculation, yielding $d J / d \vec{m}$, can be verified
2647+using repeated forward model calculations, each with a small perturbation of the
2648+parameter $\vec{m}$.
2649+
2650+The \verb+taylor_test+ method performs such a verification. The high-level
2651+arguments for this method are:
2652+\begin{lstlisting}
2653+orders = asystem.taylor_test(parameter, grad = grad,
2654+ ntest = ntest, fact = fact)
2655+\end{lstlisting}
2656+\verb+parameter+ is a \verb+dolfin.Constant+ or \verb+dolfin.Function+ that has
2657+been declared as static (see section \ref{sect:statics}) and the optional
2658+\verb+grad+ is the corresponding derivative returned by the
2659+\verb+compute_gradient+ method. If \verb+grad+ is not supplied then the
2660+model constrained derivative is computed using the adjoint model. The optional
2661+\verb+ntest+ argument is the number of forward model calculations to perform in
2662+the verification. This must be greater than or equal to 2, and a default value
2663+of 6 is used if this is not supplied. The optional \verb+fact+ is a positive
2664+float and sets the magnitude of the perturbations. Let $N$ be the value of
2665+\verb+ntest+ and \verb+f+ be the value of \verb+fact+. If \verb+parameter+ is a
2666+\verb+dolfin.Constant+ with value $m$ then the perturbations applied are
2667+$\epsilon_i \delta m$, where:
2668+\begin{subequations}
2669+ \begin{equation}
2670+ \epsilon_i = 2^{N - i}, \quad i \in \left\{1, \ldots, N \right\},
2671+ \end{equation}
2672+ \begin{equation}
2673+ \delta m = \left\{ \begin{array}{l} f \left| m \right| \quad \textrm{if } \left| m \right| > 0 \\ f \qquad \quad \textrm{otherwise} \end{array} \right. .
2674+ \end{equation}
2675+\end{subequations}
2676+If \verb+parameter+ is a \verb+dolfin.Function+ with degrees of freedom
2677+with associated values $\vec{m}$ then the perturbations applies are
2678+$\epsilon_i \vec{\delta m}$, where:
2679+\begin{subequations}
2680+ \begin{equation}
2681+ \epsilon_i = 2^{N - i}, \quad i \in \left\{1, \ldots, N \right\},
2682+ \end{equation}
2683+ \begin{equation}
2684+ \vec{\delta m} = \left\{ \begin{array}{l} \vec{F} \left|\left| \vec{m} \right|\right|_\infty \quad \textrm{if } \left|\left| \vec{m} \right|\right|_\infty > 0 \\ \vec{F} \qquad \qquad \textrm{otherwise} \end{array} \right.
2685+ \end{equation}
2686+\end{subequations}
2687+where the values of $\vec{F}$ are randomly generated values in the range
2688+$\left[ -f/2, +f/2 \right)$, with a uniform probability distribution. If
2689+\verb+fact+ is not supplied then a default value of $10^{-4}$ is used.
2690+
2691+The \verb+taylor_test+ method returns a list of relative orders of
2692+convergence. The elements of the returned list have values:
2693+\begin{equation}
2694+ o_i = \textrm{log}_2 \frac{r_{i + 1}}{r_i},
2695+\end{equation}
2696+where:
2697+\begin{subequations}
2698+ \begin{equation}
2699+ \epsilon_i = 2^{N - i}, \quad i \in \left\{1, \ldots, N \right\},
2700+ \end{equation}
2701+ \begin{equation}
2702+ r_i = \left| J \left( \vec{x} \left( \vec{m} + \epsilon_i \vec{\delta m} \right), \vec{m} + \epsilon_i \vec{\delta m} \right)
2703+ - J \left( \vec{x} \left( \vec{m} \right), \vec{m} \right)
2704+ - \epsilon_i \left< \frac{d J}{d \vec{m}}, \vec{\delta m} \right> \right|.
2705+ \end{equation}
2706+\end{subequations}
2707+The elements of the returned list should be close to 2 in a successful
2708+verification.
2709+
2710+It is recommended that new adjoint models should be verified using a Taylor
2711+remainder convergence test. Since the test involves repeated running of the
2712+forward model it may be necessary to test the adjoint on a small problem,
2713+for example using a reduced number of model timesteps. The Taylor remainder
2714+convergence test may fail if:
2715+\begin{enumerate}
2716+ \item The perturbations are too large, meaning that the asymptotic convergence
2717+ of the remainder is not observed.
2718+ \item The perturbations are too small, meaning that the convergence test is
2719+ polluted by machine precision errors.
2720+ \item The forward model is not differentiable, meaning that a model
2721+ constrained derivative does not exists.
2722+ \item Iterative solver tolerances are too large.
2723+ \item The adjoint model is inconsistent with the forward model.
2724+\end{enumerate}
2725+The first two cases can be avoided by decreasing or increasing \verb+fact+
2726+(assuming that these two cases do not both apply simultaneously, in which
2727+case a Taylor remainder convergence test cannot be used). The final case may
2728+occur if forward model data is modified manually during the model execution, or
2729+may indicate a bug in the \verb+timestepping+ library.
2730+
2731+\subsection*{Examples}
2732+
2733+\begin{lstlisting}
2734+from dolfin import *
2735+from timestepping import *
2736+
2737+# Define a simple structured mesh on the unit interval
2738+mesh = UnitIntervalMesh(10)
2739+# P1 function space
2740+space = FunctionSpace(mesh, "CG", 1)
2741+
2742+# Model parameters and boundary conditions
2743+dt = StaticConstant(0.05)
2744+bc1 = StaticDirichletBC(space, 1.0,
2745+ "on_boundary && near(x[0], 0.0)")
2746+bc2 = StaticDirichletBC(space, 0.0,
2747+ "on_boundary && near(x[0], 1.0)")
2748+bcs = [bc1, bc2]
2749+nu = StaticConstant(0.01)
2750+
2751+# Define time levels
2752+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
2753+# A time dependent function
2754+u = TimeFunction(levels, space, name = "u")
2755+
2756+# Initialise a TimeSystem
2757+system = TimeSystem()
2758+
2759+# Add an initial assignment
2760+u_ic = StaticFunction(space, name = "u_ic")
2761+u_ic.assign(Constant(0.0))
2762+bc1.apply(u_ic.vector())
2763+system.add_solve(u_ic, u[0])
2764+# Register a simple diffusion equation, discretised in time
2765+# using forward Euler
2766+test = TestFunction(space)
2767+system.add_solve(
2768+ inner(test, (1.0 / dt) * (u[n + 1] - u[n])) * dx ==
2769+ -nu * inner(grad(test), grad(u[n])) * dx,
2770+ u[n + 1], bcs,
2771+ solver_parameters = {"linear_solver":"lu"})
2772+
2773+# Assemble the TimeSystem, enabling the adjoint. Set the
2774+# functional to be equal to spatial integral of the final u.
2775+system = system.assemble(adjoint = True, functional = u[N] * dx)
2776+
2777+# Timestep the model
2778+t = 0.0
2779+while t * (1.0 + 1.0e-9) < 1.0:
2780+ system.timestep()
2781+ t += float(dt)
2782+# Finalise
2783+system.finalise()
2784+
2785+# Perform a model constrained derivative calculation
2786+dJdm = system.compute_gradient(nu)
2787+
2788+# Verify the stored forward model data
2789+system.verify_checkpoints()
2790+# Verify the computed derivative using a Taylor remainder
2791+# convergence test
2792+orders = system.taylor_test(nu, grad = dJdm)
2793+# Check the convergence order
2794+assert((orders > 1.99).all())
2795+\end{lstlisting}
2796+
2797+\chapter{Time discretisation examples}
2798+
2799+This chapter describes the implementation of a number of simple time
2800+discretisations using the \verb+timestepping+ library. The provided examples are
2801+largely illustrative, and may not be stable.
2802+
2803+All examples in this chapter consider
2804+the discretisation of the following system:
2805+\begin{subequations}
2806+ \begin{equation}\label{eqn:t_timestep}
2807+ \partial_t T = F(T),
2808+ \end{equation}
2809+ \begin{equation}\label{eqn:t_init}
2810+ \left. T \right|_{t = 0} = T_0.
2811+ \end{equation}
2812+\end{subequations}
2813+It is assumed that $T_0$ is defined using a DOLFIN variable \verb+T_0+, which
2814+may for example be a \verb+dolfin.Constant+ or \verb+dolfin.Function+. It is
2815+further assumed that some Python function for approximating
2816+$\int_\Omega \phi_i F(T)$ over the space $\Omega$ and with test functions
2817+$\phi_i$ is provided, with the following form:
2818+\begin{lstlisting}
2819+def F(test, T):
2820+ rhs = ... # Insert definition here
2821+ return rhs
2822+\end{lstlisting}
2823+where \verb+test+ is a spatial test function.
2824+
2825+\section{Adams-Bashforth}
2826+
2827+Adams-Bashforth schemes take the form:
2828+\begin{equation}
2829+ T^{n + 1} = T^n + \Delta t \sum_{i = 1}^N \gamma_i F( T^{n + 1 - i}),
2830+\end{equation}
2831+where the $\gamma_i$ are constants. Adams-Bashforth schemes therefore require
2832+the values of $F(T)$ from $N$ previous time levels. Given a choice of $N$, the
2833+$\gamma_i$ are chosen so as to yield $N$th order accuracy. If $N > 1$ then the
2834+scheme is not self starting, and care is required when initialising in order to
2835+maintain the accuracy of the method. Further details of Adams-Bashforth schemes
2836+can be found in \citet[section 2.1]{iserles2009} and
2837+\citet[section III.1]{hairer1987}.
2838+
2839+\subsection{Forward Euler}\label{sect:fe}
2840+
2841+For $N = 1$ the single coefficient $\gamma_1$ is chosen to be:
2842+\begin{equation}
2843+ \gamma_1 = 1,
2844+\end{equation}
2845+yielding:
2846+\begin{equation}
2847+ T^{n + 1} = T^n + \Delta t F(T^n).
2848+\end{equation}
2849+This is the forward Euler scheme, and has first order accuracy.
2850+
2851+This discretisation can be implemented via:
2852+\begin{lstlisting}
2853+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
2854+levels_dT = TimeLevels(levels = [n], cycle_map = {},
2855+ last_past_level = n - 1)
2856+T = TimeFunction(levels, space, name = "T")
2857+dT = TimeFunction(levels_dT, space, name = "dT")
2858+
2859+system = TimeSystem()
2860+
2861+system.add_solve(T_ic, T[0])
2862+
2863+system.add_solve(inner(test, dT[n]) * dx == dt * F(test, T[n]),
2864+ dT[n], solver_parameters = solver_parameters)
2865+system.add_solve(LinearCombination((1.0, T[n]), (1.0, dT[n])),
2866+ T[n + 1])
2867+\end{lstlisting}
2868+where \verb+space+ defines the function space for $T$, \verb+dt+ defines the
2869+timestep size, and \verb+solver_parameters+ is a dictionary of solver
2870+parameters.
2871+
2872+\subsection{Second order Adams-Bashforth}\label{sect:ab2}
2873+
2874+For $N = 2$ the $\gamma_i$ are chosen to be:
2875+\begin{align}
2876+ \gamma_1 = \frac{3}{2}, & & \gamma_2 = -\frac{1}{2},
2877+\end{align}
2878+yielding:
2879+\begin{equation}
2880+ T^{n + 1} = T^n + \Delta t \left[ \frac{3}{2} F(T^n) - \frac{1}{2} F(T^{n - 1}) \right].
2881+\end{equation}
2882+This scheme cannot be used to perform the first model timestep as $T^{-1}$ is
2883+not available. In order to ensure global second order accuracy in time the
2884+method used to perform the first timestep, and compute $T^1$, must be at least
2885+first order accurate. Hence, for example, a single forward Euler step
2886+\ref{sect:fe} can be applied:
2887+\begin{equation}
2888+ T^1 = T^0 + \Delta t F(T^0).
2889+\end{equation}
2890+
2891+This discretisation can be implemented via:
2892+\begin{lstlisting}
2893+levels = TimeLevels(levels = [n, n + 1, n + 2],
2894+ cycle_map = {n:n + 1, n + 1:n + 2}, last_past_level = n + 1)
2895+levels_dT = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1},
2896+ last_past_level = n)
2897+T = TimeFunction(levels, space, name = "T")
2898+dT = TimeFunction(levels_dT, space, name = "dT")
2899+
2900+system = TimeSystem()
2901+
2902+system.add_solve(T_ic, T[0])
2903+
2904+system.add_solve(inner(test, dT[0]) * dx == dt * F(test, T[0]),
2905+ dT[0], solver_parameters = solver_parameters)
2906+system.add_solve(LinearCombination((1.0, T[0]),
2907+ (1.0, dT[0])),
2908+ T[1])
2909+
2910+system.add_solve(inner(test, dT[n + 1]) * dx ==
2911+ dt * F(test, T[n + 1]),
2912+ dT[n + 1], solver_parameters = solver_parameters)
2913+system.add_solve(LinearCombination((1.0, T[n + 1]),
2914+ (3.0 / 2.0, dT[n + 1]),
2915+ (-1.0 / 2.0, dT[n])),
2916+ T[n + 2])
2917+\end{lstlisting}
2918+At the end of the simulation the final model solution is stored in
2919+\verb=T[N + 1]=.
2920+
2921+\subsection{Third order Adams-Bashforth}
2922+
2923+For $N = 3$ the $\gamma_i$ are chosen to be:
2924+\begin{align}
2925+ \gamma_1 = \frac{23}{12}, & & \gamma_2 = -\frac{4}{3}, & & \gamma_3 = \frac{5}{12},
2926+\end{align}
2927+yielding:
2928+\begin{equation}
2929+ T^{n + 1} = T^n + \Delta t \left[ \frac{23}{12} F(T^n) - \frac{4}{3} F(T^{n - 1}) + \frac{5}{12} F(T^{n - 2}) \right].
2930+\end{equation}
2931+This scheme cannot be used to perform the first two model timesteps as $T^{-1}$
2932+and $T^{-2}$ are not available. In order to ensure global third order accuracy
2933+in time the method used to perform the first two timesteps, and compute $T^0$
2934+and $T^1$, must be at least second order accurate. Hence, for example, second
2935+order Runge-Kutta \ref{sect:rk2} and second order Adams-Bashforth \ref{sect:ab2}
2936+steps can be applied:
2937+\begin{subequations}
2938+ \begin{equation}
2939+ T^{\frac{1}{2}} = T^0 + \frac{1}{2} \Delta t F(T^0).
2940+ \end{equation}
2941+ \begin{equation}
2942+ T^1 = T^0 + \Delta t F(T^{\frac{1}{2}}).
2943+ \end{equation}
2944+ \begin{equation}
2945+ T^2 = T^1 + \Delta t \left[ \frac{3}{2} F(T^1) - \frac{1}{2} F(T^0) \right].
2946+ \end{equation}
2947+\end{subequations}
2948+
2949+This discretisation can be implemented via:
2950+\begin{lstlisting}
2951+from fractions import Fraction
2952+hf = Fraction(1, 2)
2953+
2954+levels = TimeLevels(levels = [n, n + hf, n + 1, n + 2, n + 3],
2955+ cycle_map = {n:n + 1, n + 1:n + 2, n + 2:n + 3},
2956+ last_past_level = n + 2)
2957+levels_dT = TimeLevels(levels = [n, n + hf, n + 1, n + 2],
2958+ cycle_map = {n:n + 1, n + 1:n + 2}, last_past_level = n + 1)
2959+T = TimeFunction(levels, space, name = "T")
2960+dT = TimeFunction(levels_dT, space, name = "dT")
2961+
2962+system = TimeSystem()
2963+
2964+system.add_solve(T_ic, T[0])
2965+
2966+system.add_solve(inner(test, dT[0]) * dx == dt * F(test, T[0]),
2967+ dT[0], solver_parameters = solver_parameters)
2968+system.add_solve(LinearCombination((1.0, T[0]),
2969+ (0.5, dT[0])),
2970+ T[hf])
2971+
2972+system.add_solve(inner(test, dT[hf]) * dx == dt * F(test, T[hf]),
2973+ dT[hf], solver_parameters = solver_parameters)
2974+system.add_solve(LinearCombination((1.0, T[0]),
2975+ (1.0, dT[hf])),
2976+ T[1])
2977+
2978+system.add_solve(inner(test, dT[1]) * dx == dt * F(test, T[1]),
2979+ dT[1], solver_parameters = solver_parameters)
2980+system.add_solve(LinearCombination((1.0, T[1]),
2981+ (3.0 / 2.0, dT[1]),
2982+ (-1.0 / 2.0, dT[0])),
2983+ T[2])
2984+
2985+system.add_solve(inner(test, dT[n + 2]) * dx ==
2986+ dt * F(test, T[n + 2]),
2987+ dT[n + 2], solver_parameters = solver_parameters)
2988+system.add_solve(LinearCombination((1.0, T[n + 2]),
2989+ (23.0 / 12.0, dT[n + 2]),
2990+ (-4.0 / 3.0, dT[n + 1]),
2991+ (5.0 / 12.0, dT[n])),
2992+ T[n + 3])
2993+\end{lstlisting}
2994+At the end of the simulation the final model solution is stored in
2995+\verb=T[N + 2]=.
2996+
2997+\section{Leapfrog}
2998+
2999+A centred discretisation in time leads to:
3000+\begin{equation}
3001+ T^{n + 1} = T^{n - 1} + 2 \Delta t F( T^n ).
3002+\end{equation}
3003+This method is also known as the leapfrog scheme, and has second order accuracy.
3004+This scheme cannot be used to perform the first model timestep as $T^{-1}$ is
3005+not available. In order to ensure global second order accuracy in time the
3006+method used to perform the first timestep, and compute $T^1$, must be at least
3007+first order accurate. Hence, for example, a single forward Euler step
3008+\ref{sect:fe} can be applied:
3009+\begin{equation}
3010+ T^1 = T^0 + \Delta t F(T^0).
3011+\end{equation}
3012+
3013+This discretisation can be implemented via:
3014+\begin{lstlisting}
3015+levels = TimeLevels(levels = [n, n + 1, n + 2],
3016+ cycle_map = {n:n + 1, n + 1:n + 2}, last_past_level = n + 1)
3017+levels_dT = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1},
3018+ last_past_level = n)
3019+T = TimeFunction(levels, space, name = "T")
3020+dT = TimeFunction(levels_dT, space, name = "dT")
3021+
3022+system = TimeSystem()
3023+
3024+system.add_solve(T_ic, T[0])
3025+
3026+system.add_solve(inner(test, dT[0]) * dx == dt * F(test, T[0]),
3027+ dT[0], solver_parameters = solver_parameters)
3028+system.add_solve(LinearCombination((1.0, T[0]),
3029+ (1.0, dT[0])),
3030+ T[1])
3031+
3032+system.add_solve(inner(test, dT[n + 1]) * dx ==
3033+ dt * F(test, T[n + 1]),
3034+ dT[n + 1], solver_parameters = solver_parameters)
3035+system.add_solve(LinearCombination((1.0, T[n]),
3036+ (2.0, dT[n + 1])),
3037+ T[n + 2])
3038+\end{lstlisting}
3039+At the end of the simulation the final model solution is stored in
3040+\verb=T[N + 1]=.
3041+
3042+\section{Adams-Moulton}
3043+
3044+Adams-Moulton schemes take the form:
3045+\begin{equation}\label{eqn:adams_moulton}
3046+ T^{n + 1} = T^n + \Delta t \sum_{i = 0}^N \gamma_i F( T^{n + 1 - i}),
3047+\end{equation}
3048+where the $\gamma_i$ are constants. For $n > 1$ Adams-Moulton schemes therefore
3049+require the values of $F(T)$ from $N - 1$ previous time levels. Given a choice
3050+of $N$, the $\gamma_i$ are chosen so as to yield $N$th order accuracy. If
3051+$N > 2$ then the scheme is not self starting, and care is required when
3052+initialising in order to maintain the accuracy of the method. Further details of
3053+Adams-Moulton schemes can be found in \citet[section III.1]{hairer1987}.
3054+
3055+In an Adams-Moulton scheme the right-hand-side of equation
3056+\eqref{eqn:adams_moulton} depends implicitly on the future solution $T^{n + 1}$.
3057+If $F(T)$ is linear then the implicit term contributes to the left-hand-side
3058+matrix in a linear solve for $T^{n + 1}$. If $F(T)$ is non-linear then
3059+a non-linear equation for $T^{n + 1}$ must be solved.
3060+
3061+\subsection{Backward Euler}\label{sect:be}
3062+
3063+For $N = 0$ the single coefficient $\gamma_0$ is chosen to be:
3064+\begin{equation}
3065+ \gamma_0 = 1,
3066+\end{equation}
3067+yielding:
3068+\begin{equation}
3069+ T^{n + 1} = T^n + \Delta t F(T^{n + 1}).
3070+\end{equation}
3071+This is the backward Euler scheme, and has first order accuracy.
3072+
3073+This discretisation can be implemented via:
3074+\begin{lstlisting}
3075+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
3076+T = TimeFunction(levels, space, name = "T")
3077+
3078+system = TimeSystem()
3079+
3080+system.add_solve(T_ic, T[0])
3081+
3082+system.add_solve(inner(test, T[n + 1]) * dx ==
3083+ inner(test, T[n]) * dx + dt * F(test, T[n + 1]),
3084+ T[n + 1], solver_parameters = solver_parameters)
3085+\end{lstlisting}
3086+Note that \verb+solver_parameters+ should define Newton solver parameters if
3087+$F(T)$ is non-linear.
3088+
3089+\subsection{Implicit trapezium rule}\label{sect:implicit_trapezium}
3090+
3091+For $N = 1$ the $\gamma_i$ are chosen to be:
3092+\begin{align}
3093+ \gamma_0 = \frac{1}{2}, & & \gamma_1 = \frac{1}{2},
3094+\end{align}
3095+yielding:
3096+\begin{equation}
3097+ T^{n + 1} = T^n + \Delta t \left[ \frac{1}{2} F(T^{n + 1}) + \frac{1}{2} F(T^n) \right].
3098+\end{equation}
3099+This is the implicit trapezium rule, and has second order accuracy. If $F(T)$ is
3100+linear then this scheme is identical to the implicit midpoint rule
3101+\ref{sect:implicit_midpoint}. This method is also known as the Crank-Nicolson
3102+scheme \citep[e.g.][section 3.4]{donea2003}, (although, confusingly,
3103+``Crank-Nicolson'' can refer to the implicit midpoint rule).
3104+
3105+This discretisation can be implemented via:
3106+\begin{lstlisting}
3107+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
3108+T = TimeFunction(levels, space, name = "T")
3109+
3110+system = TimeSystem()
3111+
3112+system.add_solve(T_ic, T[0])
3113+
3114+system.add_solve(inner(test, T[n + 1]) * dx ==
3115+ inner(test, T[n]) * dx +
3116+ dt * (0.5 * F(test, T[n + 1]) + 0.5 * F(test, T[n])),
3117+ T[n + 1], solver_parameters = solver_parameters)
3118+\end{lstlisting}
3119+Note that \verb+solver_parameters+ should define Newton solver parameters if
3120+$F(T)$ is non-linear.
3121+
3122+\section{Implicit midpoint rule}\label{sect:implicit_midpoint}.
3123+
3124+The implicit midpoint rule takes the form:
3125+\begin{equation}
3126+ T^{n + 1} = T^n + \Delta t F \left( \frac{1}{2} T^{n + 1} + \frac{1}{2} T^n \right).
3127+\end{equation}
3128+This has second order accuracy. If $F(T)$ is linear then this scheme is
3129+identical to the implicit trapezium rule \ref{sect:implicit_trapezium}. This
3130+method is also known as the Crank-Nicolson scheme
3131+\citep[e.g.][section 2.22]{mitchell1980} (although, confusingly,
3132+``Crank-Nicolson'' can refer to the implicit trapezium rule).
3133+
3134+This discretisation can be implemented via:
3135+\begin{lstlisting}
3136+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
3137+T = TimeFunction(levels, space, name = "T")
3138+
3139+system = TimeSystem()
3140+
3141+system.add_solve(T_ic, T[0])
3142+
3143+system.add_solve(inner(test, T[n + 1]) * dx ==
3144+ inner(test, T[n]) * dx +
3145+ dt * F(test, 0.5 * T[n + 1] + 0.5 * T[n]),
3146+ T[n + 1], solver_parameters = solver_parameters)
3147+\end{lstlisting}
3148+Note that \verb+solver_parameters+ should define Newton solver parameters if
3149+$F(T)$ is non-linear.
3150+
3151+\section{Explicit Runge-Kutta}
3152+
3153+Runge-Kutta schemes are a class of multi-stage integration schemes. In the
3154+context of equation \eqref{eqn:t_timestep} an $S$-stage explicit Runge-Kutta
3155+scheme takes the form:
3156+\begin{subequations}
3157+ \begin{equation}
3158+ F^{n + 1,i} = F \left( T^n + \Delta t \sum_{j = 1}^{i - 1} \gamma_{ij} F^{n + 1,j} \right) \quad \textrm{ for } i \in \left\{ 1, \ldots, S \right\},
3159+ \end{equation}
3160+ \begin{equation}
3161+ T^{n + 1} = T^n + \Delta t \sum_{j = 1}^S b_j F^{n + 1,j}.
3162+ \end{equation}
3163+\end{subequations}
3164+The $\gamma_{ij}$ and $b_j$ are chosen so as to yield desired accuracy and
3165+stability properties. Further details of explicit Runge-Kutta schemes can be
3166+found in \citet[section 3.2]{iserles2009}.
3167+
3168+\subsection{First order explicit Runge-Kutta}
3169+
3170+The one stage first order explicit Runge-Kutta scheme is:
3171+\begin{subequations}
3172+ \begin{equation}
3173+ F^{n + 1,1} = F \left( T^n \right),
3174+ \end{equation}
3175+ \begin{equation}
3176+ T^{n + 1} = T^n + \Delta t F^{n + 1,1}.
3177+ \end{equation}
3178+\end{subequations}
3179+This is the forward Euler scheme \ref{sect:fe}.
3180+
3181+\subsection{Second order explicit Runge-Kutta}\label{sect:rk2}
3182+
3183+A simple second order explicit Runge-Kutta scheme takes the form:
3184+\begin{subequations}
3185+ \begin{equation}
3186+ F^{n + 1,1} = F \left( T^n \right),
3187+ \end{equation}
3188+ \begin{equation}
3189+ F^{n + 1,2} = F \left( T^n + \Delta t \frac{1}{2} F^{n + 1,1} \right),
3190+ \end{equation}
3191+ \begin{equation}
3192+ T^{n + 1} = T^n + \Delta t F^{n + 1,2}.
3193+ \end{equation}
3194+\end{subequations}
3195+
3196+This discretisation can be implemented via:
3197+\begin{lstlisting}
3198+from fractions import Fraction
3199+hf = Fraction(1, 2)
3200+
3201+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
3202+levels_F = TimeLevels(levels = [n, n + hf], cycle_map = {},
3203+ last_past_level = n - hf)
3204+T = TimeFunction(levels, space, name = "T")
3205+F_s = TimeFunction(levels_F, space, name = "F_s")
3206+
3207+system = TimeSystem()
3208+
3209+system.add_solve(T_ic, T[0])
3210+
3211+system.add_solve(inner(test, F_s[n]) * dx == F(test, T[n]),
3212+ F_s[n], solver_parameters = solver_parameters)
3213+system.add_solve(inner(test, F_s[n + hf]) * dx ==
3214+ F(test, T[n] + 0.5 * dt * F_s[n]),
3215+ F_s[n + hf], solver_parameters = solver_parameters)
3216+
3217+system.add_solve(LinearCombination((1.0, T[n]),
3218+ (dt, F_s[n + hf])),
3219+ T[n + 1])
3220+\end{lstlisting}
3221+
3222+\section{Discontinuous Galerkin Finite Element}
3223+
3224+Let a time interval $(0, T)$ be divided into a series of $N$ elements
3225+$( (n - 1) \Delta t, n \Delta t )$,
3226+$n \in \left\{ 1, \ldots, N \right\}$. Equip this one dimensional mesh with
3227+discontinuous Lagrange basis functions, thus defining a discrete function
3228+space $V^\delta \in L^2$ on $(0, T)$. A temporal discontinuous Galerkin
3229+discretisation of equation \eqref{eqn:t_timestep} yields:
3230+\begin{align}\label{eqn:t_timestep_dg}
3231+ \left[ \phi^\delta T^{\delta,-} \right]_{(n - 1) \Delta t}^{n \Delta t}
3232+ - \int_{(n - 1) \Delta t}^{n \Delta t} d_t \phi^\delta T^\delta dt
3233+ = \int_{(n - 1) \Delta t}^{n \Delta t} \phi^\delta F \left( T^\delta \right) dt \nonumber \\
3234+ \forall \phi^\delta \in V^\delta, \quad \forall n \in \left\{ 1, \ldots, N \right\},
3235+\end{align}
3236+where $T^\delta \in V^\delta$ and
3237+$T^{\delta,-} = \lim_{t \rightarrow \left[ (n - 1) \Delta t \right]^{-}} T^{\delta}$.
3238+The time derivative has been integrated by parts and an ``upwind flux''
3239+approximation applied.
3240+
3241+\subsection{$P0$}
3242+
3243+Consider discontinuous Lagrange elements of degree zero. Introduce basis
3244+functions:
3245+\begin{equation}
3246+ \phi^n = \left\{ \begin{array}{l} 1 \quad \textrm{ if } t \in \left( (n - 1) \Delta t, n \Delta t \right) \\
3247+ 0 \quad \textrm{ otherwise}\end{array} \right. ,
3248+\end{equation}
3249+and let $T^\delta = \sum_{n = 1}^N T^n \phi^n$. Then equation
3250+\eqref{eqn:t_timestep_dg} leads to:
3251+\begin{equation}
3252+ T^{n + 1} - T^n
3253+ = \int_{n \Delta t}^{(n + 1) \Delta t} \phi^{n + 1} F \left( T^\delta \right) dt \quad \forall n \in \left\{ 0, \ldots, N - 1 \right\}.
3254+\end{equation}
3255+Application of the midpoint rule to the right-hand-side yields:
3256+\begin{equation}
3257+ T^{n + 1} - T^n = \Delta t F \left( T^{n + 1} \right).
3258+\end{equation}
3259+This is the backward Euler scheme \ref{sect:be}, and has first order accuracy
3260+in time.
3261+
3262+\subsection{$P1_{DG}$}
3263+
3264+Consider discontinuous Lagrange elements of degree one. Introduce basis
3265+functions:
3266+\begin{subequations}
3267+ \begin{align}
3268+ \phi^{n,1} & = \left\{ \begin{array}{l} 1 - \frac{n \Delta t - t}{\Delta t} \\ 0 \end{array}\begin{array}{l} \textrm{ if } t \in \left( (n - 1) \Delta t, n \Delta t \right) \\ \textrm{ otherwise} \end{array} \right. , \\
3269+ \phi^{n,2} & = \left\{ \begin{array}{l} \frac{t - (n - 1) \Delta t}{\Delta t} \\ 0 \end{array}\begin{array}{l} \textrm{ if } t \in \left( (n - 1) \Delta t, n \Delta t \right) \\ \textrm{ otherwise} \end{array} \right. ,
3270+ \end{align}
3271+\end{subequations}
3272+and let $T^\delta = \sum_{n = 1}^N T^{n,1} \phi^{n,1} + T^{n,2} \phi^{n,2}$.
3273+Then equation \eqref{eqn:t_timestep_dg} leads to:
3274+\begin{subequations}
3275+ \begin{align}
3276+ -T^{n,2} + \frac{1}{2} \left( T^{n + 1,1} + T^{n + 1,2} \right)
3277+ = \int_{n \Delta t}^{(n + 1) \Delta t} \phi^{n + 1,1} F \left( T^\delta \right) dt \nonumber \\ \forall n \in \left\{ 0, \ldots, N - 1 \right\},
3278+ \end{align}
3279+ \begin{align}
3280+ T^{n + 1,2} - \frac{1}{2} \left( T^{n + 1,1} + T^{n + 1,2} \right)
3281+ = \int_{n \Delta t}^{(n + 1) \Delta t} \phi^{n + 1,2} F \left( T^\delta \right) dt \nonumber \\ \forall n \in \left\{ 0, \ldots, N - 1 \right\},
3282+ \end{align}
3283+\end{subequations}
3284+The right-hand-sides can be integrated to second order accuracy in time via
3285+application of the trapezium rule to yield:
3286+\begin{subequations}
3287+ \begin{align}
3288+ -T^{n,2} + \frac{1}{2} \left( T^{n + 1,1} + T^{n + 1,2} \right)
3289+ = \Delta t \left[ \frac{1}{3} F \left( T^{n + 1,1} \right) + \frac{1}{6} F \left( T^{n + 1,2} \right) \right] \nonumber \\ \forall n \in \left\{ 0, \ldots, N - 1 \right\},
3290+ \end{align}
3291+ \begin{align}
3292+ T^{n + 1,2} - \frac{1}{2} \left( T^{n + 1,1} + T^{n + 1,2} \right)
3293+ = \Delta t \left[ \frac{1}{6} F \left( T^{n + 1,1} \right) + \frac{1}{3} F \left( T^{n + 1,2} \right) \right] \nonumber \\ \forall n \in \left\{ 0, \ldots, N - 1 \right\},
3294+ \end{align}
3295+\end{subequations}
3296+The complete discretisation, including the initial condition, becomes:
3297+\begin{subequations}
3298+ \begin{align}
3299+ \frac{1}{2} T^{n + 1,2} + \frac{1}{2} T^{n + 1,1} - T^{n,2}
3300+ = \Delta t \left[ \frac{1}{3} F \left( T^{n + 1,1} \right) + \frac{1}{6} F \left( T^{n + 1,2} \right) \right] \nonumber \\ \forall n \in \left\{ 0, \ldots, N - 1 \right\},
3301+ \end{align}
3302+ \begin{align}
3303+ \frac{1}{2} T^{n + 1,2} - \frac{1}{2} T^{n + 1,1}
3304+ = \Delta t \left[ \frac{1}{6} F \left( T^{n + 1,1} \right) + \frac{1}{3} F \left( T^{n + 1,2} \right) \right] \nonumber \\ \forall n \in \left\{ 0, \ldots, N - 1 \right\},
3305+ \end{align}
3306+ \begin{equation}
3307+ T^{0,2} = T_0.
3308+ \end{equation}
3309+\end{subequations}
3310+Each timestep involves the implicit solution for two new values, $T^{n + 1,1}$
3311+and $T^{n + 1,2}$, given one previous value, $T^{n,2}$.
3312+
3313+This discretisation can be implemented via:
3314+\begin{lstlisting}
3315+spaces = space * space
3316+tests = TestFunction(spaces)
3317+test1, test2 = split(tests)
3318+
3319+levels = TimeLevels(levels = [n, n + 1], cycle_map = {n:n + 1})
3320+T = TimeFunction(levels, spaces, name = "T")
3321+
3322+system = TimeSystem()
3323+
3324+system.add_solve(inner(tests, T[0]) * dx == inner(test2, T_ic) * dx,
3325+ T[0], solver_parameters = solver_parameters)
3326+
3327+m_11 = m_22 = 1.0 / 3.0
3328+m_12 = m_21 = 1.0 / 6.0
3329+system.add_solve(
3330+ inner(test1, 0.5 * T[n + 1][1] + 0.5 * T[n + 1][0] - T[n][1])*dx +
3331+ inner(test2, 0.5 * T[n + 1][1] - 0.5 * T[n + 1][0]) * dx ==
3332+ dt*(m_11 * F(test1, T[n + 1][0]) + m_12 * F(test1, T[n + 1][1]) +
3333+ m_21 * F(test2, T[n + 1][0]) + m_22 * F(test2, T[n + 1][1])),
3334+ T[n + 1], solver_parameters = {"linear_solver":"lu"})
3335+\end{lstlisting}
3336+Note that \verb+solver_parameters+ should define Newton solver parameters if
3337+$F(T)$ is non-linear.
3338+
3339+\bibliography{bibliography}
3340+\bibliographystyle{plainnat}
3341+
3342+\end{document}
3343
3344=== added directory 'timestepping/python'
3345=== added directory 'timestepping/python/dolfin_adjoint_timestepping'
3346=== added file 'timestepping/python/dolfin_adjoint_timestepping/__init__.py'
3347--- timestepping/python/dolfin_adjoint_timestepping/__init__.py 1970-01-01 00:00:00 +0000
3348+++ timestepping/python/dolfin_adjoint_timestepping/__init__.py 2013-05-15 14:57:23 +0000
3349@@ -0,0 +1,51 @@
3350+#!/usr/bin/env python
3351+
3352+# Copyright (C) 2011-2012 by Imperial College London
3353+# Copyright (C) 2013 University of Oxford
3354+#
3355+# This program is free software: you can redistribute it and/or modify
3356+# it under the terms of the GNU Lesser General Public License as published by
3357+# the Free Software Foundation, version 3 of the License
3358+#
3359+# This program is distributed in the hope that it will be useful,
3360+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3361+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3362+# GNU Lesser General Public License for more details.
3363+#
3364+# You should have received a copy of the GNU Lesser General Public License
3365+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3366+
3367+import dolfin_adjoint
3368+import dolfin_adjoint_timestepping
3369+
3370+import timestepping
3371+
3372+from timestepping import *
3373+from dolfin_adjoint_timestepping import *
3374+
3375+__doc__ = \
3376+"""
3377+A timestepping abstraction and automated adjoining library. This library
3378+utilises the FEniCS system for symbolic manipulation and automated code
3379+generation, and supplements this system with a syntax for the description of
3380+timestepping finite element models.
3381+
3382+This version of the library integrates with dolfin-adjoint.
3383+"""
3384+
3385+__license__ = "LGPL-3"
3386+
3387+__version__ = "1.2.0"
3388+
3389+__all__ = \
3390+ dolfin_adjoint_timestepping.__all__ + \
3391+ [
3392+ "__doc__",
3393+ "__license__",
3394+ "__name__",
3395+ "__version__",
3396+ "dolfin_adjoint",
3397+ "dolfin_adjoint_timestepping",
3398+ "timestepping"
3399+ ]
3400+
3401\ No newline at end of file
3402
3403=== added file 'timestepping/python/dolfin_adjoint_timestepping/dolfin_adjoint_timestepping.py'
3404--- timestepping/python/dolfin_adjoint_timestepping/dolfin_adjoint_timestepping.py 1970-01-01 00:00:00 +0000
3405+++ timestepping/python/dolfin_adjoint_timestepping/dolfin_adjoint_timestepping.py 2013-05-15 14:57:23 +0000
3406@@ -0,0 +1,439 @@
3407+#!/usr/bin/env python
3408+
3409+# Copyright (C) 2011-2012 by Imperial College London
3410+# Copyright (C) 2013 University of Oxford
3411+#
3412+# This program is free software: you can redistribute it and/or modify
3413+# it under the terms of the GNU Lesser General Public License as published by
3414+# the Free Software Foundation, version 3 of the License
3415+#
3416+# This program is distributed in the hope that it will be useful,
3417+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3418+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3419+# GNU Lesser General Public License for more details.
3420+#
3421+# You should have received a copy of the GNU Lesser General Public License
3422+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3423+
3424+# Based on code from dolfin-adjoint bzr trunk 640
3425+# Code first added: 13/05/05
3426+
3427+import copy
3428+import types
3429+
3430+import dolfin
3431+import dolfin_adjoint
3432+import libadjoint
3433+import ufl
3434+
3435+import timestepping
3436+
3437+__all__ = timestepping.__all__ + dir(dolfin_adjoint)
3438+for val in copy.copy(__all__):
3439+ if val.startswith("__") and val.endswith("__"):
3440+ __all__.remove(val)
3441+
3442+from timestepping import *
3443+from dolfin_adjoint import *
3444+
3445+def system_info():
3446+ """
3447+ Print system information and assorted library versions.
3448+ """
3449+
3450+ timestepping.system_info()
3451+ dolfin.info("dolfin-adjoint version: %s" % dolfin_adjoint.__version__)
3452+
3453+ return
3454+
3455+dolfin.parameters["timestepping"]["pre_assembly"]["linear_forms"]["whole_form_optimisation"] = True
3456+
3457+# dolfin-adjoint internals expect Constant s and Function s to have an
3458+# adj_name attribute. Patch __getattr__ to provide it.
3459+def adj_name__getattr__(self, name):
3460+ if name == "adj_name":
3461+ return self.name()
3462+ else:
3463+ return object.__getattr__(self, name)
3464+dolfin.Constant.__getattr__ = adj_name__getattr__
3465+dolfin.Function.__getattr__ = adj_name__getattr__
3466+del(adj_name__getattr__)
3467+
3468+# constant.get_constant expects to be dealing with dolfin-adjoint Constant s.
3469+# Patch it so that it can handle any Constant. Modified version of get_constant
3470+# from constant.py.
3471+def get_constant(a):
3472+ if isinstance(a, dolfin.Constant):
3473+ return a
3474+ else:
3475+ return constant_objects[a]
3476+constant.get_constant.func_code = get_constant.func_code
3477+del(get_constant)
3478+
3479+# Resolve namespace clashes
3480+def assemble(*args, **kwargs):
3481+ if isinstance(args[0], QForm):
3482+ if "form_compiler_parameters" in kwargs:
3483+ raise InvalidArgumentException("Cannot supply form_compiler_parameters argument when assembling a QForm")
3484+ return dolfin_adjoint.assemble(form_compiler_parameters = args[0].form_compiler_parameters(), *args, **kwargs)
3485+ elif isinstance(args[0], tuple(_assemble_classes)):
3486+ return args[0].assemble(*args[1:], **kwargs)
3487+ else:
3488+ return dolfin_adjoint.assemble(*args, **kwargs)
3489+
3490+def Constant(value, cell = None, name = "u"):
3491+ if isinstance(value, tuple):
3492+ return dolfin.as_vector([dolfin_adjoint.Constant(val, cell = cell, name = "%s_%i" % (name, i)) for i, val in enumerate(value)])
3493+ else:
3494+ return dolfin_adjoint.Constant(value, cell = cell, name = name)
3495+
3496+class TimeFunction(timestepping.TimeFunction):
3497+ def __init__(self, tlevels, space, name = "u"):
3498+ timestepping.TimeFunction.__init__(self, tlevels, space, name = name)
3499+ # Ensure that all time level Function s are defined at all times
3500+ cycle_map = self.final_cycle_map()
3501+ for level in cycle_map:
3502+ self.__lfns[level].wrap(self.__fns[cycle_map[level]])
3503+ return
3504+
3505+ def cycle(self, extended = True):
3506+ """
3507+ Perform a timestep cycle. The optional extended argument has no effect.
3508+ """
3509+
3510+ cycle_map = self.cycle_map()
3511+ for level in cycle_map:
3512+ # Annotate the timestep variable cycle
3513+ record = da_annotate_assign(self[cycle_map[level]], self[level])
3514+ assert(not record)
3515+ self[level].vector()[:] = self[cycle_map[level]].vector()
3516+
3517+ return
3518+
3519+ def final_cycle(self):
3520+ """
3521+ Perform the final cycle.
3522+ """
3523+
3524+ return
3525+
3526+__ForwardModel_timestep_cycle_orig = timestepping.ForwardModel.timestep_cycle
3527+def ForwardModel_timestep_cycle(self, extended = True):
3528+ """
3529+ Perform the timestep cycle. The optional extended argument has no effect.
3530+ """
3531+
3532+ __ForwardModel_timestep_cycle_orig(self, extended = False)
3533+ # Signal the end of the timestep
3534+ adj_inc_timestep()
3535+
3536+ return
3537+timestepping.ForwardModel.timestep_cycle = ForwardModel_timestep_cycle
3538+del(ForwardModel_timestep_cycle)
3539+
3540+__ForwardModel_finalise_orig = timestepping.ForwardModel.finalise
3541+def ForwardModel_finalise(self):
3542+ """
3543+ Solve final equations and perform the final variable cycle.
3544+ """
3545+
3546+ __ForwardModel_finalise_orig(self)
3547+ # Signal the end of the forward model
3548+ adj_inc_timestep()
3549+
3550+ return
3551+timestepping.ForwardModel.finalise = ForwardModel_finalise
3552+del(ForwardModel_finalise)
3553+
3554+__AssignmentSolver__init__orig = AssignmentSolver.__init__
3555+def AssignmentSolver__init__(self, *args, **kwargs):
3556+ __AssignmentSolver__init__orig(self, *args, **kwargs)
3557+ solve_orig = self.solve.im_class.solve
3558+ # Equation annotation based on solve in solving.py
3559+ def solve(self, *args, **kwargs):
3560+ # Annotate an assignment solve
3561+ record = da_annotate_equation_solve(self)
3562+ annotate = not dolfin.parameters["adjoint"]["stop_annotating"]
3563+ dolfin.parameters["adjoint"]["stop_annotating"] = True
3564+ ret = solve_orig(self, *args, **kwargs)
3565+ dolfin.parameters["adjoint"]["stop_annotating"] = not annotate
3566+ if record:
3567+ x = self.x()
3568+ if isinstance(x, WrappedFunction):
3569+ x = x.fn()
3570+ adjglobals.adjointer.record_variable(adjglobals.adj_variables[x], libadjoint.MemoryStorage(adjlinalg.Vector(x)))
3571+ return ret
3572+ solve.__doc__ = solve_orig.__doc__
3573+ self.solve = types.MethodType(solve, self)
3574+ return
3575+AssignmentSolver.__init__ = AssignmentSolver__init__
3576+del(AssignmentSolver__init__)
3577+
3578+__EquationSolver__init__orig = EquationSolver.__init__
3579+def EquationSolver__init__(self, *args, **kwargs):
3580+ __EquationSolver__init__orig(self, *args, **kwargs)
3581+ solve_orig = self.solve.im_class.solve
3582+ # Equation annotation based on solve in solving.py
3583+ def solve(self, *args, **kwargs):
3584+ # Annotate an equation solve
3585+ record = da_annotate_equation_solve(self)
3586+ # The EquationSolver solve method could do a lot of work, including
3587+ # further solves. Temporarily disable annotation during the solve ...
3588+ annotate = not dolfin.parameters["adjoint"]["stop_annotating"]
3589+ dolfin.parameters["adjoint"]["stop_annotating"] = True
3590+ ret = solve_orig(self, *args, **kwargs)
3591+ # ... and then restore the previous annotation status.
3592+ dolfin.parameters["adjoint"]["stop_annotating"] = not annotate
3593+ if record:
3594+ x = self.x()
3595+ # We still want to wrap functions with WrappedFunction so that we can for
3596+ # example write separate equations for u[0] and u[n]. However
3597+ # dolfin-adjoint needs to treat these as the same Function, so
3598+ # strategically unwrap WrappedFunction s.
3599+ if isinstance(x, WrappedFunction):
3600+ x = x.fn()
3601+ adjglobals.adjointer.record_variable(adjglobals.adj_variables[x], libadjoint.MemoryStorage(adjlinalg.Vector(x)))
3602+ return ret
3603+ solve.__doc__ = solve_orig.__doc__
3604+ self.solve = types.MethodType(solve, self)
3605+ return
3606+EquationSolver.__init__ = EquationSolver__init__
3607+del(EquationSolver__init__)
3608+
3609+def unwrap_fns(form):
3610+ """
3611+ Return a form with all WrappedFunction s unwrapped.
3612+ """
3613+
3614+ repl = {}
3615+ for dep in ufl.algorithms.extract_coefficients(form):
3616+ if isinstance(dep, WrappedFunction):
3617+ repl[dep] = dep.fn()
3618+
3619+ return replace(form, repl)
3620+
3621+# Based on the Functional class in functional.py
3622+class Functional(functional.Functional):
3623+ def __init__(self, timeform, verbose = False, name = None):
3624+ # Unwrap WrappedFunction s in the Functional
3625+ if isinstance(timeform, ufl.form.Form):
3626+ timeform = unwrap_fns(timeform)
3627+ else:
3628+ for term in timeform.terms:
3629+ term.form = unwrap_fns(term.form)
3630+ functional.Functional.__init__(self, timeform, verbose = verbose, name = name)
3631+
3632+ return
3633+
3634+# Based on the ReducedFunctional class in reduced_functional.py
3635+class ReducedFunctional(reduced_functional.ReducedFunctional):
3636+ def eval_array(self, m_array):
3637+ # Clear caches
3638+ clear_caches()
3639+ return reduced_functional.ReducedFunctional.eval_array(self, m_array)
3640+
3641+# Based on dolfin_adjoint_assign in function.py.
3642+def da_annotate_assign(y, x):
3643+ """
3644+ Annotate an assignment. Returns whether the variable being solved for should
3645+ be recorded by dolfin-adjoint.
3646+ """
3647+
3648+ if dolfin.parameters["adjoint"]["stop_annotating"]:
3649+ # Annotation disabled
3650+ return False
3651+
3652+ if isinstance(x, WrappedFunction):
3653+ x = x.fn()
3654+ if isinstance(y, WrappedFunction):
3655+ y = y.fn()
3656+ if not x == y:
3657+ # ?? What does this do ??
3658+ if not adjglobals.adjointer.variable_known(adjglobals.adj_variables[x]):
3659+ adjglobals.adj_variables.forget(x)
3660+ assign.register_assign(x, y)
3661+
3662+ return False
3663+
3664+# A simple cache for use by dolfin-adjoint callbacks.
3665+da_matrix_cache = {}
3666+def clear_caches(*args):
3667+ """
3668+ Clear caches. Constant s or Function s can be supplied, indicating that only
3669+ cached data associated with those coefficients should be cleared.
3670+ """
3671+
3672+ timestepping.clear_caches(*args)
3673+ da_matrix_cache.clear()
3674+
3675+ return
3676+
3677+# Based on annotate in solving.py
3678+def da_annotate_equation_solve(solve):
3679+ """
3680+ Annotate an equation solve. Returns whether the variable being solved for
3681+ should be recorded by dolfin-adjoint.
3682+ """
3683+
3684+ if dolfin.parameters["adjoint"]["stop_annotating"]:
3685+ # Annotation disabled
3686+ return False
3687+
3688+ # The Function being solved for
3689+ x = solve.x()
3690+ if isinstance(x, WrappedFunction):
3691+ x_fn = x.fn()
3692+ else:
3693+ x_fn = x
3694+
3695+ if isinstance(solve, AssignmentSolver):
3696+ # Assignment solve case
3697+
3698+ rhs = solve.rhs()
3699+ if isinstance(rhs, (list, tuple)):
3700+ if len(rhs) == 1 and rhs[0][0] == 1.0:
3701+ # This is a direct assignment, so register an assignment
3702+ return da_annotate_assign(rhs[0][1], x_fn)
3703+ nrhs = rhs[0][0] * rhs[0][1]
3704+ for term in rhs[1:]:
3705+ nrhs += term[0] * term[1]
3706+ rhs = nrhs; del(nrhs)
3707+ if isinstance(rhs, (float, int, ufl.constantvalue.FloatValue, ufl.constantvalue.IntValue, ufl.constantvalue.Zero)):
3708+ # This is a direct assignment, so register an assignment
3709+ return da_annotate_assign(Constant(rhs), x_fn)
3710+ elif isinstance(rhs, (dolfin.Constant, dolfin.Function)):
3711+ # This is a direct assignment, so register an assignment
3712+ return da_annotate_assign(rhs, x_fn)
3713+ # This is a LinearCombination assignment or expression assignment. For now
3714+ # register this as a Galerkin projection.
3715+ eq = dolfin.inner(dolfin.TestFunction(x.function_space()), dolfin.TrialFunction(x.function_space())) * dolfin.dx == \
3716+ dolfin.inner(dolfin.TestFunction(x.function_space()), rhs) * dolfin.dx
3717+ bcs = []
3718+ solver_parameters = {"linear_solver":"lu"}
3719+ adjoint_solver_parameters = solver_parameters
3720+ else:
3721+ # Equation solve case
3722+ assert(isinstance(solve, EquationSolver))
3723+
3724+ eq = solve.eq()
3725+ bcs = solve.bcs()
3726+ solver_parameters = solve.solver_parameters()
3727+ adjoint_solver_parameters = solve.adjoint_solver_parameters()
3728+
3729+ # Unwrap WrappedFunction s in the equation
3730+ eq.lhs = unwrap_fns(eq.lhs)
3731+ if not is_zero_rhs(eq.rhs):
3732+ eq.rhs = unwrap_fns(eq.rhs)
3733+
3734+ if hasattr(x, "_time_level_data"):
3735+ # This is a time level solve. Set up a Matrix class with some caching
3736+ # enabled.
3737+
3738+ class DAMatrix(adjlinalg.Matrix):
3739+ def __init__(self, *args, **kwargs):
3740+ adjlinalg.Matrix.__init__(self, *args, **kwargs)
3741+ self.__x = x
3742+ self.__x_fn = x_fn
3743+ self.__eq = eq
3744+ self.__bcs = bcs
3745+ self.__solver_parameters = solver_parameters
3746+ self.__adjoint_solver_parameters = adjoint_solver_parameters
3747+ self.parameters = dolfin.Parameters(**dolfin.parameters["timestepping"]["pre_assembly"])
3748+
3749+ return
3750+
3751+ # Based on Matrix.solve in adjlinalg.py
3752+ def solve(self, var, b):
3753+ if isinstance(self.data, adjlinalg.IdentityMatrix) or b.data is None:
3754+ return adjlinalg.Matrix.solve(self, var, b)
3755+
3756+ # Configure the cache
3757+ if not self.__x in da_matrix_cache:
3758+ da_matrix_cache[self.__x] = {}
3759+ cache = da_matrix_cache[self.__x]
3760+
3761+ # Boundary conditions
3762+ if var.type in ["ADJ_ADJOINT", "ADJ_SOA", "ADJ_TLM"]:
3763+ bcs = [homogenize(bc) for bc in self.__bcs]
3764+ else:
3765+ bcs = self.__bcs
3766+ static_bcs = n_non_static_bcs(bcs) == 0
3767+
3768+ if ("pa_a", var.type) in cache:
3769+ # We have cached data for this matrix
3770+ if cache[("pa_a", var.type)] is None:
3771+ # The cache is empty
3772+ static_a = False
3773+ a = assemble(self.data)
3774+ apply_a_bcs = True
3775+ else:
3776+ # The cache contains a matrix, let's use it
3777+ static_a = True
3778+ a, apply_a_bcs = cache[("pa_a", var.type)]
3779+ else:
3780+ if extract_form_data(self.__eq.lhs).rank == 2:
3781+ assert(not self.__x_fn in ufl.algorithms.extract_coefficients(self.__eq.lhs))
3782+ if not is_zero_rhs(self.__eq.rhs):
3783+ assert(not self.__x_fn in ufl.algorithms.extract_coefficients(self.__eq.rhs))
3784+ # The forward equation is a linear variational problem. Is the
3785+ # forward LHS matrix static?
3786+ static_a = is_static_form(self.__eq.lhs)
3787+ else:
3788+ # Is the matrix static?
3789+ static_a = is_static_form(self.data)
3790+ if static_a:
3791+ # The matrix is static, so we can cache it
3792+ if not self.parameters["equations"]["symmetric_boundary_conditions"] and static_bcs:
3793+ # Cache with boundary conditions
3794+ a = assembly_cache.assemble(self.data, bcs = bcs)
3795+ apply_a_bcs = False
3796+ else:
3797+ # Cache without boundary conditions
3798+ a = assembly_cache.assemble(self.data)
3799+ apply_a_bcs = True
3800+ # Cache
3801+ cache[("pa_a", var.type)] = a, apply_a_bcs
3802+ else:
3803+ # The matrix is not static, so we cannot cache it. Add an empty
3804+ # entry to the cache to prevent repeated processing.
3805+ cache[("pa_a", var.type)] = None
3806+ a = assemble(self.data)
3807+ apply_a_bcs = True
3808+
3809+ # Assemble the RHS
3810+ if isinstance(b.data, ufl.form.Form):
3811+ b = assemble(b.data)
3812+ else:
3813+ b = b.data.vector()
3814+
3815+ # Apply boundary conditions
3816+ if apply_a_bcs:
3817+ apply_bcs(a, bcs, L = b, symmetric_bcs = self.parameters["equations"]["symmetric_boundary_conditions"])
3818+ else:
3819+ enforce_bcs(b, bcs)
3820+
3821+ if ("solver", var.type) in cache:
3822+ # Extract a linear solver from the cache
3823+ solver = cache[("solver", var.type)]
3824+ else:
3825+ # Create a new linear solver and cache it
3826+ if var.type in ["ADJ_ADJOINT", "ADJ_SOA"]:
3827+ solver_parameters = self.__adjoint_solver_parameters
3828+ else:
3829+ solver_parameters = self.__solver_parameters
3830+ solver = cache[("solver", var.type)] = solver_cache.solver(self.data, solver_parameters, static = static_a and static_bcs, bcs = bcs, symmetric_bcs = self.parameters["equations"]["symmetric_boundary_conditions"])
3831+
3832+ # RHS solution vector
3833+ x = adjlinalg.Vector(dolfin.Function(self.__x.function_space()))
3834+
3835+ # Solve and return
3836+ solver.set_operator(a)
3837+ solver.solve(x.data.vector(), b)
3838+ return x
3839+ else:
3840+ # This is not a time level solve. Use the default Matrix type.
3841+ DAMatrix = adjlinalg.Matrix
3842+
3843+ # Annotate the equation
3844+ solving.annotate(eq, x_fn, bcs, solver_parameters = solver_parameters, matrix_class = DAMatrix)
3845+ return True
3846
3847=== added directory 'timestepping/python/timestepping'
3848=== added file 'timestepping/python/timestepping/__init__.py'
3849--- timestepping/python/timestepping/__init__.py 1970-01-01 00:00:00 +0000
3850+++ timestepping/python/timestepping/__init__.py 2013-05-15 14:57:23 +0000
3851@@ -0,0 +1,112 @@
3852+#!/usr/bin/env python
3853+
3854+# Copyright (C) 2011-2012 by Imperial College London
3855+# Copyright (C) 2013 University of Oxford
3856+#
3857+# This program is free software: you can redistribute it and/or modify
3858+# it under the terms of the GNU Lesser General Public License as published by
3859+# the Free Software Foundation, version 3 of the License
3860+#
3861+# This program is distributed in the hope that it will be useful,
3862+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3863+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3864+# GNU Lesser General Public License for more details.
3865+#
3866+# You should have received a copy of the GNU Lesser General Public License
3867+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3868+
3869+from fenics_patches import *
3870+from parameters import *
3871+
3872+import caches
3873+import checkpointing
3874+import equation_solvers
3875+import embedded_cpp
3876+import exceptions
3877+import fenics_overrides
3878+import fenics_utils
3879+import quadrature
3880+import pre_assembled_adjoint
3881+import pre_assembled_equations
3882+import pre_assembled_forms
3883+import statics
3884+import time_functions
3885+import time_levels
3886+import time_systems
3887+import versions
3888+import vtu_io
3889+
3890+from caches import *
3891+from checkpointing import *
3892+from equation_solvers import *
3893+from embedded_cpp import *
3894+from exceptions import *
3895+from fenics_overrides import *
3896+from fenics_utils import *
3897+from pre_assembled_adjoint import *
3898+from pre_assembled_equations import *
3899+from pre_assembled_forms import *
3900+from quadrature import *
3901+from statics import *
3902+from time_functions import *
3903+from time_levels import *
3904+from time_systems import *
3905+from versions import *
3906+from vtu_io import *
3907+
3908+__doc__ = \
3909+"""
3910+A timestepping abstraction and automated adjoining library. This library
3911+utilises the FEniCS system for symbolic manipulation and automated code
3912+generation, and supplements this system with a syntax for the description of
3913+timestepping finite element models.
3914+"""
3915+
3916+__license__ = "LGPL-3"
3917+
3918+__version__ = "1.2.0"
3919+
3920+__all__ = \
3921+ caches.__all__ + \
3922+ checkpointing.__all__ + \
3923+ equation_solvers.__all__ + \
3924+ embedded_cpp.__all__ + \
3925+ exceptions.__all__ + \
3926+ fenics_overrides.__all__ + \
3927+ fenics_patches.__all__ + \
3928+ fenics_utils.__all__ + \
3929+ parameters.__all__ + \
3930+ pre_assembled_adjoint.__all__ + \
3931+ pre_assembled_equations.__all__ + \
3932+ pre_assembled_forms.__all__ + \
3933+ quadrature.__all__ + \
3934+ statics.__all__ + \
3935+ time_functions.__all__ + \
3936+ time_levels.__all__ + \
3937+ time_systems.__all__ + \
3938+ versions.__all__ + \
3939+ vtu_io.__all__ + \
3940+ [
3941+ "__doc__",
3942+ "__license__",
3943+ "__name__",
3944+ "__version__",
3945+ "caches",
3946+ "checkpointing",
3947+ "equation_solvers",
3948+ "embedded_cpp",
3949+ "exceptions",
3950+ "fenics_overrides",
3951+ "fenics_utils",
3952+ "pre_assembled_adjoint",
3953+ "pre_assembled_equations",
3954+ "pre_assembled_forms",
3955+ "quadrature",
3956+ "statics",
3957+ "time_functions",
3958+ "time_levels",
3959+ "time_systems",
3960+ "versions",
3961+ "vtu_io"
3962+ ]
3963+
3964\ No newline at end of file
3965
3966=== added file 'timestepping/python/timestepping/caches.py'
3967--- timestepping/python/timestepping/caches.py 1970-01-01 00:00:00 +0000
3968+++ timestepping/python/timestepping/caches.py 2013-05-15 14:57:23 +0000
3969@@ -0,0 +1,266 @@
3970+#!/usr/bin/env python
3971+
3972+# Copyright (C) 2011-2012 by Imperial College London
3973+# Copyright (C) 2013 University of Oxford
3974+#
3975+# This program is free software: you can redistribute it and/or modify
3976+# it under the terms of the GNU Lesser General Public License as published by
3977+# the Free Software Foundation, version 3 of the License
3978+#
3979+# This program is distributed in the hope that it will be useful,
3980+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3981+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3982+# GNU Lesser General Public License for more details.
3983+#
3984+# You should have received a copy of the GNU Lesser General Public License
3985+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3986+
3987+from collections import OrderedDict
3988+import copy
3989+
3990+import dolfin
3991+import ufl
3992+
3993+from exceptions import *
3994+from fenics_overrides import *
3995+from fenics_utils import *
3996+
3997+__all__ = \
3998+ [
3999+ "AssemblyCache",
4000+ "SolverCache",
4001+ "assembly_cache",
4002+ "solver_cache",
4003+ "cache_info",
4004+ "clear_caches"
4005+ ]
4006+
4007+def cache_info(msg, info = dolfin.info):
4008+ """
4009+ Print a message if verbose pre-assembly is enabled.
4010+ """
4011+
4012+ if dolfin.parameters["timestepping"]["pre_assembly"]["verbose"]:
4013+ info(msg)
4014+ return
4015+
4016+class AssemblyCache:
4017+ """
4018+ A cache of assembled Form s. The assemble method can be used to assemble a
4019+ given Form. If an assembled version of the Form exists in the cache, then the
4020+ cached result is returned. Note that this does not check that the Form
4021+ dependencies are unchanged between subsequent assemble calls -- that is
4022+ deemed the responsibility of the caller.
4023+ """
4024+
4025+ def __init__(self):
4026+ self.__cache = {}
4027+
4028+ return
4029+
4030+ def assemble(self, form, bcs = [], symmetric_bcs = False):
4031+ """
4032+ Return the result of assembling the supplied Form, optionally with boundary
4033+ conditions, which are optionally applied so as to yield a symmetric matrix.
4034+ If an assembled version of the Form exists in the cache, return the cached
4035+ result. Note that this does not check that the Form dependencies are
4036+ unchanged between subsequent assemble calls -- that is deemed the
4037+ responsibility of the caller.
4038+ """
4039+
4040+ if not isinstance(form, ufl.form.Form):
4041+ raise InvalidArgumentException("form must be a Form")
4042+ if not isinstance(bcs, list):
4043+ raise InvalidArgumentException("bcs must be a list of DirichletBC s")
4044+ for bc in bcs:
4045+ if not isinstance(bc, dolfin.cpp.DirichletBC):
4046+ raise InvalidArgumentException("bcs must be a list of DirichletBC s")
4047+
4048+ form_data = extract_form_data(form)
4049+ if len(bcs) == 0:
4050+ key = (expand(form), None, None)
4051+ if not key in self.__cache:
4052+ cache_info("Assembling form with rank %i" % form_data.rank, dolfin.info_red)
4053+ self.__cache[key] = assemble(form)
4054+ else:
4055+ cache_info("Using cached assembled form with rank %i" % form_data.rank, dolfin.info_green)
4056+ else:
4057+ if not form_data.rank == 2:
4058+ raise InvalidArgumentException("form must be rank 2 when applying boundary conditions")
4059+
4060+ key = (expand(form), tuple(bcs), symmetric_bcs)
4061+ if not key in self.__cache:
4062+ cache_info("Assembling form with rank 2, with boundary conditions", dolfin.info_red)
4063+ mat = assemble(form)
4064+ apply_bcs(mat, bcs, symmetric_bcs = symmetric_bcs)
4065+ self.__cache[key] = mat
4066+ else:
4067+ cache_info("Using cached assembled form with rank 2, with boundary conditions", dolfin.info_green)
4068+
4069+ return self.__cache[key]
4070+
4071+ def info(self):
4072+ """
4073+ Print some cache status information.
4074+ """
4075+
4076+ counts = [0, 0, 0]
4077+ for key in self.__cache.keys():
4078+ counts[extract_form_data(key[0]).rank] += 1
4079+
4080+ dolfin.info_blue("Assembly cache status:")
4081+ for i in range(3):
4082+ dolfin.info_blue("Pre-assembled rank %i forms: %i" % (i, counts[i]))
4083+
4084+ return
4085+
4086+ def clear(self, *args):
4087+ """
4088+ Clear the cache. If arguments are supplied, clear only the cached assembled
4089+ Form s which depend upon the supplied Constant s or Function s.
4090+ """
4091+
4092+ if len(args) == 0:
4093+ self.__cache = {}
4094+ else:
4095+ for dep in args:
4096+ if not isinstance(dep, (dolfin.Constant, dolfin.Function)):
4097+ raise InvalidArgumentException("Arguments must be Constant s or Function s")
4098+
4099+ for dep in args:
4100+ for key in copy.copy(self.__cache.keys()):
4101+ form = key[0]
4102+ if dep in ufl.algorithms.extract_coefficients(form):
4103+ del(self.__cache[key])
4104+
4105+ return
4106+
4107+class SolverCache:
4108+ """
4109+ A cache of LUSolver s and KrylovSolver s. The solver method can be used to
4110+ return an LUSolver or KrylovSolver suitable for solving an equation with the
4111+ supplied rank 2 Form defining the LHS matrix.
4112+ """
4113+
4114+ def __init__(self):
4115+ self.__cache = OrderedDict()
4116+
4117+ return
4118+
4119+ def __del__(self):
4120+ for key in self.__cache:
4121+ del(self.__cache[key])
4122+
4123+ return
4124+
4125+ def solver(self, form, solver_parameters, static = False, bcs = [], symmetric_bcs = False):
4126+ """
4127+ Return an LUSolver or KrylovSolver suitable for solving an equation with the
4128+ supplied rank 2 Form defining the LHS. If such a solver exists in the cache,
4129+ return the cached solver. Optionally accepts boundary conditions which
4130+ are optionally applied so as to yield a symmetric matrix. If static is true
4131+ then it is assumed that the Form is "static", and solver caching
4132+ options are enabled. The appropriate value of the static argument is
4133+ deemed the responsibility of the caller.
4134+ """
4135+
4136+ if not isinstance(form, ufl.form.Form):
4137+ raise InvalidArgumentException("form must be a rank 2 Form")
4138+ elif not extract_form_data(form).rank == 2:
4139+ raise InvalidArgumentException("form must be a rank 2 Form")
4140+ if not isinstance(solver_parameters, dict):
4141+ raise InvalidArgumentException("solver_parameters must be a dictionary")
4142+ if not isinstance(bcs, list):
4143+ raise InvalidArgumentException("bcs must be a list of DirichletBC s")
4144+ for bc in bcs:
4145+ if not isinstance(bc, dolfin.cpp.DirichletBC):
4146+ raise InvalidArgumentException("bcs must be a list of DirichletBC s")
4147+
4148+ def flatten_parameters(opts):
4149+ assert(isinstance(opts, dict))
4150+ fopts = []
4151+ for key in opts.keys():
4152+ if isinstance(opts[key], dict):
4153+ fopts.append(flatten_parameters(opts[key]))
4154+ else:
4155+ fopts.append((key, opts[key]))
4156+ return tuple(fopts)
4157+
4158+ if static:
4159+ if not "linear_solver" in solver_parameters or solver_parameters["linear_solver"] == "lu":
4160+ solver_parameters = expand_solver_parameters(solver_parameters, default_solver_parameters = {"lu_solver":{"reuse_factorization":True, "same_nonzero_pattern":True}})
4161+ else:
4162+ solver_parameters = expand_solver_parameters(solver_parameters, default_solver_parameters = {"krylov_solver":{"preconditioner":{"reuse":True}}})
4163+ else:
4164+ solver_parameters = expand_solver_parameters(solver_parameters)
4165+
4166+ static_parameters = False
4167+ if solver_parameters["linear_solver"] == "lu":
4168+ static_parameters = solver_parameters["lu_solver"]["reuse_factorization"] or solver_parameters["lu_solver"]["same_nonzero_pattern"]
4169+ else:
4170+ static_parameters = solver_parameters["krylov_solver"]["preconditioner"]["reuse"]
4171+ if static_parameters:
4172+ raise ParameterException("Non-static solve supplied with static solver parameters")
4173+
4174+ if static:
4175+ if len(bcs) == 0:
4176+ key = (expand(form), flatten_parameters(solver_parameters))
4177+ else:
4178+ key = (expand(form), tuple(bcs), symmetric_bcs, flatten_parameters(solver_parameters))
4179+ else:
4180+ args = ufl.algorithms.extract_arguments(form)
4181+ assert(len(args) == 2)
4182+ test, trial = args
4183+ if test.count() > trial.count():
4184+ test, trial = trial, test
4185+ if len(bcs) == 0:
4186+ key = ((test, trial), flatten_parameters(solver_parameters))
4187+ else:
4188+ key = ((test, trial), tuple(bcs), symmetric_bcs, flatten_parameters(solver_parameters))
4189+
4190+ if not key in self.__cache:
4191+ if static:
4192+ cache_info("Creating new static linear solver", dolfin.info_red)
4193+ else:
4194+ cache_info("Creating new non-static linear solver", dolfin.info_red)
4195+ self.__cache[key] = LinearSolver(solver_parameters)
4196+ else:
4197+ cache_info("Using cached linear solver", dolfin.info_green)
4198+ return self.__cache[key]
4199+
4200+ def clear(self, *args):
4201+ """
4202+ Clear the cache. If arguments are supplied, clear only the solvers
4203+ associated with Form s which depend upon the supplied Constant s or
4204+ Function s.
4205+ """
4206+
4207+ if len(args) == 0:
4208+ for key in self.__cache.keys():
4209+ del(self.__cache[key])
4210+ else:
4211+ for dep in args:
4212+ if not isinstance(dep, (dolfin.Constant, dolfin.Function)):
4213+ raise InvalidArgumentException("Arguments must be Constant s or Function s")
4214+
4215+ for key in self.__cache:
4216+ form = key[0]
4217+ if isinstance(form, ufl.form.Form) and dep in ufl.algorithms.extract_coefficients(form):
4218+ del(self.__cache[key])
4219+
4220+ return
4221+
4222+# Default assembly and solver caches.
4223+assembly_cache = AssemblyCache()
4224+solver_cache = SolverCache()
4225+def clear_caches(*args):
4226+ """
4227+ Clear the default assembly and solver caches. If arguments are supplied, clear
4228+ only cached data associated with Form s which depend upon the supplied
4229+ Constant s or Function s.
4230+ """
4231+
4232+ assembly_cache.clear(*args)
4233+ solver_cache.clear(*args)
4234+
4235+ return
4236\ No newline at end of file
4237
4238=== added file 'timestepping/python/timestepping/checkpointing.py'
4239--- timestepping/python/timestepping/checkpointing.py 1970-01-01 00:00:00 +0000
4240+++ timestepping/python/timestepping/checkpointing.py 2013-05-15 14:57:23 +0000
4241@@ -0,0 +1,394 @@
4242+#!/usr/bin/env python
4243+
4244+# Copyright (C) 2011-2012 by Imperial College London
4245+# Copyright (C) 2013 University of Oxford
4246+#
4247+# This program is free software: you can redistribute it and/or modify
4248+# it under the terms of the GNU Lesser General Public License as published by
4249+# the Free Software Foundation, version 3 of the License
4250+#
4251+# This program is distributed in the hope that it will be useful,
4252+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4253+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4254+# GNU Lesser General Public License for more details.
4255+#
4256+# You should have received a copy of the GNU Lesser General Public License
4257+# along with this program. If not, see <http://www.gnu.org/licenses/>.
4258+
4259+import copy
4260+import pickle
4261+import os
4262+
4263+import dolfin
4264+
4265+from exceptions import *
4266+
4267+__all__ = \
4268+ [
4269+ "Checkpointer",
4270+ "DiskCheckpointer",
4271+ "MemoryCheckpointer"
4272+ ]
4273+
4274+class Checkpointer:
4275+ """
4276+ A template for Constant and Function storage.
4277+ """
4278+
4279+ def __init__(self):
4280+ return
4281+
4282+ def __pack(self, c):
4283+ if isinstance(c, dolfin.Constant):
4284+ return float(c)
4285+ else:
4286+ assert(isinstance(c, dolfin.Function))
4287+ return c.vector().array()
4288+
4289+ def __unpack(self, c, c_c):
4290+ if isinstance(c, dolfin.Constant):
4291+ c.assign(c_c)
4292+ else:
4293+ assert(isinstance(c, dolfin.Function))
4294+ c.vector().set_local(c_c)
4295+ c.vector().apply("insert")
4296+
4297+ return
4298+
4299+ def __verify(self, c, c_c, tolerance = 0.0):
4300+ if isinstance(c, dolfin.Constant):
4301+ err = abs(float(c) - c_c)
4302+ if err > tolerance:
4303+ raise CheckpointException("Invalid checkpoint data for Constant with value %.6g, error %.6g" % (float(c), err))
4304+ else:
4305+ assert(isinstance(c, dolfin.Function))
4306+ err = abs(c.vector().array() - c_c).max()
4307+ if err > tolerance:
4308+ raise CheckpointException("Invalid checkpoint data for Function %s, error %.6g" % (c.name(), err))
4309+
4310+ return
4311+
4312+ def __check_cs(self, cs):
4313+ if not isinstance(cs, (list, set)):
4314+ raise InvalidArgumentException("cs must be a list of Constant s or Function s")
4315+ for c in cs:
4316+ if not isinstance(c, (dolfin.Constant, dolfin.Function)):
4317+ raise InvalidArgumentException("cs must be a list of Constant s or Function s")
4318+
4319+ if not isinstance(cs, set):
4320+ cs = set(cs)
4321+ cs = list(cs)
4322+ def cmp(x, y):
4323+ return x.id() - y.id()
4324+ cs.sort(cmp = cmp)
4325+
4326+ return cs
4327+
4328+ def checkpoint(self, key, cs):
4329+ """
4330+ Store, with the supplied key, the supplied Constant s and Function s.
4331+ """
4332+
4333+ raise AbstractMethodException("checkpoint method not overridden")
4334+
4335+ def restore(self, key, cs = None):
4336+ """
4337+ Restore Constant s and Function s with the given key. If cs is supplied,
4338+ only restore Constant s and Function s found in cs.
4339+ """
4340+
4341+ raise AbstractMethodException("restore method not overridden")
4342+
4343+ def has_key(key):
4344+ """
4345+ Return whether any data is associated with the given key.
4346+ """
4347+
4348+ raise AbstractMethodException("has_key method not overridden")
4349+
4350+ def verify(self, key, tolerance = 0.0):
4351+ """
4352+ Verify data associated with the given key, with the specified tolerance.
4353+ """
4354+
4355+ raise AbstractMethodException("verify method not overridden")
4356+
4357+ def remove(self, key):
4358+ """
4359+ Remove data associated with the given key.
4360+ """
4361+
4362+ raise AbstractMethodException("remove method not overridden")
4363+
4364+ def clear(self, keep = []):
4365+ """
4366+ Clear all stored data, except for those with keys in keep.
4367+ """
4368+
4369+ raise AbstractMethodException("clear method not overridden")
4370+
4371+class MemoryCheckpointer(Checkpointer):
4372+ """
4373+ Constant and Function storage in memory.
4374+ """
4375+
4376+ def __init__(self):
4377+ Checkpointer.__init__(self)
4378+
4379+ self.__cache = {}
4380+
4381+ return
4382+
4383+ def checkpoint(self, key, cs):
4384+ """
4385+ Store, with the supplied key, the supplied Constant s and Function s.
4386+ """
4387+
4388+ if key in self.__cache:
4389+ raise CheckpointException("Attempting to overwrite checkpoint with key %s" % str(key))
4390+ cs = self._Checkpointer__check_cs(cs)
4391+
4392+ c_cs = {}
4393+ for c in cs:
4394+ c_cs[c] = self._Checkpointer__pack(c)
4395+
4396+ self.__cache[key] = c_cs
4397+
4398+ return
4399+
4400+ def restore(self, key, cs = None):
4401+ """
4402+ Restore Constant s and Function s with the given key. If cs is supplied,
4403+ only restore Constant s and Function s found in cs.
4404+ """
4405+
4406+ if not key in self.__cache:
4407+ raise CheckpointException("Missing checkpoint with key %s" % str(key))
4408+ if not cs is None:
4409+ cs = self._Checkpointer__check_cs(cs)
4410+
4411+ c_cs = self.__cache[key]
4412+ if cs is None:
4413+ cs = c_cs.keys()
4414+
4415+ for c in cs:
4416+ self._Checkpointer__unpack(c, c_cs[c])
4417+
4418+ return
4419+
4420+ def has_key(self, key):
4421+ """
4422+ Return whether any data is associated with the given key.
4423+ """
4424+
4425+ return key in self.__cache
4426+
4427+ def verify(self, key, tolerance = 0.0):
4428+ """
4429+ Verify data associated with the given key, with the specified tolerance.
4430+ """
4431+
4432+ if not key in self.__cache:
4433+ raise CheckpointException("Missing checkpoint with key %s" % str(key))
4434+ if not isinstance(tolerance, float) or tolerance < 0.0:
4435+ raise InvalidArgumentException("tolerance must be a non-negative float")
4436+ c_cs = self.__cache[key]
4437+
4438+ try:
4439+ for c in c_cs:
4440+ self._Checkpointer__verify(c, c_cs[c], tolerance = tolerance)
4441+ dolfin.info_green("Verified checkpoint with key %s" % str(key))
4442+ except CheckpointException as e:
4443+ dolfin.info_red(str(e))
4444+ raise CheckpointException("Failed to verify checkpoint with key %s" % str(key))
4445+
4446+ return
4447+
4448+ def remove(self, key):
4449+ """
4450+ Remove data associated with the given key.
4451+ """
4452+
4453+ if not key in self.__cache:
4454+ raise CheckpointException("Missing checkpoint with key %s" % str(key))
4455+
4456+ del(self.__cache[key])
4457+
4458+ return
4459+
4460+ def clear(self, keep = []):
4461+ """
4462+ Clear all stored data, except for those with keys in keep.
4463+ """
4464+
4465+ if not isinstance(keep, list):
4466+ raise InvalidArgumentException("keep must be a list")
4467+
4468+ if len(keep) == 0:
4469+ self.__cache = {}
4470+ else:
4471+ for key in copy.copy(self.__cache.keys()):
4472+ if not key in keep:
4473+ del(self.__cache[key])
4474+
4475+ return
4476+
4477+class DiskCheckpointer(Checkpointer):
4478+ """
4479+ Constant and Function storage on disk. All keys handled by a DiskCheckpointer
4480+ are internally cast to strings.
4481+
4482+ Constructor arguments:
4483+ dirname: The directory in which data is to be stored.
4484+ """
4485+
4486+ def __init__(self, dirname = "checkpoints~"):
4487+ if not isinstance(dirname, str):
4488+ raise InvalidArgumentException("dirname must be a string")
4489+
4490+ Checkpointer.__init__(self)
4491+
4492+ if dolfin.MPI.process_number() == 0:
4493+ if not os.path.exists(dirname):
4494+ os.mkdir(dirname)
4495+ dolfin.MPI.barrier()
4496+
4497+ self.__dirname = dirname
4498+ self.__filenames = {}
4499+ self.__id_map = {}
4500+
4501+ return
4502+
4503+ def __filename(self, key):
4504+ return os.path.join(self.__dirname, "checkpoint_%s_%i" % (str(key), dolfin.MPI.process_number()))
4505+
4506+ def checkpoint(self, key, cs):
4507+ """
4508+ Store, with the supplied key, the supplied Constant s and Function s. The
4509+ key is internally cast to a string.
4510+ """
4511+
4512+ key = str(key)
4513+ if key in self.__filenames:
4514+ raise CheckpointException("Attempting to overwrite checkpoint with key %s" % key)
4515+ cs = self._Checkpointer__check_cs(cs)
4516+
4517+ c_cs = {}
4518+ id_map = {}
4519+ for c in cs:
4520+ c_id = c.id()
4521+ c_cs[c_id] = self._Checkpointer__pack(c)
4522+ id_map[c_id] = c
4523+
4524+ filename = self.__filename(key)
4525+ handle = open(filename, "wb")
4526+ pickler = pickle.Pickler(handle, -1)
4527+ pickler.dump(c_cs)
4528+
4529+ self.__filenames[key] = filename
4530+ self.__id_map[key] = id_map
4531+
4532+ return
4533+
4534+ def restore(self, key, cs = None):
4535+ """
4536+ Restore Constant s and Function s with the given key. If cs is supplied,
4537+ only restore Constant s and Function s found in cs. The key is internally
4538+ cast to a string.
4539+ """
4540+
4541+ key = str(key)
4542+ if not key in self.__filenames:
4543+ raise CheckpointException("Missing checkpoint with key %s" % key)
4544+ if not cs is None:
4545+ cs = self._Checkpointer__check_cs(cs)
4546+ cs = [c.id() for c in cs]
4547+
4548+ handle = open(self.__filename(key), "rb")
4549+ pickler = pickle.Unpickler(handle)
4550+ c_cs = pickler.load()
4551+ if cs is None:
4552+ cs = c_cs.keys()
4553+
4554+ id_map = self.__id_map[key]
4555+ for c_id in cs:
4556+ c = id_map[c_id]
4557+ self._Checkpointer__unpack(c, c_cs[c_id])
4558+
4559+ return
4560+
4561+ def has_key(self, key):
4562+ """
4563+ Return whether any data is associated with the given key. The key is
4564+ internally cast to a string.
4565+ """
4566+
4567+ key = str(key)
4568+ return key in self.__filenames
4569+
4570+ def verify(self, key, tolerance = 0.0):
4571+ """
4572+ Verify data associated with the given key, with the specified tolerance. The
4573+ key is internally cast to a string.
4574+ """
4575+
4576+ key = str(key)
4577+ if not key in self.__filenames:
4578+ raise CheckpointException("Missing checkpoint with key %s" % key)
4579+ if not isinstance(tolerance, float) or tolerance < 0.0:
4580+ raise InvalidArgumentException("tolerance must be a non-negative float")
4581+ handle = open(self.__filename(key), "rb")
4582+ pickler = pickle.Unpickler(handle)
4583+ c_cs = pickler.load()
4584+
4585+ try:
4586+ id_map = self.__id_map[key]
4587+ for c_id in c_cs:
4588+ c = id_map[c_id]
4589+ self._Checkpointer__verify(c, c_cs[c_id], tolerance = tolerance)
4590+ dolfin.info_green("Verified checkpoint with key %s" % key)
4591+ except CheckpointException as e:
4592+ dolfin.info_red(str(e))
4593+ raise CheckpointException("Failed to verify checkpoint with key %s" % key)
4594+
4595+ return
4596+
4597+ def remove(self, key):
4598+ """
4599+ Remove data associated with the given key. The key is internally cast to a
4600+ string.
4601+ """
4602+
4603+ key = str(key)
4604+ if not key in self.__filenames:
4605+ raise CheckpointException("Missing checkpoint with key %s" % key)
4606+
4607+# os.remove(self.__filenames[key])
4608+ del(self.__filenames[key])
4609+ del(self.__id_map[key])
4610+
4611+ return
4612+
4613+ def clear(self, keep = []):
4614+ """
4615+ Clear all stored data, except for those with keys in keep. The keys are
4616+ internally cast to strings.
4617+ """
4618+
4619+ if not isinstance(keep, list):
4620+ raise InvalidArgumentException("keep must be a list")
4621+
4622+ if len(keep) == 0:
4623+# for key in self.__filenames:
4624+# os.remove(self.__filenames[key])
4625+ self.__filenames = {}
4626+ self.__id_map = {}
4627+ else:
4628+ keep = [str(key) for key in keep]
4629+ for key in copy.copy(self.__filenames.keys()):
4630+ if not key in keep:
4631+ # os.remove(self.__filenames[key])
4632+ del(self.__filenames[key])
4633+ del(self.__id_map[key])
4634+
4635+ return
4636\ No newline at end of file
4637
4638=== added file 'timestepping/python/timestepping/embedded_cpp.py'
4639--- timestepping/python/timestepping/embedded_cpp.py 1970-01-01 00:00:00 +0000
4640+++ timestepping/python/timestepping/embedded_cpp.py 2013-05-15 14:57:23 +0000
4641@@ -0,0 +1,232 @@
4642+#!/usr/bin/env python
4643+
4644+# Copyright (C) 2011-2012 by Imperial College London
4645+# Copyright (C) 2013 University of Oxford
4646+#
4647+# This program is free software: you can redistribute it and/or modify
4648+# it under the terms of the GNU Lesser General Public License as published by
4649+# the Free Software Foundation, version 3 of the License
4650+#
4651+# This program is distributed in the hope that it will be useful,
4652+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4653+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4654+# GNU Lesser General Public License for more details.
4655+#
4656+# You should have received a copy of the GNU Lesser General Public License
4657+# along with this program. If not, see <http://www.gnu.org/licenses/>.
4658+
4659+import copy
4660+import ctypes
4661+import os
4662+
4663+import dolfin
4664+import instant
4665+import numpy
4666+
4667+from exceptions import *
4668+from fenics_versions import *
4669+
4670+__all__ = \
4671+ [
4672+ "EmbeddedCpp",
4673+ "CellKernel",
4674+ "double_arr",
4675+ "int_arr",
4676+ "long_arr"
4677+ ]
4678+
4679+int_arr, long_arr, double_arr = 2, 3, 4
4680+
4681+class EmbeddedCpp:
4682+ """
4683+ A wrapper for short sections of embedded C++ code.
4684+
4685+ Constructor arguments:
4686+ code: C++ code.
4687+ includes: Code which can, for example be used to include header files.
4688+ include_dirs: Header file directories.
4689+ Remaining keyword arguments form a list of name:type pairs, with:
4690+ name: The name of a variable in the code, which will be passed from
4691+ Python.
4692+ type: One of int, float, int_arr, long_arr double_arr, Function,
4693+ GenericVector, or Mesh, identifying the variable type.
4694+ """
4695+
4696+ if dolfin_version() < (1, 1, 0):
4697+ __default_includes = """#include "dolfin.h"
4698+#define la_index uint"""
4699+ else:
4700+ __default_includes = """#include "dolfin.h" """
4701+
4702+ def __init__(self, code, includes = "", include_dirs = [], **kwargs):
4703+ if not isinstance(code, str):
4704+ raise InvalidArgumentException("code must be a string")
4705+ if not isinstance(includes, str):
4706+ raise InvalidArgumentException("includes must be a string")
4707+ if not isinstance(include_dirs, list):
4708+ raise InvalidArgumentException("include_dirs must be a list of strings")
4709+ for path in include_dirs:
4710+ if not isinstance(path, str):
4711+ raise InvalidArgumentException("include_dirs must be a list of strings")
4712+ for arg in kwargs.keys():
4713+ if not isinstance(arg, str):
4714+ raise InvalidArgumentException("Argument name must be a string")
4715+ for arg in kwargs.values():
4716+ if not arg in [int, float, int_arr, double_arr, long_arr, dolfin.Function, dolfin.GenericVector, dolfin.Mesh]:
4717+ raise InvalidArgumentException("Argument type must be int, float, int_arr, long_arr, double_arr, Function, GenericVector or Mesh")
4718+
4719+ self.__code = code
4720+ self.__includes = """%s
4721+
4722+%s""" % (self.__default_includes, includes)
4723+ self.__include_dirs = copy.copy(include_dirs)
4724+ self.__args = copy.copy(kwargs)
4725+ self.__lib = None
4726+
4727+ return
4728+
4729+ def compile(self):
4730+ """
4731+ Compile the code.
4732+ """
4733+
4734+ args = ""
4735+ cast_code = ""
4736+ for name in sorted(self.__args.keys()):
4737+ arg = self.__args[name]
4738+ if len(args) > 0:
4739+ args += ", "
4740+ if arg == int:
4741+ args += "int %s" % name
4742+ elif arg == float:
4743+ args += "double %s" % name
4744+ elif arg == int_arr:
4745+ args += "int* %s" % name
4746+ elif arg == long_arr:
4747+ args += "long* %s" % name
4748+ elif arg == double_arr:
4749+ args += "double* %s" % name
4750+ else:
4751+ name_mangle = name
4752+ while name_mangle in self.__args.keys():
4753+ name_mangle = "%s_" % name_mangle
4754+ args += "void* %s" % name_mangle
4755+ if arg == dolfin.Function:
4756+ cast_code += " boost::shared_ptr<Function> %s = (*((boost::shared_ptr<Function>*)%s));\n" % (name, name_mangle)
4757+ elif arg == dolfin.GenericVector:
4758+ cast_code += " boost::shared_ptr<GenericVector> %s = (*((boost::shared_ptr<GenericVector>*)%s));\n" % (name, name_mangle)
4759+ else:
4760+ assert(arg == dolfin.Mesh)
4761+ cast_code += " boost::shared_ptr<Mesh> %s = (*((boost::shared_ptr<Mesh>*)%s));\n" % (name, name_mangle)
4762+
4763+ code = \
4764+"""%s
4765+
4766+using namespace dolfin;
4767+
4768+extern "C" {
4769+ int code(%s){
4770+%s
4771+
4772+%s
4773+ return 0;
4774+ }
4775+}""" % (self.__includes, args, cast_code, self.__code)
4776+
4777+ mod = instant.build_module(code = code, cppargs = dolfin.parameters["form_compiler"]["cpp_optimize_flags"], include_dirs = self.__include_dirs)
4778+ path = os.path.dirname(mod.__file__)
4779+ name = os.path.split(path)[-1]
4780+ self.__lib = ctypes.cdll.LoadLibrary(os.path.join(path, "_%s.so" % name))
4781+ self.__lib.code.restype = int
4782+
4783+ return
4784+
4785+ def run(self, **kwargs):
4786+ """
4787+ Run the code. The keyword arguments form a list of name:variable pairs,
4788+ with:
4789+ name: The name of a variable in the C++ code, which will be passed
4790+ from Python.
4791+ variable: The variable to be passed to the C++ code. The type must be
4792+ consistent with the type passed to the constructor.
4793+ """
4794+
4795+ args = kwargs
4796+ if not len(args) == len(self.__args) or not tuple(sorted(args.keys())) == tuple(sorted(self.__args.keys())):
4797+ raise InvalidArgumentException("Invalid argument names")
4798+ for name in args.keys():
4799+ arg = args[name]
4800+ if isinstance(arg, numpy.ndarray):
4801+ if arg.dtype == "int32":
4802+ if not self.__args[name] == int_arr:
4803+ raise InvalidArgumentException("Argument %s is of invalid type" % name)
4804+ elif arg.dtype == "int64":
4805+ if not self.__args[name] == long_arr:
4806+ raise InvalidArgumentException("Argument %s is of invalid type" % name)
4807+ elif arg.dtype == "float64":
4808+ if not self.__args[name] == double_arr:
4809+ raise InvalidArgumentException("Argument %s is of invalid type" % name)
4810+ else:
4811+ raise InvalidArgumentException("Argument %s is of invalid type" % name)
4812+ elif not isinstance(arg, self.__args[name]):
4813+ raise InvalidArgumentException("Argument %s is of invalid type" % name)
4814+
4815+ if self.__lib is None:
4816+ self.compile()
4817+
4818+ largs = []
4819+ for name in sorted(args.keys()):
4820+ arg = args[name]
4821+ if isinstance(arg, int):
4822+ largs.append(ctypes.c_int(arg))
4823+ elif isinstance(arg, float):
4824+ largs.append(ctypes.c_double(arg))
4825+ elif isinstance(arg, numpy.ndarray):
4826+ largs.append(arg.ctypes.data)
4827+ else:
4828+ assert(isinstance(arg, (dolfin.Function, dolfin.GenericVector, dolfin.Mesh)))
4829+ largs.append(ctypes.c_void_p(int(arg.this)))
4830+
4831+ ret = self.__lib.code(*largs)
4832+ if not ret == 0:
4833+ raise StateException("Non-zero return value: %i" % ret)
4834+
4835+ return
4836+
4837+class CellKernel(EmbeddedCpp):
4838+ """
4839+ A wrapper for short sections of embedded C++ code which iterate over cells.
4840+
4841+ Constructor arguments:
4842+ mesh: A Mesh.
4843+ kernel_code: Code executed for each cell in the mesh.
4844+ initialisation_code: Code executed before iterating over the cells.
4845+ finalisation_code: Code executed after iterating over the cells.
4846+ includes: Code which can, for example, be used to include header
4847+ files.
4848+ include_dirs: Header file directories.
4849+ Remaining keyword arguments are as for the EmbeddedCpp constructor. An
4850+ additional size_t variable cell is defined in the cell iteration, indicating
4851+ the cell number.
4852+ """
4853+
4854+ def __init__(self, mesh, kernel_code, initialisation_code = "", finalisation_code = "", includes = "", include_dirs = [], **kwargs):
4855+ if not isinstance(mesh, dolfin.Mesh):
4856+ raise InvalidArgumentException("mesh must be a Mesh")
4857+ if not isinstance(kernel_code, str):
4858+ raise InvalidArgumentException("kernel_code must be a string")
4859+ if not isinstance(initialisation_code, str):
4860+ raise InvalidArgumentException("initialisation_code must be a string")
4861+ if not isinstance(finalisation_code, str):
4862+ raise InvalidArgumentException("finalisation_code must be a string")
4863+
4864+ code = \
4865+"""%s
4866+ for(size_t cell = 0;cell < %i;cell++){
4867+%s
4868+ }
4869+%s""" % (initialisation_code, mesh.num_cells(), kernel_code, finalisation_code)
4870+
4871+ EmbeddedCpp.__init__(self, code, includes = includes, include_dirs = include_dirs, **kwargs)
4872+
4873+ return
4874\ No newline at end of file
4875
4876=== added file 'timestepping/python/timestepping/equation_solvers.py'
4877--- timestepping/python/timestepping/equation_solvers.py 1970-01-01 00:00:00 +0000
4878+++ timestepping/python/timestepping/equation_solvers.py 2013-05-15 14:57:23 +0000
4879@@ -0,0 +1,556 @@
4880+#!/usr/bin/env python
4881+
4882+# Copyright (C) 2011-2012 by Imperial College London
4883+# Copyright (C) 2013 University of Oxford
4884+#
4885+# This program is free software: you can redistribute it and/or modify
4886+# it under the terms of the GNU Lesser General Public License as published by
4887+# the Free Software Foundation, version 3 of the License
4888+#
4889+# This program is distributed in the hope that it will be useful,
4890+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4891+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4892+# GNU Lesser General Public License for more details.
4893+#
4894+# You should have received a copy of the GNU Lesser General Public License
4895+# along with this program. If not, see <http://www.gnu.org/licenses/>.
4896+
4897+import copy
4898+
4899+import dolfin
4900+import ufl
4901+
4902+from exceptions import *
4903+from fenics_overrides import *
4904+from fenics_utils import *
4905+
4906+__all__ = \
4907+ [
4908+ "AssignmentSolver",
4909+ "EquationSolver",
4910+ "LinearCombination"
4911+ ]
4912+
4913+class LinearCombination:
4914+ """
4915+ A linear combination.
4916+
4917+ Constructor arguments: An arbitrary number of tuples, each of which contains
4918+ two elements:
4919+ (alpha, x)
4920+ where alpha is one of:
4921+ 1. A float.
4922+ or:
4923+ 2. An arbitrary Expr.
4924+ and x is a Function. The supplied alphas are not checked to ensure that this
4925+ really is a true linear combination.
4926+ """
4927+
4928+ def __init__(self, *args):
4929+ for arg in args:
4930+ if not isinstance(arg, tuple) or not len(arg) == 2 \
4931+ or not isinstance(arg[0], (float, ufl.expr.Expr)) or not isinstance(arg[1], dolfin.Function):
4932+ raise InvalidArgumentException("Require tuples of (float or Expr, Function) as arguments")
4933+
4934+ alpha = [arg[0] for arg in args]
4935+ for i, lalpha in enumerate(alpha):
4936+ if isinstance(lalpha, float):
4937+ alpha[i] = ufl.constantvalue.FloatValue(lalpha)
4938+ y = [arg[1] for arg in args]
4939+
4940+ use_exp = False
4941+ for lalpha in alpha:
4942+ if not isinstance(lalpha, (ufl.constantvalue.FloatValue, dolfin.Constant)):
4943+ use_exp = True
4944+ break
4945+ if use_exp:
4946+ fl = ufl.constantvalue.Zero()
4947+ for lalpha, ly in zip(alpha, y):
4948+ fl += lalpha * ly
4949+ else:
4950+ fl = [(lalpha, ly) for lalpha, ly in zip(alpha, y)]
4951+
4952+ self.__alpha = alpha
4953+ self.__y = y
4954+ self.__fl = fl
4955+
4956+ return
4957+
4958+ def dependencies(self, non_symbolic = False):
4959+ """
4960+ Return all dependencies associated with the LinearCombination. The
4961+ optional non_symbolic argument has no effect.
4962+ """
4963+
4964+ deps = copy.copy(self.__y)
4965+ for alpha in self.__alpha:
4966+ deps += ufl.algorithms.extract_coefficients(alpha)
4967+
4968+ return deps
4969+
4970+ def nonlinear_dependencies(self):
4971+ """
4972+ Return all non-linear dependencies associated with the LinearCombination.
4973+ """
4974+
4975+ if isinstance(self.__fl, ufl.expr.Expr):
4976+ nl_deps = []
4977+ for dep in self.dependencies():
4978+ if isinstance(dep, dolfin.Function):
4979+ nl_deps += ufl.algorithms.extract_coefficients(differentiate_expr(self.__fl, dep))
4980+ elif not isinstance(dep, (dolfin.Constant, dolfin.Expression)):
4981+ raise DependencyException("Invalid dependency")
4982+ return nl_deps
4983+ else:
4984+ return []
4985+
4986+ def flatten(self):
4987+ """
4988+ Return a flattened version of the LinearCombination. If all alphas are
4989+ floats or Constant s then return a list of (alpha, x) tuples. Otherwise
4990+ return an Expr.
4991+ """
4992+
4993+ return self.__fl
4994+
4995+ def solve(self, x):
4996+ """
4997+ Assign the given Function x to be equal to the value of the
4998+ LinearCombination.
4999+ """
5000+
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to status/vote changes: