Merge lp:~jamesmadd/dolfin-adjoint/timestepping into lp:dolfin-adjoint
- timestepping
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Patrick Farrell | Pending | ||
Review via email: mp+162989@code.launchpad.net |
Commit message
Description of the change
Timestepping library, with manual, tests, and dolfin-adjoint integration
- 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
Patrick Farrell (pefarrell) wrote : | # |
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
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 ..
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
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?
James Maddison (jamesmadd) wrote : | # |
a, e, f, g, h, i, j are complete. python/
- 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
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
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 | + |
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 src/dolfin- adjoint/ timestepping/ tests/short/ burgers_ leapfrog_ da src/dolfin- adjoint/ timestepping/ tests/short/ linearised_ shallow_ water_cn/ shallow_ water
Failures: 2
Failures:
/data/pfarrell/
/data/pfarrell/
burgers_leapfrog_da fails with
-4.892485136094 69882e+ 01 2.2239987629291 1358e-12 leapfrog_ da", line 114, in <module>
Traceback (most recent call last):
File "./burgers_
assert(err < 7.0e-13)
AssertionError
shallow_water fails with
Assembling form with rank 2 5065e-05 7189e-05 7849e-16 7620e-07 6568e-07 6049e-14
4.1082137068403
4.1082137068209
1.9378758580462
8.5132188012726
8.5132189531134
1.5184078948575
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 .. ?